shaped 1.0.2__py3-none-any.whl → 2.0.0__py3-none-any.whl
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.
- shaped/cli/shaped_cli.py +434 -157
- shaped-2.0.0.dist-info/METADATA +341 -0
- {shaped-1.0.2.dist-info → shaped-2.0.0.dist-info}/RECORD +7 -7
- {shaped-1.0.2.dist-info → shaped-2.0.0.dist-info}/WHEEL +1 -1
- shaped-1.0.2.dist-info/METADATA +0 -266
- {shaped-1.0.2.dist-info → shaped-2.0.0.dist-info}/entry_points.txt +0 -0
- {shaped-1.0.2.dist-info → shaped-2.0.0.dist-info}/top_level.txt +0 -0
- {shaped-1.0.2.dist-info → shaped-2.0.0.dist-info}/zip-safe +0 -0
shaped/cli/shaped_cli.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import sys
|
|
3
|
-
import urllib
|
|
4
3
|
from pathlib import Path
|
|
5
|
-
from typing import List, Optional
|
|
6
4
|
|
|
7
5
|
import pandas as pd
|
|
8
6
|
import pyarrow.parquet as pq
|
|
@@ -40,7 +38,7 @@ def _read_config() -> Config:
|
|
|
40
38
|
|
|
41
39
|
|
|
42
40
|
def _get_shaped_url(config: Config) -> str:
|
|
43
|
-
return f"https://api.{config.env}.shaped.ai/
|
|
41
|
+
return f"https://api.{config.env}.shaped.ai/v2"
|
|
44
42
|
|
|
45
43
|
|
|
46
44
|
def _parse_file_as_json(file: typer.FileText) -> str:
|
|
@@ -64,19 +62,45 @@ def _parse_response_as_yaml(content: str) -> str:
|
|
|
64
62
|
|
|
65
63
|
|
|
66
64
|
@app.command()
|
|
67
|
-
def init(
|
|
65
|
+
def init(
|
|
66
|
+
api_key: str = typer.Option(..., help="Your Shaped API key."),
|
|
67
|
+
env: str = typer.Option("prod", help="Environment to use (e.g., prod, dev, staging)."),
|
|
68
|
+
):
|
|
69
|
+
"""
|
|
70
|
+
Initialize the Shaped CLI with your API key and environment.
|
|
71
|
+
|
|
72
|
+
This command saves your configuration locally so you don't need to
|
|
73
|
+
provide your API key for every command.
|
|
74
|
+
"""
|
|
68
75
|
config = Config(api_key=api_key, env=env)
|
|
69
76
|
_write_config(config)
|
|
70
77
|
typer.echo(f"Initializing with config: {config.dict()}")
|
|
71
78
|
|
|
72
79
|
|
|
80
|
+
##############
|
|
81
|
+
# ENGINE API #
|
|
82
|
+
##############
|
|
83
|
+
|
|
73
84
|
@app.command()
|
|
74
|
-
def
|
|
75
|
-
file: typer.FileText = typer.Option(
|
|
85
|
+
def create_engine(
|
|
86
|
+
file: typer.FileText = typer.Option(
|
|
87
|
+
None, help="Path to a JSON or YAML file containing the engine configuration."
|
|
88
|
+
),
|
|
76
89
|
):
|
|
90
|
+
"""
|
|
91
|
+
Create a new engine.
|
|
92
|
+
|
|
93
|
+
The engine configuration can be provided via:
|
|
94
|
+
- --file: Path to a JSON/YAML file
|
|
95
|
+
- stdin: Pipe JSON/YAML content
|
|
96
|
+
"""
|
|
77
97
|
config = _read_config()
|
|
78
|
-
url = f"{_get_shaped_url(config)}/
|
|
79
|
-
headers = {
|
|
98
|
+
url = f"{_get_shaped_url(config)}/engines"
|
|
99
|
+
headers = {
|
|
100
|
+
"accept": "application/json",
|
|
101
|
+
"content-type": "application/json",
|
|
102
|
+
"x-api-key": config.api_key,
|
|
103
|
+
}
|
|
80
104
|
|
|
81
105
|
if not sys.stdin.isatty():
|
|
82
106
|
payload = sys.stdin.read()
|
|
@@ -87,14 +111,32 @@ def create_model(
|
|
|
87
111
|
|
|
88
112
|
typer.echo(payload)
|
|
89
113
|
response = requests.post(url, headers=headers, data=payload)
|
|
114
|
+
if response.status_code not in [200, 201]:
|
|
115
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
116
|
+
raise typer.Exit(1)
|
|
90
117
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
91
118
|
|
|
92
119
|
|
|
93
120
|
@app.command()
|
|
94
|
-
def
|
|
121
|
+
def update_engine(
|
|
122
|
+
file: typer.FileText = typer.Option(
|
|
123
|
+
None, help="Path to a JSON or YAML file containing the engine configuration."
|
|
124
|
+
),
|
|
125
|
+
):
|
|
126
|
+
"""
|
|
127
|
+
Update an existing engine.
|
|
128
|
+
|
|
129
|
+
The engine configuration can be provided via:
|
|
130
|
+
- --file: Path to a JSON/YAML file
|
|
131
|
+
- stdin: Pipe JSON/YAML content
|
|
132
|
+
"""
|
|
95
133
|
config = _read_config()
|
|
96
|
-
url = f"{_get_shaped_url(config)}/
|
|
97
|
-
headers = {
|
|
134
|
+
url = f"{_get_shaped_url(config)}/engines"
|
|
135
|
+
headers = {
|
|
136
|
+
"accept": "application/json",
|
|
137
|
+
"content-type": "application/json",
|
|
138
|
+
"x-api-key": config.api_key,
|
|
139
|
+
}
|
|
98
140
|
|
|
99
141
|
if not sys.stdin.isatty():
|
|
100
142
|
payload = sys.stdin.read()
|
|
@@ -105,150 +147,80 @@ def update_model(file: typer.FileText = typer.Option(None)):
|
|
|
105
147
|
|
|
106
148
|
typer.echo(payload)
|
|
107
149
|
response = requests.patch(url, headers=headers, data=payload)
|
|
150
|
+
if response.status_code != 200:
|
|
151
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
152
|
+
raise typer.Exit(1)
|
|
108
153
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
109
154
|
|
|
110
155
|
|
|
111
156
|
@app.command()
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
response = requests.get(url, headers=headers)
|
|
117
|
-
typer.echo(_parse_response_as_yaml(response.text))
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@app.command()
|
|
121
|
-
def view_model(model_name: str = typer.Option(...)):
|
|
157
|
+
def list_engines():
|
|
158
|
+
"""
|
|
159
|
+
List all engines.
|
|
160
|
+
"""
|
|
122
161
|
config = _read_config()
|
|
123
|
-
url = f"{_get_shaped_url(config)}/
|
|
162
|
+
url = f"{_get_shaped_url(config)}/engines"
|
|
124
163
|
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
125
164
|
response = requests.get(url, headers=headers)
|
|
165
|
+
if response.status_code != 200:
|
|
166
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
167
|
+
raise typer.Exit(1)
|
|
126
168
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
127
169
|
|
|
128
170
|
|
|
129
171
|
@app.command()
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
url = f"{_get_shaped_url(config)}/models/{model_name}"
|
|
133
|
-
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
134
|
-
response = requests.delete(url, headers=headers)
|
|
135
|
-
typer.echo(_parse_response_as_yaml(response.text))
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
@app.command()
|
|
139
|
-
def rank(
|
|
140
|
-
model_name: str = typer.Option(...),
|
|
141
|
-
user_id=typer.Option(None),
|
|
142
|
-
limit=typer.Option(15),
|
|
143
|
-
filter_predicate=typer.Option(None),
|
|
144
|
-
text_query: str = typer.Option(None),
|
|
145
|
-
return_metadata: bool = typer.Option(False),
|
|
146
|
-
flush_paginations: bool = typer.Option(False),
|
|
147
|
-
exploration_factor: float = typer.Option(0.0),
|
|
148
|
-
diversity_factor: float = typer.Option(0.0),
|
|
149
|
-
extra_query_args: str = typer.Option(None),
|
|
172
|
+
def view_engine(
|
|
173
|
+
engine_name: str = typer.Option(..., help="Name of the engine to view."),
|
|
150
174
|
):
|
|
175
|
+
"""
|
|
176
|
+
View the configuration of a specific engine.
|
|
177
|
+
"""
|
|
151
178
|
config = _read_config()
|
|
152
|
-
url = f"{_get_shaped_url(config)}/
|
|
179
|
+
url = f"{_get_shaped_url(config)}/engines/{engine_name}"
|
|
153
180
|
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
raise typer.BadParameter("`diversity_factor` must be greater than 0.0")
|
|
159
|
-
|
|
160
|
-
query_args = {
|
|
161
|
-
"exploration_factor": exploration_factor,
|
|
162
|
-
"diversity_factor": diversity_factor,
|
|
163
|
-
"limit": str(limit),
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if user_id is not None:
|
|
167
|
-
query_args |= {"user_id": user_id}
|
|
168
|
-
if filter_predicate is not None:
|
|
169
|
-
query_args |= {"filter_predicate": filter_predicate}
|
|
170
|
-
if return_metadata is not None:
|
|
171
|
-
query_args |= {"return_metadata": bool(return_metadata)}
|
|
172
|
-
if text_query is not None:
|
|
173
|
-
query_args |= {"text_query": text_query}
|
|
174
|
-
if flush_paginations is not None:
|
|
175
|
-
query_args |= {"flush_paginations": flush_paginations}
|
|
176
|
-
if extra_query_args is not None:
|
|
177
|
-
extra_query_args = json.loads(extra_query_args)
|
|
178
|
-
query_args |= extra_query_args
|
|
179
|
-
|
|
180
|
-
response = requests.post(url, headers=headers, json=query_args)
|
|
181
|
+
response = requests.get(url, headers=headers)
|
|
182
|
+
if response.status_code != 200:
|
|
183
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
184
|
+
raise typer.Exit(1)
|
|
181
185
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
182
186
|
|
|
183
187
|
|
|
184
188
|
@app.command()
|
|
185
|
-
def
|
|
186
|
-
|
|
187
|
-
item_ids: List[str] = typer.Option(...),
|
|
188
|
-
user_id: Optional[str] = typer.Option(None),
|
|
189
|
-
return_metadata: bool = typer.Option(False),
|
|
190
|
-
limit=typer.Option(15),
|
|
189
|
+
def delete_engine(
|
|
190
|
+
engine_name: str = typer.Option(..., help="Name of the engine to delete."),
|
|
191
191
|
):
|
|
192
|
+
"""
|
|
193
|
+
Delete an engine.
|
|
194
|
+
"""
|
|
192
195
|
config = _read_config()
|
|
193
|
-
url = f"{_get_shaped_url(config)}/
|
|
196
|
+
url = f"{_get_shaped_url(config)}/engines/{engine_name}"
|
|
194
197
|
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if user_id is not None:
|
|
202
|
-
query_args |= {"user_id": user_id}
|
|
203
|
-
if return_metadata is not None:
|
|
204
|
-
query_args |= {"return_metadata": bool(return_metadata)}
|
|
205
|
-
|
|
206
|
-
response = requests.post(url, headers=headers, json=query_args)
|
|
207
|
-
typer.echo(_parse_response_as_yaml(response.text))
|
|
198
|
+
response = requests.delete(url, headers=headers)
|
|
199
|
+
if response.status_code not in [200, 204]:
|
|
200
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
201
|
+
raise typer.Exit(1)
|
|
202
|
+
if response.text:
|
|
203
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
208
204
|
|
|
209
205
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
item_id: str = typer.Option(...),
|
|
214
|
-
user_id: Optional[str] = typer.Option(None),
|
|
215
|
-
limit=typer.Option(15),
|
|
216
|
-
extra_query_args: str = typer.Option(None),
|
|
217
|
-
):
|
|
218
|
-
config = _read_config()
|
|
219
|
-
item_id = urllib.parse.quote(item_id, safe="/", encoding=None, errors=None)
|
|
220
|
-
path = f"/models/{model_name}/similar_items"
|
|
221
|
-
path += f"?limit={limit}&item_id={item_id}"
|
|
222
|
-
if user_id is not None:
|
|
223
|
-
user_id = urllib.parse.quote(user_id, safe="/", encoding=None, errors=None)
|
|
224
|
-
path += f"&user_id={user_id}"
|
|
225
|
-
|
|
226
|
-
url = f"{_get_shaped_url(config)}{path}"
|
|
227
|
-
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
228
|
-
response = requests.get(url, headers=headers)
|
|
229
|
-
typer.echo(_parse_response_as_yaml(response.text))
|
|
206
|
+
##############
|
|
207
|
+
# TABLE API #
|
|
208
|
+
##############
|
|
230
209
|
|
|
231
210
|
|
|
232
211
|
@app.command()
|
|
233
|
-
def
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
url = f"{_get_shaped_url(config)}/models/{model_name}/similar_users"
|
|
240
|
-
url += f"?limit={limit}&user_id={user_id}"
|
|
241
|
-
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
242
|
-
response = requests.get(url, headers=headers)
|
|
243
|
-
typer.echo(_parse_response_as_yaml(response.text))
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
@app.command()
|
|
247
|
-
def create_dataset_from_uri(
|
|
248
|
-
name: str = typer.Option(...),
|
|
249
|
-
path: str = typer.Option(...),
|
|
250
|
-
type: str = typer.Option(...),
|
|
212
|
+
def create_table_from_uri(
|
|
213
|
+
name: str = typer.Option(..., help="Name of the table to create."),
|
|
214
|
+
path: str = typer.Option(..., help="Path to the data file."),
|
|
215
|
+
type: str = typer.Option(
|
|
216
|
+
..., help="File type. One of: parquet, csv, tsv, json, jsonl."
|
|
217
|
+
),
|
|
251
218
|
):
|
|
219
|
+
"""
|
|
220
|
+
Create a table from a data file and automatically insert the data.
|
|
221
|
+
|
|
222
|
+
The table schema is inferred from the first chunk of data.
|
|
223
|
+
"""
|
|
252
224
|
chunks = _read_chunks(path, type, chunk_size=1)
|
|
253
225
|
chunk = next(iter(chunks))
|
|
254
226
|
if chunk is None:
|
|
@@ -259,18 +231,29 @@ def create_dataset_from_uri(
|
|
|
259
231
|
raise ValueError("No columns found.")
|
|
260
232
|
|
|
261
233
|
chunk_column_definitions = {col: "String" for col in chunk_columns}
|
|
262
|
-
|
|
234
|
+
table_definition = {
|
|
263
235
|
"name": name,
|
|
264
236
|
"schema_type": "CUSTOM",
|
|
265
237
|
"column_schema": chunk_column_definitions,
|
|
266
238
|
}
|
|
267
|
-
|
|
268
|
-
if
|
|
269
|
-
|
|
239
|
+
table_created = _create_table_from_payload(json.dumps(table_definition))
|
|
240
|
+
if table_created:
|
|
241
|
+
table_insert(name, path, type)
|
|
270
242
|
|
|
271
243
|
|
|
272
244
|
@app.command()
|
|
273
|
-
def
|
|
245
|
+
def create_table(
|
|
246
|
+
file: typer.FileText = typer.Option(
|
|
247
|
+
None, help="Path to a JSON or YAML file containing the table configuration."
|
|
248
|
+
),
|
|
249
|
+
):
|
|
250
|
+
"""
|
|
251
|
+
Create a new table.
|
|
252
|
+
|
|
253
|
+
The table configuration can be provided via:
|
|
254
|
+
- --file: Path to a JSON/YAML file
|
|
255
|
+
- stdin: Pipe JSON/YAML content
|
|
256
|
+
"""
|
|
274
257
|
if not sys.stdin.isatty():
|
|
275
258
|
payload = sys.stdin.read()
|
|
276
259
|
elif file is not None:
|
|
@@ -278,42 +261,78 @@ def create_dataset(file: typer.FileText = typer.Option(None)):
|
|
|
278
261
|
else:
|
|
279
262
|
raise ValueError("Must provide either a '--file' or stdin input.")
|
|
280
263
|
|
|
281
|
-
|
|
264
|
+
_create_table_from_payload(payload)
|
|
282
265
|
|
|
283
266
|
|
|
284
|
-
def
|
|
267
|
+
def _create_table_from_payload(payload: str) -> bool:
|
|
285
268
|
config = _read_config()
|
|
286
|
-
url = f"{_get_shaped_url(config)}/
|
|
287
|
-
headers = {
|
|
269
|
+
url = f"{_get_shaped_url(config)}/tables"
|
|
270
|
+
headers = {
|
|
271
|
+
"accept": "application/json",
|
|
272
|
+
"content-type": "application/json",
|
|
273
|
+
"x-api-key": config.api_key,
|
|
274
|
+
}
|
|
288
275
|
typer.echo(payload)
|
|
289
276
|
response = requests.post(url, headers=headers, data=payload)
|
|
277
|
+
if response.status_code not in [200, 201]:
|
|
278
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
279
|
+
raise typer.Exit(1)
|
|
290
280
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
291
|
-
return response.status_code
|
|
281
|
+
return response.status_code in [200, 201]
|
|
292
282
|
|
|
293
283
|
|
|
294
284
|
@app.command()
|
|
295
|
-
def
|
|
285
|
+
def list_tables():
|
|
286
|
+
"""
|
|
287
|
+
List all tables.
|
|
288
|
+
"""
|
|
296
289
|
config = _read_config()
|
|
297
|
-
url = f"{_get_shaped_url(config)}/
|
|
290
|
+
url = f"{_get_shaped_url(config)}/tables"
|
|
298
291
|
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
299
292
|
response = requests.get(url, headers=headers)
|
|
293
|
+
if response.status_code != 200:
|
|
294
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
295
|
+
raise typer.Exit(1)
|
|
300
296
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
301
297
|
|
|
302
298
|
|
|
303
299
|
@app.command()
|
|
304
|
-
def
|
|
300
|
+
def view_table(
|
|
301
|
+
table_name: str = typer.Option(..., help="Name of the table to view."),
|
|
302
|
+
):
|
|
303
|
+
"""
|
|
304
|
+
View the configuration of a specific table.
|
|
305
|
+
"""
|
|
305
306
|
config = _read_config()
|
|
306
|
-
url = f"{_get_shaped_url(config)}/
|
|
307
|
+
url = f"{_get_shaped_url(config)}/tables/{table_name}"
|
|
307
308
|
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
308
309
|
response = requests.get(url, headers=headers)
|
|
310
|
+
if response.status_code != 200:
|
|
311
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
312
|
+
raise typer.Exit(1)
|
|
309
313
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
310
314
|
|
|
311
315
|
|
|
312
316
|
@app.command()
|
|
313
|
-
def
|
|
317
|
+
def update_table(
|
|
318
|
+
file: typer.FileText = typer.Option(
|
|
319
|
+
None, help="Path to a JSON or YAML file containing the table configuration."
|
|
320
|
+
),
|
|
321
|
+
):
|
|
322
|
+
"""
|
|
323
|
+
Update an existing table.
|
|
324
|
+
|
|
325
|
+
The table configuration can be provided via:
|
|
326
|
+
- --file: Path to a JSON/YAML file
|
|
327
|
+
- stdin: Pipe JSON/YAML content
|
|
328
|
+
"""
|
|
314
329
|
config = _read_config()
|
|
315
|
-
url = f"{_get_shaped_url(config)}/
|
|
316
|
-
headers = {
|
|
330
|
+
url = f"{_get_shaped_url(config)}/tables"
|
|
331
|
+
headers = {
|
|
332
|
+
"accept": "application/json",
|
|
333
|
+
"content-type": "application/json",
|
|
334
|
+
"x-api-key": config.api_key,
|
|
335
|
+
}
|
|
317
336
|
|
|
318
337
|
if not sys.stdin.isatty():
|
|
319
338
|
payload = sys.stdin.read()
|
|
@@ -324,18 +343,32 @@ def update_dataset(file: typer.FileText = typer.Option(None)):
|
|
|
324
343
|
|
|
325
344
|
typer.echo(payload)
|
|
326
345
|
response = requests.patch(url, headers=headers, data=payload)
|
|
346
|
+
if response.status_code != 200:
|
|
347
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
348
|
+
raise typer.Exit(1)
|
|
327
349
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
328
350
|
|
|
329
351
|
|
|
330
352
|
@app.command()
|
|
331
|
-
def
|
|
332
|
-
|
|
333
|
-
file: str = typer.Option(
|
|
334
|
-
type: str = typer.Option(
|
|
353
|
+
def table_insert(
|
|
354
|
+
table_name: str = typer.Option(..., help="Name of the table to insert data into."),
|
|
355
|
+
file: str = typer.Option(..., help="Path to the data file to insert."),
|
|
356
|
+
type: str = typer.Option(
|
|
357
|
+
..., help="File type. One of: parquet, csv, tsv, json, jsonl."
|
|
358
|
+
),
|
|
335
359
|
):
|
|
360
|
+
"""
|
|
361
|
+
Insert data into a table from a file.
|
|
362
|
+
|
|
363
|
+
Data is read in chunks and uploaded progressively with a progress bar.
|
|
364
|
+
"""
|
|
336
365
|
config = _read_config()
|
|
337
|
-
url = f"{_get_shaped_url(config)}/
|
|
338
|
-
headers = {
|
|
366
|
+
url = f"{_get_shaped_url(config)}/tables/{table_name}/insert"
|
|
367
|
+
headers = {
|
|
368
|
+
"accept": "application/json",
|
|
369
|
+
"content-type": "application/json",
|
|
370
|
+
"x-api-key": config.api_key,
|
|
371
|
+
}
|
|
339
372
|
bar = tqdm(unit=" Records")
|
|
340
373
|
|
|
341
374
|
def _write_chunk(chunk: pd.DataFrame):
|
|
@@ -343,14 +376,15 @@ def dataset_insert(
|
|
|
343
376
|
payload = json.dumps({"data": json.loads(chunk.to_json(orient="records"))})
|
|
344
377
|
response = requests.post(url, headers=headers, data=payload)
|
|
345
378
|
if response.status_code != 200:
|
|
346
|
-
typer.echo(response.text)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
_parse_response_as_yaml(response.text)
|
|
379
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
380
|
+
bar.close()
|
|
381
|
+
raise typer.Exit(1)
|
|
350
382
|
|
|
351
383
|
# Chunk read and upload.
|
|
352
384
|
for chunk in _read_chunks(file, type, chunk_size=1000):
|
|
353
385
|
_write_chunk(chunk)
|
|
386
|
+
|
|
387
|
+
bar.close()
|
|
354
388
|
|
|
355
389
|
|
|
356
390
|
def _read_chunks(file: str, type: str, chunk_size: int) -> pd.DataFrame:
|
|
@@ -381,11 +415,254 @@ def _read_chunks(file: str, type: str, chunk_size: int) -> pd.DataFrame:
|
|
|
381
415
|
|
|
382
416
|
|
|
383
417
|
@app.command()
|
|
384
|
-
def
|
|
418
|
+
def delete_table(
|
|
419
|
+
table_name: str = typer.Option(..., help="Name of the table to delete."),
|
|
420
|
+
):
|
|
421
|
+
"""
|
|
422
|
+
Delete a table.
|
|
423
|
+
"""
|
|
385
424
|
config = _read_config()
|
|
386
|
-
url = f"{_get_shaped_url(config)}/
|
|
425
|
+
url = f"{_get_shaped_url(config)}/tables/{table_name}"
|
|
387
426
|
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
388
427
|
response = requests.delete(url, headers=headers)
|
|
428
|
+
if response.status_code not in [200, 204]:
|
|
429
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
430
|
+
raise typer.Exit(1)
|
|
431
|
+
if response.text:
|
|
432
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
@app.command()
|
|
436
|
+
def create_view(
|
|
437
|
+
file: typer.FileText = typer.Option(
|
|
438
|
+
None, help="Path to a JSON or YAML file containing the view configuration."
|
|
439
|
+
),
|
|
440
|
+
):
|
|
441
|
+
"""
|
|
442
|
+
Create a new view.
|
|
443
|
+
|
|
444
|
+
The view configuration can be provided via:
|
|
445
|
+
- --file: Path to a JSON/YAML file
|
|
446
|
+
- stdin: Pipe JSON/YAML content
|
|
447
|
+
"""
|
|
448
|
+
config = _read_config()
|
|
449
|
+
url = f"{_get_shaped_url(config)}/views"
|
|
450
|
+
headers = {
|
|
451
|
+
"accept": "application/json",
|
|
452
|
+
"content-type": "application/json",
|
|
453
|
+
"x-api-key": config.api_key,
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if not sys.stdin.isatty():
|
|
457
|
+
payload = sys.stdin.read()
|
|
458
|
+
elif file is not None:
|
|
459
|
+
payload = _parse_file_as_json(file)
|
|
460
|
+
else:
|
|
461
|
+
raise ValueError("Must provide either a '--file' or stdin input.")
|
|
462
|
+
|
|
463
|
+
typer.echo(payload)
|
|
464
|
+
response = requests.post(url, headers=headers, data=payload)
|
|
465
|
+
if response.status_code not in [200, 201]:
|
|
466
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
467
|
+
raise typer.Exit(1)
|
|
468
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
#################
|
|
472
|
+
# VIEW API #
|
|
473
|
+
#################
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
@app.command()
|
|
477
|
+
def list_views():
|
|
478
|
+
"""
|
|
479
|
+
List all views.
|
|
480
|
+
"""
|
|
481
|
+
config = _read_config()
|
|
482
|
+
url = f"{_get_shaped_url(config)}/views"
|
|
483
|
+
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
484
|
+
response = requests.get(url, headers=headers)
|
|
485
|
+
if response.status_code != 200:
|
|
486
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
487
|
+
raise typer.Exit(1)
|
|
488
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@app.command()
|
|
492
|
+
def view_view(
|
|
493
|
+
view_name: str = typer.Option(..., help="Name of the view to view."),
|
|
494
|
+
):
|
|
495
|
+
"""
|
|
496
|
+
View the configuration of a specific view.
|
|
497
|
+
"""
|
|
498
|
+
config = _read_config()
|
|
499
|
+
url = f"{_get_shaped_url(config)}/views/{view_name}"
|
|
500
|
+
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
501
|
+
response = requests.get(url, headers=headers)
|
|
502
|
+
if response.status_code != 200:
|
|
503
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
504
|
+
raise typer.Exit(1)
|
|
505
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
@app.command()
|
|
509
|
+
def update_view(
|
|
510
|
+
file: typer.FileText = typer.Option(
|
|
511
|
+
None, help="Path to a JSON or YAML file containing the view configuration."
|
|
512
|
+
),
|
|
513
|
+
):
|
|
514
|
+
"""
|
|
515
|
+
Update an existing view.
|
|
516
|
+
|
|
517
|
+
The view configuration can be provided via:
|
|
518
|
+
- --file: Path to a JSON/YAML file
|
|
519
|
+
- stdin: Pipe JSON/YAML content
|
|
520
|
+
"""
|
|
521
|
+
config = _read_config()
|
|
522
|
+
url = f"{_get_shaped_url(config)}/views"
|
|
523
|
+
headers = {
|
|
524
|
+
"accept": "application/json",
|
|
525
|
+
"content-type": "application/json",
|
|
526
|
+
"x-api-key": config.api_key,
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if not sys.stdin.isatty():
|
|
530
|
+
payload = sys.stdin.read()
|
|
531
|
+
elif file is not None:
|
|
532
|
+
payload = _parse_file_as_json(file)
|
|
533
|
+
else:
|
|
534
|
+
raise ValueError("Must provide either a '--file' or stdin input.")
|
|
535
|
+
|
|
536
|
+
typer.echo(payload)
|
|
537
|
+
response = requests.patch(url, headers=headers, data=payload)
|
|
538
|
+
if response.status_code != 200:
|
|
539
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
540
|
+
raise typer.Exit(1)
|
|
541
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
@app.command()
|
|
545
|
+
def delete_view(
|
|
546
|
+
view_name: str = typer.Option(..., help="Name of the view to delete."),
|
|
547
|
+
):
|
|
548
|
+
"""
|
|
549
|
+
Delete a view.
|
|
550
|
+
"""
|
|
551
|
+
config = _read_config()
|
|
552
|
+
url = f"{_get_shaped_url(config)}/views/{view_name}"
|
|
553
|
+
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
554
|
+
response = requests.delete(url, headers=headers)
|
|
555
|
+
if response.status_code not in [200, 204]:
|
|
556
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
557
|
+
raise typer.Exit(1)
|
|
558
|
+
if response.text:
|
|
559
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
#################
|
|
563
|
+
# QUERY API #
|
|
564
|
+
#################
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
@app.command()
|
|
568
|
+
def query(
|
|
569
|
+
engine_name: str = typer.Option(..., help="Name of the engine to execute the query against."),
|
|
570
|
+
query_file: typer.FileText = typer.Option(
|
|
571
|
+
None, help="Path to a JSON or YAML file containing the query."
|
|
572
|
+
),
|
|
573
|
+
query: str = typer.Option(
|
|
574
|
+
None, help="JSON string containing the query. Can be used instead of --query-file."
|
|
575
|
+
),
|
|
576
|
+
):
|
|
577
|
+
"""
|
|
578
|
+
Execute an ad-hoc query against an engine.
|
|
579
|
+
|
|
580
|
+
The query can be provided via:
|
|
581
|
+
- --query-file: Path to a JSON/YAML file
|
|
582
|
+
- --query: JSON string directly
|
|
583
|
+
- stdin: Pipe JSON/YAML content
|
|
584
|
+
"""
|
|
585
|
+
config = _read_config()
|
|
586
|
+
url = f"{_get_shaped_url(config)}/engines/{engine_name}/query"
|
|
587
|
+
headers = {
|
|
588
|
+
"accept": "application/json",
|
|
589
|
+
"content-type": "application/json",
|
|
590
|
+
"x-api-key": config.api_key,
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if not sys.stdin.isatty():
|
|
594
|
+
payload = sys.stdin.read()
|
|
595
|
+
elif query_file is not None:
|
|
596
|
+
payload = _parse_file_as_json(query_file)
|
|
597
|
+
elif query is not None:
|
|
598
|
+
payload = query
|
|
599
|
+
else:
|
|
600
|
+
raise ValueError(
|
|
601
|
+
"Must provide either a '--query-file', '--query' JSON string, or stdin input."
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
typer.echo(payload)
|
|
605
|
+
response = requests.post(url, headers=headers, data=payload)
|
|
606
|
+
if response.status_code != 200:
|
|
607
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
608
|
+
raise typer.Exit(1)
|
|
609
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
@app.command()
|
|
613
|
+
def execute_saved_query(
|
|
614
|
+
engine_name: str = typer.Option(..., help="Name of the engine containing the saved query."),
|
|
615
|
+
query_name: str = typer.Option(..., help="Name of the saved query to execute."),
|
|
616
|
+
):
|
|
617
|
+
"""
|
|
618
|
+
Execute a previously saved query by name.
|
|
619
|
+
"""
|
|
620
|
+
config = _read_config()
|
|
621
|
+
url = f"{_get_shaped_url(config)}/engines/{engine_name}/queries/{query_name}"
|
|
622
|
+
headers = {
|
|
623
|
+
"accept": "application/json",
|
|
624
|
+
"content-type": "application/json",
|
|
625
|
+
"x-api-key": config.api_key,
|
|
626
|
+
}
|
|
627
|
+
response = requests.post(url, headers=headers)
|
|
628
|
+
if response.status_code != 200:
|
|
629
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
630
|
+
raise typer.Exit(1)
|
|
631
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
@app.command()
|
|
635
|
+
def view_saved_query(
|
|
636
|
+
engine_name: str = typer.Option(..., help="Name of the engine containing the saved query."),
|
|
637
|
+
query_name: str = typer.Option(..., help="Name of the saved query to view."),
|
|
638
|
+
):
|
|
639
|
+
"""
|
|
640
|
+
View the definition of a saved query.
|
|
641
|
+
"""
|
|
642
|
+
config = _read_config()
|
|
643
|
+
url = f"{_get_shaped_url(config)}/engines/{engine_name}/queries/{query_name}"
|
|
644
|
+
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
645
|
+
response = requests.get(url, headers=headers)
|
|
646
|
+
if response.status_code != 200:
|
|
647
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
648
|
+
raise typer.Exit(1)
|
|
649
|
+
typer.echo(_parse_response_as_yaml(response.text))
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
@app.command()
|
|
653
|
+
def list_saved_queries(
|
|
654
|
+
engine_name: str = typer.Option(..., help="Name of the engine to list saved queries for."),
|
|
655
|
+
):
|
|
656
|
+
"""
|
|
657
|
+
List all saved queries for an engine.
|
|
658
|
+
"""
|
|
659
|
+
config = _read_config()
|
|
660
|
+
url = f"{_get_shaped_url(config)}/engines/{engine_name}/queries"
|
|
661
|
+
headers = {"accept": "application/json", "x-api-key": config.api_key}
|
|
662
|
+
response = requests.get(url, headers=headers)
|
|
663
|
+
if response.status_code != 200:
|
|
664
|
+
typer.echo(f"Error: {response.status_code}\n{response.text}")
|
|
665
|
+
raise typer.Exit(1)
|
|
389
666
|
typer.echo(_parse_response_as_yaml(response.text))
|
|
390
667
|
|
|
391
668
|
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shaped
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: CLI and SDK tools for interacting with the Shaped API.
|
|
5
|
+
Home-page: https://github.com/shaped-ai/shaped-cli
|
|
6
|
+
Author: Shaped Team
|
|
7
|
+
Author-email: support@shaped.ai
|
|
8
|
+
Keywords: shaped-ai
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Requires-Python: >=3.9, <3.14
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: pyarrow==20.0.0
|
|
17
|
+
Requires-Dist: pandas==2.3.0
|
|
18
|
+
Requires-Dist: numpy==1.26.4
|
|
19
|
+
Requires-Dist: typer>=0.7.0
|
|
20
|
+
Requires-Dist: requests>=2.28.1
|
|
21
|
+
Requires-Dist: pydantic>=2.8.2
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: tqdm==4.67.1
|
|
24
|
+
Requires-Dist: s3fs==0.4.2
|
|
25
|
+
Requires-Dist: fsspec==2023.5.0
|
|
26
|
+
Requires-Dist: urllib3<2.1.0,>=1.25.3
|
|
27
|
+
Requires-Dist: python-dateutil
|
|
28
|
+
Requires-Dist: typing-extensions>=4.7.1
|
|
29
|
+
Requires-Dist: pytest>=6.2.5
|
|
30
|
+
Requires-Dist: pytest-mock==3.14.0
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: classifier
|
|
34
|
+
Dynamic: description
|
|
35
|
+
Dynamic: description-content-type
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: keywords
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
41
|
+
|
|
42
|
+
# Shaped CLI
|
|
43
|
+
|
|
44
|
+
CLI for interactions with the Shaped API.
|
|
45
|
+
|
|
46
|
+
## Installing the Shaped CLI
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
pip install shaped
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Initialize
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
shaped init --api-key <API_KEY> [--env <ENV>]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The `--env` option defaults to `prod` and can be set to other environments like `dev` or `staging`.
|
|
59
|
+
|
|
60
|
+
## Engine API
|
|
61
|
+
|
|
62
|
+
### Create Engine
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
shaped create-engine --file <PATH_TO_FILE>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or pipe from stdin:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
cat <PATH_TO_FILE> | shaped create-engine
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Update Engine
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
shaped update-engine --file <PATH_TO_FILE>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or pipe from stdin:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
cat <PATH_TO_FILE> | shaped update-engine
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### List Engines
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
shaped list-engines
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### View Engine
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
shaped view-engine --engine-name <ENGINE_NAME>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Delete Engine
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
shaped delete-engine --engine-name <ENGINE_NAME>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Table API
|
|
105
|
+
|
|
106
|
+
### Create Table
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
shaped create-table --file <PATH_TO_FILE>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Or pipe from stdin:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
cat <PATH_TO_FILE> | shaped create-table
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Create Table from URI
|
|
119
|
+
|
|
120
|
+
Create a table and automatically insert data from a file:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
shaped create-table-from-uri --name <TABLE_NAME> --path <PATH_TO_FILE> --type <FILE_TYPE>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Supported file types: `parquet`, `csv`, `tsv`, `json`, `jsonl`
|
|
127
|
+
|
|
128
|
+
### List Tables
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
shaped list-tables
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Table Insert
|
|
135
|
+
|
|
136
|
+
Insert data into an existing table:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
shaped table-insert --table-name <TABLE_NAME> --file <DATAFRAME_FILE> --type <FILE_TYPE>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Supported file types: `parquet`, `csv`, `tsv`, `json`, `jsonl`
|
|
143
|
+
|
|
144
|
+
### View Table
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
shaped view-table --table-name <TABLE_NAME>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Update Table
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
shaped update-table --file <PATH_TO_FILE>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Or pipe from stdin:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
cat <PATH_TO_FILE> | shaped update-table
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Delete Table
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
shaped delete-table --table-name <TABLE_NAME>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## View API
|
|
169
|
+
|
|
170
|
+
### Create View
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
shaped create-view --file <PATH_TO_FILE>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Or pipe from stdin:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
cat <PATH_TO_FILE> | shaped create-view
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### List Views
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
shaped list-views
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### View View
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
shaped view-view --view-name <VIEW_NAME>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Update View
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
shaped update-view --file <PATH_TO_FILE>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Or pipe from stdin:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
cat <PATH_TO_FILE> | shaped update-view
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Delete View
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
shaped delete-view --view-name <VIEW_NAME>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Query API
|
|
213
|
+
|
|
214
|
+
### Execute Query
|
|
215
|
+
|
|
216
|
+
Execute an ad-hoc query against an engine. The query can be provided in three ways:
|
|
217
|
+
|
|
218
|
+
**From a file:**
|
|
219
|
+
```
|
|
220
|
+
shaped query --engine-name <ENGINE_NAME> --query-file <PATH_TO_FILE>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**As a JSON string:**
|
|
224
|
+
```
|
|
225
|
+
shaped query --engine-name <ENGINE_NAME> --query '{"sql": "SELECT * FROM table"}'
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**From stdin:**
|
|
229
|
+
```
|
|
230
|
+
cat <PATH_TO_FILE> | shaped query --engine-name <ENGINE_NAME>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Execute Saved Query
|
|
234
|
+
|
|
235
|
+
Execute a previously saved query by name:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
shaped execute-saved-query --engine-name <ENGINE_NAME> --query-name <QUERY_NAME>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### List Saved Queries
|
|
242
|
+
|
|
243
|
+
List all saved queries for an engine:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
shaped list-saved-queries --engine-name <ENGINE_NAME>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### View Saved Query
|
|
250
|
+
|
|
251
|
+
View the definition of a saved query:
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
shaped view-saved-query --engine-name <ENGINE_NAME> --query-name <QUERY_NAME>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
# Python SDK
|
|
260
|
+
|
|
261
|
+
## Installation
|
|
262
|
+
|
|
263
|
+
### Pip Installation
|
|
264
|
+
|
|
265
|
+
```sh
|
|
266
|
+
pip install shaped
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Rank
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
import shaped
|
|
273
|
+
|
|
274
|
+
api_key = 'your_api_key'
|
|
275
|
+
client = shaped.Client(api_key=api_key)
|
|
276
|
+
|
|
277
|
+
api_response = client.rank(
|
|
278
|
+
model_name="amazon_beauty_product_recommendations",
|
|
279
|
+
user_id="A2FRWMTWYJUK7P",
|
|
280
|
+
return_metadata=True,
|
|
281
|
+
item_ids=['B01AHSUT8M', 'B00GW7H5EY', 'B01GLA54SA', 'B0016PKWK6', 'B00PJD7KPG', 'B00BCI8OP2', 'B004FK7R02'],
|
|
282
|
+
)
|
|
283
|
+
print(api_response)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Retrieve
|
|
287
|
+
```python
|
|
288
|
+
import shaped
|
|
289
|
+
|
|
290
|
+
api_key = 'your_api_key'
|
|
291
|
+
client = shaped.Client(api_key=api_key)
|
|
292
|
+
|
|
293
|
+
api_response = client.retrieve(
|
|
294
|
+
model_name="amazon_beauty_product_recommendations",
|
|
295
|
+
)
|
|
296
|
+
print(api_response)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Similar Items
|
|
300
|
+
```python
|
|
301
|
+
import shaped
|
|
302
|
+
|
|
303
|
+
api_key = 'your_api_key'
|
|
304
|
+
client = shaped.Client(api_key=api_key)
|
|
305
|
+
|
|
306
|
+
api_response = client.similar_items(
|
|
307
|
+
model_name="amazon_beauty_product_recommendations",
|
|
308
|
+
item_id="B000FOI48G",
|
|
309
|
+
)
|
|
310
|
+
print(api_response)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Similar Users
|
|
314
|
+
```python
|
|
315
|
+
import shaped
|
|
316
|
+
|
|
317
|
+
api_key = 'your_api_key'
|
|
318
|
+
client = shaped.Client(api_key=api_key)
|
|
319
|
+
|
|
320
|
+
api_response = client.similar_users(
|
|
321
|
+
model_name="amazon_beauty_product_recommendations",
|
|
322
|
+
user_id="A2FRWMTWYJUK7P",
|
|
323
|
+
)
|
|
324
|
+
print(api_response)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Complement Items
|
|
328
|
+
```python
|
|
329
|
+
import shaped
|
|
330
|
+
|
|
331
|
+
api_key = 'your_api_key'
|
|
332
|
+
client = shaped.Client(api_key=api_key)
|
|
333
|
+
|
|
334
|
+
api_response = client.complement_items(
|
|
335
|
+
model_name="amazon_beauty_product_recommendations",
|
|
336
|
+
item_ids=['B000URXP6E', 'B0012Y0ZG2'],
|
|
337
|
+
)
|
|
338
|
+
print(api_response)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
@@ -64,10 +64,10 @@ shaped/autogen/models/validation_error.py,sha256=kVstUJ1nwQ6Y9kssgDBpeoKGFScWegp
|
|
|
64
64
|
shaped/autogen/models/value_type.py,sha256=uxlGNPto3CK17jHKDRvAl3BqNCPkHlxfRBPdofi_qdM,2917
|
|
65
65
|
shaped/autogen/models/view_model_response.py,sha256=4xzesZSHMcPCZHsevo5Bt_3P9jBNiEbd_PFTWQ5Q9KU,5757
|
|
66
66
|
shaped/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
|
-
shaped/cli/shaped_cli.py,sha256=
|
|
68
|
-
shaped-
|
|
69
|
-
shaped-
|
|
70
|
-
shaped-
|
|
71
|
-
shaped-
|
|
72
|
-
shaped-
|
|
73
|
-
shaped-
|
|
67
|
+
shaped/cli/shaped_cli.py,sha256=DzBbnRlk2XWkCq8eGGUVRatm6ntVHclfsrNHYEYxdyA,20562
|
|
68
|
+
shaped-2.0.0.dist-info/METADATA,sha256=uNlIBs-grJV6aNnpdVM-NyICOtzi0mEUMxeqJdktkSU,5761
|
|
69
|
+
shaped-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
70
|
+
shaped-2.0.0.dist-info/entry_points.txt,sha256=4xoVmnNTKtmzjGNX5ezp9MCWLgi7880TtfqFByN1u5U,53
|
|
71
|
+
shaped-2.0.0.dist-info/top_level.txt,sha256=w-lDaoadQVYpze9N9gZyK9qngb7fZCJ-KCdHLGvt0SU,7
|
|
72
|
+
shaped-2.0.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
73
|
+
shaped-2.0.0.dist-info/RECORD,,
|
shaped-1.0.2.dist-info/METADATA
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.2
|
|
2
|
-
Name: shaped
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: CLI and SDK tools for interacting with the Shaped API.
|
|
5
|
-
Home-page: https://github.com/shaped-ai/shaped-cli
|
|
6
|
-
Author: Shaped Team
|
|
7
|
-
Author-email: support@shaped.ai
|
|
8
|
-
Keywords: shaped-ai
|
|
9
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
-
Classifier: Operating System :: MacOS :: MacOS X
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Requires-Python: >=3.8, <3.12
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
Requires-Dist: typer>=0.7.0
|
|
17
|
-
Requires-Dist: requests>=2.28.1
|
|
18
|
-
Requires-Dist: pydantic>=2.8.2
|
|
19
|
-
Requires-Dist: pyyaml>=6.0
|
|
20
|
-
Requires-Dist: pyarrow==11.0.0
|
|
21
|
-
Requires-Dist: pandas==1.5.3
|
|
22
|
-
Requires-Dist: tqdm==4.65.0
|
|
23
|
-
Requires-Dist: s3fs==0.4.2
|
|
24
|
-
Requires-Dist: fsspec==2023.5.0
|
|
25
|
-
Requires-Dist: numpy==1.26.4
|
|
26
|
-
Requires-Dist: urllib3<2.1.0,>=1.25.3
|
|
27
|
-
Requires-Dist: python-dateutil
|
|
28
|
-
Requires-Dist: typing-extensions>=4.7.1
|
|
29
|
-
Requires-Dist: pytest>=6.2.5
|
|
30
|
-
Requires-Dist: pytest-mock==3.14.0
|
|
31
|
-
Dynamic: author
|
|
32
|
-
Dynamic: author-email
|
|
33
|
-
Dynamic: classifier
|
|
34
|
-
Dynamic: description
|
|
35
|
-
Dynamic: description-content-type
|
|
36
|
-
Dynamic: home-page
|
|
37
|
-
Dynamic: keywords
|
|
38
|
-
Dynamic: requires-dist
|
|
39
|
-
Dynamic: requires-python
|
|
40
|
-
Dynamic: summary
|
|
41
|
-
|
|
42
|
-
# Python SDK
|
|
43
|
-
|
|
44
|
-
## Installation
|
|
45
|
-
|
|
46
|
-
### Local Development
|
|
47
|
-
|
|
48
|
-
```sh
|
|
49
|
-
pip install -e .
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Pip Installation
|
|
53
|
-
|
|
54
|
-
```sh
|
|
55
|
-
pip install shaped
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Rank
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
import shaped
|
|
62
|
-
|
|
63
|
-
api_key = 'your_api_key'
|
|
64
|
-
client = shaped.Client(api_key=api_key)
|
|
65
|
-
|
|
66
|
-
api_response = client.rank(
|
|
67
|
-
model_name="amazon_beauty_product_recommendations",
|
|
68
|
-
user_id="A2FRWMTWYJUK7P",
|
|
69
|
-
return_metadata=True,
|
|
70
|
-
item_ids=['B01AHSUT8M', 'B00GW7H5EY', 'B01GLA54SA', 'B0016PKWK6', 'B00PJD7KPG', 'B00BCI8OP2', 'B004FK7R02'],
|
|
71
|
-
)
|
|
72
|
-
print(api_response)
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Retrieve
|
|
76
|
-
```python
|
|
77
|
-
import shaped
|
|
78
|
-
|
|
79
|
-
api_key = 'your_api_key'
|
|
80
|
-
client = shaped.Client(api_key=api_key)
|
|
81
|
-
|
|
82
|
-
api_response = client.retrieve(
|
|
83
|
-
model_name="amazon_beauty_product_recommendations",
|
|
84
|
-
)
|
|
85
|
-
print(api_response)
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Similar Items
|
|
89
|
-
```python
|
|
90
|
-
import shaped
|
|
91
|
-
|
|
92
|
-
api_key = 'your_api_key'
|
|
93
|
-
client = shaped.Client(api_key=api_key)
|
|
94
|
-
|
|
95
|
-
api_response = client.similar_items(
|
|
96
|
-
model_name="amazon_beauty_product_recommendations",
|
|
97
|
-
item_id="B000FOI48G",
|
|
98
|
-
)
|
|
99
|
-
print(api_response)
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Similar Users
|
|
103
|
-
```python
|
|
104
|
-
import shaped
|
|
105
|
-
|
|
106
|
-
api_key = 'your_api_key'
|
|
107
|
-
client = shaped.Client(api_key=api_key)
|
|
108
|
-
|
|
109
|
-
api_response = client.similar_users(
|
|
110
|
-
model_name="amazon_beauty_product_recommendations",
|
|
111
|
-
user_id="A2FRWMTWYJUK7P",
|
|
112
|
-
)
|
|
113
|
-
print(api_response)
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Complement Items
|
|
117
|
-
```python
|
|
118
|
-
import shaped
|
|
119
|
-
|
|
120
|
-
api_key = 'your_api_key'
|
|
121
|
-
client = shaped.Client(api_key=api_key)
|
|
122
|
-
|
|
123
|
-
api_response = client.complement_items(
|
|
124
|
-
model_name="amazon_beauty_product_recommendations",
|
|
125
|
-
item_ids=['B000URXP6E', 'B0012Y0ZG2'],
|
|
126
|
-
)
|
|
127
|
-
print(api_response)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Maintainer Notes
|
|
131
|
-
|
|
132
|
-
To recreate the autogenerated code:
|
|
133
|
-
|
|
134
|
-
1. `brew install openapi-generator`
|
|
135
|
-
2. Copy across `openapi.yaml` from the `shaped-docs` repository to `~`
|
|
136
|
-
1. Change `-g python` to the language you want to generate
|
|
137
|
-
1. Change `-o python/` to the directory you want to output to
|
|
138
|
-
3. Navigate to `~` and run the command:
|
|
139
|
-
|
|
140
|
-
### Python
|
|
141
|
-
```bash
|
|
142
|
-
openapi-generator generate -g python -i openapi.yaml -o python/ -p packageName=shaped.autogen --global-property models,apis,apiDocs=false,modelDocs=false,modelTests=false,apiTests=false,supportingFiles=api_client.py:api_response.py:configuration.py:exceptions.py:rest.py:__init__.py
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Node.js
|
|
146
|
-
```bash
|
|
147
|
-
openapi-generator generate -i openapi.yaml -g javascript -o nodejs/src -c config.yaml --global-property models,apis,apiDocs=false,modelDocs=false,modelTests=false,apiTests=false,supportingFiles=ApiClient.js
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
## Testing
|
|
151
|
-
`pytest python/tests/test_rank.py --api-key 'api_key'`
|
|
152
|
-
`npm test`
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
# Shaped CLI
|
|
156
|
-
|
|
157
|
-
CLI for interactions with the Shaped API.
|
|
158
|
-
|
|
159
|
-
## Installing the Shaped CLI
|
|
160
|
-
|
|
161
|
-
```
|
|
162
|
-
pip install shaped
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## Initialize
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
shaped init --api-key <API_KEY>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## Model API
|
|
172
|
-
|
|
173
|
-
### Create Model (File)
|
|
174
|
-
|
|
175
|
-
```
|
|
176
|
-
shaped create-model --file <PATH_TO_FILE>
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Create Model (STDIN)
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
cat $(PATH_TO_FILE) | shaped create-model
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### List Models
|
|
186
|
-
|
|
187
|
-
```
|
|
188
|
-
shaped list-models
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### View Model
|
|
192
|
-
|
|
193
|
-
```
|
|
194
|
-
shaped view-model --model-name <MODEL_NAME>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
## Delete Model
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
shaped delete-model --model-name <MODEL_NAME>
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## Dataset API
|
|
204
|
-
|
|
205
|
-
### Create Dataset
|
|
206
|
-
|
|
207
|
-
```
|
|
208
|
-
shaped create-dataset --file <PATH_TO_FILE>
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### List Datasets
|
|
212
|
-
|
|
213
|
-
```
|
|
214
|
-
shaped list-datasets
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Dataset Insert
|
|
218
|
-
|
|
219
|
-
```
|
|
220
|
-
shaped dataset-insert --dataset-name <DATASET_NAME> --file <DATAFRAME_FILE> --type <FILE_TYPE>
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## Delete dataset
|
|
224
|
-
|
|
225
|
-
```
|
|
226
|
-
shaped delete-dataset --dataset-name <DATASET_NAME>
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
## Rank API
|
|
230
|
-
|
|
231
|
-
### Rank
|
|
232
|
-
|
|
233
|
-
```
|
|
234
|
-
shaped rank --model-name <MODEL_NAME> --user-id <USER_ID>
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Similar Items
|
|
238
|
-
|
|
239
|
-
```
|
|
240
|
-
shaped similar --model-name <MODEL_NAME> --item-id <ITEM_ID>
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### Similar Users
|
|
244
|
-
|
|
245
|
-
```
|
|
246
|
-
shaped similar --model-name <MODEL_NAME> --user-id <USER_ID>
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
# Development
|
|
250
|
-
|
|
251
|
-
## Installing the Shaped CLI from Test PyPI
|
|
252
|
-
|
|
253
|
-
Upon all pushes to main branch, a new version of the CLI is published to Test PyPI. To install the latest version of the CLI from Test PyPI, run the following commands:
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
conda create -n cli-dev python=3.9
|
|
257
|
-
conda activate cli-dev
|
|
258
|
-
export PACKAGE_VERSION={} # Specify the version you want to install
|
|
259
|
-
pip install --extra-index-url https://test.pypi.org/simple/ shaped-cli==$PACKAGE_VERSION
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
## Releasing a new CLI version to PyPI
|
|
263
|
-
|
|
264
|
-
To release a new version of the CLI to PyPI, open a PR changing the version of the package in `setup.py`, following [Semantic Versioning](https://semver.org) principles, e.g. `0.1.1`.
|
|
265
|
-
|
|
266
|
-
CircleCI will generate an approval prompt when this branch is merged to main, and upon approval will publish to PyPI.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|