vaultkit 0.2.0__tar.gz → 0.3.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 (41) hide show
  1. {vaultkit-0.2.0 → vaultkit-0.3.0}/PKG-INFO +32 -1
  2. {vaultkit-0.2.0 → vaultkit-0.3.0}/README.md +31 -0
  3. {vaultkit-0.2.0 → vaultkit-0.3.0}/pyproject.toml +1 -1
  4. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/__init__.py +1 -1
  5. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/client.py +41 -0
  6. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/definitions.py +67 -0
  7. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/executor.py +4 -0
  8. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/utils/validation.py +74 -0
  9. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit.egg-info/PKG-INFO +32 -1
  10. {vaultkit-0.2.0 → vaultkit-0.3.0}/setup.cfg +0 -0
  11. {vaultkit-0.2.0 → vaultkit-0.3.0}/tests/test_fetch.py +0 -0
  12. {vaultkit-0.2.0 → vaultkit-0.3.0}/tests/test_query.py +0 -0
  13. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/core/__init__.py +0 -0
  14. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/core/http.py +0 -0
  15. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/core/polling.py +0 -0
  16. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/errors/__init__.py +0 -0
  17. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/errors/base.py +0 -0
  18. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/errors/exceptions.py +0 -0
  19. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/mcp/__init__.py +0 -0
  20. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/mcp/__main__.py +0 -0
  21. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/mcp/identity.py +0 -0
  22. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/mcp/server.py +0 -0
  23. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/models/__init__.py +0 -0
  24. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/models/dataset_info.py +0 -0
  25. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/models/dataset_schema.py +0 -0
  26. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/models/fetch_result.py +0 -0
  27. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/models/query_result.py +0 -0
  28. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/__init__.py +0 -0
  29. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/adapters/__init__.py +0 -0
  30. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/adapters/anthropic.py +0 -0
  31. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/adapters/mcp.py +0 -0
  32. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/adapters/openai.py +0 -0
  33. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/builder.py +0 -0
  34. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/tools/schemas.py +0 -0
  35. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/utils/__init__.py +0 -0
  36. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit/utils/retry.py +0 -0
  37. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit.egg-info/SOURCES.txt +0 -0
  38. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit.egg-info/dependency_links.txt +0 -0
  39. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit.egg-info/entry_points.txt +0 -0
  40. {vaultkit-0.2.0 → vaultkit-0.3.0}/vaultkit.egg-info/requires.txt +0 -0
  41. {vaultkit-0.2.0 → vaultkit-0.3.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.2.0
3
+ Version: 0.3.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
@@ -74,6 +74,37 @@ print(result.rows)
74
74
 
75
75
  ---
76
76
 
77
+ ## Analytical Queries
78
+
79
+ Aggregate, group, filter on aggregates and sort within a single dataset:
80
+
81
+ ```python
82
+ result = client.execute(
83
+ dataset="orders",
84
+ fields=["region"],
85
+ aggregates=[{"func": "sum", "field": "amount", "alias": "total"}],
86
+ having=[{"field": "SUM(amount)", "operator": "gt", "value": 1000}],
87
+ order_by={"column": "total", "direction": "desc"},
88
+ limit=10,
89
+ purpose="Quarterly revenue by region",
90
+ )
91
+ # [{"region": "US", "total": 18250}, {"region": "CA", "total": 4100}, ...]
92
+ ```
93
+
94
+ - `aggregates`: `func` is one of `count`, `sum`, `avg`, `min`, `max`; use `field="*"` with `count`.
95
+ - `group_by`: optional. Defaults to `fields`; if given, it must include every entry in `fields`.
96
+ - `having`: needs an aggregate expression such as `SUM(amount)`, not an alias.
97
+ - `order_by`: a field, an aggregate alias or an aggregate expression, `asc` (default) or `desc`.
98
+ - `fields` can be omitted for a single total, e.g. `aggregates=[{"func": "count", "field": "*"}]`.
99
+
100
+ Policies apply to every field these clauses use. `min`/`max`/`sum`/`avg` over a masked
101
+ field are denied; `count` is allowed. Joins are not supported.
102
+
103
+ > Requires a VaultKit control plane with intent analytics support. Older control
104
+ > planes ignore these parameters and return ungrouped rows.
105
+
106
+ ---
107
+
77
108
  ## AI Agent Usage
78
109
 
79
110
  VaultKit provides built-in tools for LLM agents.
@@ -48,6 +48,37 @@ print(result.rows)
48
48
 
49
49
  ---
50
50
 
51
+ ## Analytical Queries
52
+
53
+ Aggregate, group, filter on aggregates and sort within a single dataset:
54
+
55
+ ```python
56
+ result = client.execute(
57
+ dataset="orders",
58
+ fields=["region"],
59
+ aggregates=[{"func": "sum", "field": "amount", "alias": "total"}],
60
+ having=[{"field": "SUM(amount)", "operator": "gt", "value": 1000}],
61
+ order_by={"column": "total", "direction": "desc"},
62
+ limit=10,
63
+ purpose="Quarterly revenue by region",
64
+ )
65
+ # [{"region": "US", "total": 18250}, {"region": "CA", "total": 4100}, ...]
66
+ ```
67
+
68
+ - `aggregates`: `func` is one of `count`, `sum`, `avg`, `min`, `max`; use `field="*"` with `count`.
69
+ - `group_by`: optional. Defaults to `fields`; if given, it must include every entry in `fields`.
70
+ - `having`: needs an aggregate expression such as `SUM(amount)`, not an alias.
71
+ - `order_by`: a field, an aggregate alias or an aggregate expression, `asc` (default) or `desc`.
72
+ - `fields` can be omitted for a single total, e.g. `aggregates=[{"func": "count", "field": "*"}]`.
73
+
74
+ Policies apply to every field these clauses use. `min`/`max`/`sum`/`avg` over a masked
75
+ field are denied; `count` is allowed. Joins are not supported.
76
+
77
+ > Requires a VaultKit control plane with intent analytics support. Older control
78
+ > planes ignore these parameters and return ungrouped rows.
79
+
80
+ ---
81
+
51
82
  ## AI Agent Usage
52
83
 
53
84
  VaultKit provides built-in tools for LLM agents.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vaultkit"
7
- version = "0.2.0"
7
+ version = "0.3.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" }
@@ -53,7 +53,7 @@ from .models import (
53
53
  # Optional (nice DX improvement)
54
54
  from .tools import ToolBuilder, ToolExecutor
55
55
 
56
- __version__ = "0.1.0"
56
+ __version__ = "0.3.0"
57
57
 
58
58
  __all__ = [
59
59
  "VaultKitClient",
@@ -19,7 +19,11 @@ from vaultkit.models.query_result import QueryResult
19
19
  from vaultkit.utils.retry import RetryConfig, with_retries
20
20
  from vaultkit.utils.validation import (
21
21
  require_str,
22
+ validate_aggregates,
22
23
  validate_filters,
24
+ validate_group_by,
25
+ validate_having,
26
+ validate_order_by,
23
27
  validate_limit,
24
28
  )
25
29
 
@@ -98,6 +102,10 @@ class VaultKitClient:
98
102
  fields: Optional[List[str]] = None,
99
103
  filters: Optional[List[Dict[str, Any]]] = None,
100
104
  limit: Optional[int] = None,
105
+ aggregates: Optional[List[Dict[str, Any]]] = None,
106
+ group_by: Optional[List[str]] = None,
107
+ having: Optional[List[Dict[str, Any]]] = None,
108
+ order_by: Optional[Dict[str, Any]] = None,
101
109
  purpose: Optional[str] = None,
102
110
  requester_region: Optional[str] = None,
103
111
  requester_clearance: Optional[str] = None,
@@ -106,6 +114,18 @@ class VaultKitClient:
106
114
  """
107
115
  Submit an intent request and return data — full grant lifecycle internal.
108
116
 
117
+ Analytical queries (single dataset, no joins):
118
+ aggregates [{"func": "sum", "field": "amount", "alias": "total"}]
119
+ func is one of count, sum, avg, min, max; "*" only with count.
120
+ group_by ["region"] — every field in `fields` must be listed when given.
121
+ having [{"field": "SUM(amount)", "operator": "gt", "value": 1000}]
122
+ field must be an aggregate expression, not an alias.
123
+ order_by {"column": "total", "direction": "desc"}
124
+
125
+ `fields` may be omitted when `aggregates` is given (e.g. a single COUNT(*)).
126
+ Requires a control plane that supports intent analytics; older ones
127
+ ignore these parameters.
128
+
109
129
  Raises:
110
130
  DeniedError Policy rejected. Do not retry.
111
131
  ApprovalRequiredError Needs human approval (request_id attached).
@@ -126,6 +146,10 @@ class VaultKitClient:
126
146
  fields=fields,
127
147
  filters=filters,
128
148
  limit=limit,
149
+ aggregates=aggregates,
150
+ group_by=group_by,
151
+ having=having,
152
+ order_by=order_by,
129
153
  purpose=purpose,
130
154
  requester_region=requester_region,
131
155
  requester_clearance=requester_clearance,
@@ -214,6 +238,10 @@ class VaultKitClient:
214
238
  fields: Optional[List[str]] = None,
215
239
  filters: Optional[List[Dict[str, Any]]] = None,
216
240
  limit: Optional[int] = None,
241
+ aggregates: Optional[List[Dict[str, Any]]] = None,
242
+ group_by: Optional[List[str]] = None,
243
+ having: Optional[List[Dict[str, Any]]] = None,
244
+ order_by: Optional[Dict[str, Any]] = None,
217
245
  purpose: Optional[str] = None,
218
246
  requester_region: Optional[str] = None,
219
247
  requester_clearance: Optional[str] = None,
@@ -223,6 +251,10 @@ class VaultKitClient:
223
251
  dataset = require_str("dataset", dataset)
224
252
  limit = validate_limit(limit)
225
253
  filters = validate_filters(filters)
254
+ aggregates = validate_aggregates(aggregates)
255
+ group_by = validate_group_by(group_by)
256
+ having = validate_having(having)
257
+ order_by = validate_order_by(order_by)
226
258
 
227
259
  req: Dict[str, Any] = {"dataset": dataset}
228
260
  body: Dict[str, Any] = {"request": req}
@@ -235,6 +267,14 @@ class VaultKitClient:
235
267
  req["purpose"] = str(purpose)
236
268
  if filters is not None:
237
269
  req["filters"] = filters
270
+ if aggregates is not None:
271
+ req["aggregates"] = aggregates
272
+ if group_by is not None:
273
+ req["group_by"] = group_by
274
+ if having is not None:
275
+ req["having"] = having
276
+ if order_by is not None:
277
+ req["order_by"] = order_by
238
278
  if requester_region is not None:
239
279
  body["requester_region"] = requester_region
240
280
  if requester_clearance is not None:
@@ -248,6 +288,7 @@ class VaultKitClient:
248
288
  "poll": poll,
249
289
  "has_fields": fields is not None,
250
290
  "has_filters": filters is not None,
291
+ "has_aggregates": aggregates is not None,
251
292
  "limit": limit,
252
293
  },
253
294
  )
@@ -115,6 +115,8 @@ def query_tool_def(
115
115
  "description": (
116
116
  "Query a governed dataset through VaultKit. Policies are enforced automatically: "
117
117
  "sensitive fields may be masked and some datasets require approval before data is returned. "
118
+ "Supports aggregation (count/sum/avg/min/max), grouping, having and sorting on a single dataset; "
119
+ "joins are not supported. "
118
120
  "Returns data or a pending status if approval is required."
119
121
  ),
120
122
  "input_schema": {
@@ -143,6 +145,71 @@ def query_tool_def(
143
145
  "maximum": 10000,
144
146
  "description": "Maximum number of rows to return.",
145
147
  },
148
+ "aggregates": {
149
+ "type": "array",
150
+ "minItems": 1,
151
+ "items": {
152
+ "type": "object",
153
+ "properties": {
154
+ "func": {"type": "string", "enum": ["count", "sum", "avg", "min", "max"]},
155
+ "field": {
156
+ "type": "string",
157
+ "description": "A field of the selected dataset, or '*' with count.",
158
+ },
159
+ "alias": {
160
+ "type": "string",
161
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
162
+ "description": "Name of the result column. Use it in order_by.",
163
+ },
164
+ },
165
+ "required": ["func", "field"],
166
+ },
167
+ "description": (
168
+ "Aggregate functions over the selected dataset. When aggregating, every entry in "
169
+ "'fields' is a grouping column. 'fields' may be omitted for a single total.\n\n"
170
+ "Example: [{func: 'sum', field: 'amount', alias: 'total'}]\n"
171
+ "min/max/sum/avg over masked fields are denied by policy; count is allowed."
172
+ ),
173
+ },
174
+ "group_by": {
175
+ "type": "array",
176
+ "items": {"type": "string"},
177
+ "description": (
178
+ "Fields to group by. Optional: defaults to 'fields'. If given, it must include "
179
+ "every entry in 'fields'."
180
+ ),
181
+ },
182
+ "having": {
183
+ "type": "array",
184
+ "items": {
185
+ "type": "object",
186
+ "properties": {
187
+ "field": {
188
+ "type": "string",
189
+ "description": "An aggregate expression such as 'SUM(amount)'. Aliases are not allowed here.",
190
+ },
191
+ "operator": {"type": "string", "enum": ["gt", "lt", "eq", "gte", "lte"]},
192
+ "value": {"type": "number"},
193
+ },
194
+ "required": ["field", "operator", "value"],
195
+ },
196
+ "description": (
197
+ "Filters applied after aggregation. Requires 'aggregates'.\n\n"
198
+ "Example: [{field: 'SUM(amount)', operator: 'gt', value: 1000}]"
199
+ ),
200
+ },
201
+ "order_by": {
202
+ "type": "object",
203
+ "properties": {
204
+ "column": {
205
+ "type": "string",
206
+ "description": "A field, an aggregate alias, or an aggregate expression.",
207
+ },
208
+ "direction": {"type": "string", "enum": ["asc", "desc"], "default": "asc"},
209
+ },
210
+ "required": ["column"],
211
+ "description": "Sort order of the results.",
212
+ },
146
213
  "purpose": {
147
214
  "type": "string",
148
215
  "description": (
@@ -105,6 +105,10 @@ class ToolExecutor:
105
105
  fields=args.get("fields"),
106
106
  filters=args.get("filters"),
107
107
  limit=args.get("limit"),
108
+ aggregates=args.get("aggregates"),
109
+ group_by=args.get("group_by"),
110
+ having=args.get("having"),
111
+ order_by=args.get("order_by"),
108
112
  purpose=purpose,
109
113
  requester_region=args.get("requester_region") or self._default_requester_region,
110
114
  )
@@ -85,3 +85,77 @@ def validate_filters(value: Optional[Any]) -> Optional[list]:
85
85
  )
86
86
 
87
87
  return value
88
+
89
+
90
+ # Analytical clauses. Only the shape is checked here; the control plane
91
+ # validates functions, fields and operators and returns a clear error.
92
+
93
+ def _require_list_of_dicts(name: str, value: Optional[Any]) -> Optional[list]:
94
+ if value is None:
95
+ return None
96
+
97
+ if not isinstance(value, list):
98
+ raise ValidationError(
99
+ f"{name} must be a list",
100
+ context=ErrorContext(raw=value),
101
+ )
102
+
103
+ for item in value:
104
+ if not isinstance(item, dict):
105
+ raise ValidationError(
106
+ f"each {name} entry must be an object",
107
+ context=ErrorContext(raw=item),
108
+ )
109
+
110
+ return value
111
+
112
+
113
+ def validate_aggregates(value: Optional[Any]) -> Optional[list]:
114
+ value = _require_list_of_dicts("aggregates", value)
115
+ if value is None:
116
+ return None
117
+
118
+ if not value:
119
+ raise ValidationError(
120
+ "aggregates cannot be empty",
121
+ context=ErrorContext(raw=value),
122
+ )
123
+
124
+ for agg in value:
125
+ if not agg.get("func") or not agg.get("field"):
126
+ raise ValidationError(
127
+ "each aggregate needs 'func' and 'field'",
128
+ context=ErrorContext(raw=agg),
129
+ )
130
+
131
+ return value
132
+
133
+
134
+ def validate_group_by(value: Optional[Any]) -> Optional[list]:
135
+ if value is None:
136
+ return None
137
+
138
+ if not isinstance(value, list) or not all(isinstance(v, str) and v.strip() for v in value):
139
+ raise ValidationError(
140
+ "group_by must be a list of field names",
141
+ context=ErrorContext(raw=value),
142
+ )
143
+
144
+ return value
145
+
146
+
147
+ def validate_having(value: Optional[Any]) -> Optional[list]:
148
+ return _require_list_of_dicts("having", value)
149
+
150
+
151
+ def validate_order_by(value: Optional[Any]) -> Optional[Dict[str, Any]]:
152
+ if value is None:
153
+ return None
154
+
155
+ if not isinstance(value, dict) or not value.get("column"):
156
+ raise ValidationError(
157
+ "order_by must be an object with a 'column'",
158
+ context=ErrorContext(raw=value),
159
+ )
160
+
161
+ return value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vaultkit
3
- Version: 0.2.0
3
+ Version: 0.3.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
@@ -74,6 +74,37 @@ print(result.rows)
74
74
 
75
75
  ---
76
76
 
77
+ ## Analytical Queries
78
+
79
+ Aggregate, group, filter on aggregates and sort within a single dataset:
80
+
81
+ ```python
82
+ result = client.execute(
83
+ dataset="orders",
84
+ fields=["region"],
85
+ aggregates=[{"func": "sum", "field": "amount", "alias": "total"}],
86
+ having=[{"field": "SUM(amount)", "operator": "gt", "value": 1000}],
87
+ order_by={"column": "total", "direction": "desc"},
88
+ limit=10,
89
+ purpose="Quarterly revenue by region",
90
+ )
91
+ # [{"region": "US", "total": 18250}, {"region": "CA", "total": 4100}, ...]
92
+ ```
93
+
94
+ - `aggregates`: `func` is one of `count`, `sum`, `avg`, `min`, `max`; use `field="*"` with `count`.
95
+ - `group_by`: optional. Defaults to `fields`; if given, it must include every entry in `fields`.
96
+ - `having`: needs an aggregate expression such as `SUM(amount)`, not an alias.
97
+ - `order_by`: a field, an aggregate alias or an aggregate expression, `asc` (default) or `desc`.
98
+ - `fields` can be omitted for a single total, e.g. `aggregates=[{"func": "count", "field": "*"}]`.
99
+
100
+ Policies apply to every field these clauses use. `min`/`max`/`sum`/`avg` over a masked
101
+ field are denied; `count` is allowed. Joins are not supported.
102
+
103
+ > Requires a VaultKit control plane with intent analytics support. Older control
104
+ > planes ignore these parameters and return ungrouped rows.
105
+
106
+ ---
107
+
77
108
  ## AI Agent Usage
78
109
 
79
110
  VaultKit provides built-in tools for LLM agents.
File without changes
File without changes
File without changes
File without changes