unitysvc-services 0.1.24__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.
- unitysvc_services/__init__.py +4 -0
- unitysvc_services/api.py +421 -0
- unitysvc_services/cli.py +23 -0
- unitysvc_services/format_data.py +140 -0
- unitysvc_services/interactive_prompt.py +1132 -0
- unitysvc_services/list.py +216 -0
- unitysvc_services/models/__init__.py +71 -0
- unitysvc_services/models/base.py +1375 -0
- unitysvc_services/models/listing_data.py +118 -0
- unitysvc_services/models/listing_v1.py +56 -0
- unitysvc_services/models/provider_data.py +79 -0
- unitysvc_services/models/provider_v1.py +54 -0
- unitysvc_services/models/seller_data.py +120 -0
- unitysvc_services/models/seller_v1.py +42 -0
- unitysvc_services/models/service_data.py +114 -0
- unitysvc_services/models/service_v1.py +81 -0
- unitysvc_services/populate.py +207 -0
- unitysvc_services/publisher.py +1628 -0
- unitysvc_services/py.typed +0 -0
- unitysvc_services/query.py +688 -0
- unitysvc_services/scaffold.py +1103 -0
- unitysvc_services/schema/base.json +777 -0
- unitysvc_services/schema/listing_v1.json +1286 -0
- unitysvc_services/schema/provider_v1.json +952 -0
- unitysvc_services/schema/seller_v1.json +379 -0
- unitysvc_services/schema/service_v1.json +1306 -0
- unitysvc_services/test.py +965 -0
- unitysvc_services/unpublisher.py +505 -0
- unitysvc_services/update.py +287 -0
- unitysvc_services/utils.py +533 -0
- unitysvc_services/validator.py +731 -0
- unitysvc_services-0.1.24.dist-info/METADATA +184 -0
- unitysvc_services-0.1.24.dist-info/RECORD +37 -0
- unitysvc_services-0.1.24.dist-info/WHEEL +5 -0
- unitysvc_services-0.1.24.dist-info/entry_points.txt +3 -0
- unitysvc_services-0.1.24.dist-info/licenses/LICENSE +21 -0
- unitysvc_services-0.1.24.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
"""Query command group - query backend API for data."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from .api import UnitySvcAPI
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(help="Query backend API for data")
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ServiceDataQuery(UnitySvcAPI):
|
|
18
|
+
"""Query service data from UnitySVC backend endpoints.
|
|
19
|
+
|
|
20
|
+
Inherits HTTP methods with automatic curl fallback from UnitySvcAPI.
|
|
21
|
+
Provides async methods for querying public service data.
|
|
22
|
+
Use with async context manager for proper resource cleanup.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@app.command("sellers")
|
|
29
|
+
def query_sellers(
|
|
30
|
+
format: str = typer.Option(
|
|
31
|
+
"table",
|
|
32
|
+
"--format",
|
|
33
|
+
"-f",
|
|
34
|
+
help="Output format: table, json",
|
|
35
|
+
),
|
|
36
|
+
fields: str = typer.Option(
|
|
37
|
+
"id,name,display_name,seller_type",
|
|
38
|
+
"--fields",
|
|
39
|
+
help=(
|
|
40
|
+
"Comma-separated list of fields to display. Available fields: "
|
|
41
|
+
"id, name, display_name, seller_type, contact_email, "
|
|
42
|
+
"secondary_contact_email, homepage, description, "
|
|
43
|
+
"business_registration, tax_id, account_manager_id, "
|
|
44
|
+
"created_at, updated_at, status"
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
skip: int = typer.Option(
|
|
48
|
+
0,
|
|
49
|
+
"--skip",
|
|
50
|
+
help="Number of records to skip (for pagination)",
|
|
51
|
+
),
|
|
52
|
+
limit: int = typer.Option(
|
|
53
|
+
100,
|
|
54
|
+
"--limit",
|
|
55
|
+
help="Maximum number of records to return (default: 100)",
|
|
56
|
+
),
|
|
57
|
+
):
|
|
58
|
+
"""Query all sellers from the backend.
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
# Use default fields
|
|
62
|
+
unitysvc_services query sellers
|
|
63
|
+
|
|
64
|
+
# Show only specific fields
|
|
65
|
+
unitysvc_services query sellers --fields id,name,contact_email
|
|
66
|
+
|
|
67
|
+
# Retrieve more than 100 records
|
|
68
|
+
unitysvc_services query sellers --limit 500
|
|
69
|
+
|
|
70
|
+
# Pagination: skip first 100, get next 100
|
|
71
|
+
unitysvc_services query sellers --skip 100 --limit 100
|
|
72
|
+
|
|
73
|
+
# Show all available fields
|
|
74
|
+
unitysvc_services query sellers --fields \\
|
|
75
|
+
id,name,display_name,seller_type,contact_email,homepage,created_at,updated_at
|
|
76
|
+
"""
|
|
77
|
+
# Parse fields list
|
|
78
|
+
field_list = [f.strip() for f in fields.split(",")]
|
|
79
|
+
|
|
80
|
+
# Define allowed fields from SellerPublic model
|
|
81
|
+
allowed_fields = {
|
|
82
|
+
"id",
|
|
83
|
+
"name",
|
|
84
|
+
"display_name",
|
|
85
|
+
"seller_type",
|
|
86
|
+
"contact_email",
|
|
87
|
+
"secondary_contact_email",
|
|
88
|
+
"homepage",
|
|
89
|
+
"description",
|
|
90
|
+
"business_registration",
|
|
91
|
+
"tax_id",
|
|
92
|
+
"account_manager_id",
|
|
93
|
+
"created_at",
|
|
94
|
+
"updated_at",
|
|
95
|
+
"status",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Validate fields
|
|
99
|
+
invalid_fields = [f for f in field_list if f not in allowed_fields]
|
|
100
|
+
if invalid_fields:
|
|
101
|
+
console.print(
|
|
102
|
+
f"[red]✗[/red] Invalid field(s): {', '.join(invalid_fields)}",
|
|
103
|
+
style="bold red",
|
|
104
|
+
)
|
|
105
|
+
console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
|
106
|
+
raise typer.Exit(code=1)
|
|
107
|
+
|
|
108
|
+
async def _query_sellers_async():
|
|
109
|
+
async with ServiceDataQuery() as query:
|
|
110
|
+
sellers = await query.get("/publish/sellers", {"skip": skip, "limit": limit})
|
|
111
|
+
return sellers.get("data", sellers) if isinstance(sellers, dict) else sellers
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
sellers = asyncio.run(_query_sellers_async())
|
|
115
|
+
|
|
116
|
+
if format == "json":
|
|
117
|
+
# For JSON, filter fields if not all are requested
|
|
118
|
+
if set(field_list) != allowed_fields:
|
|
119
|
+
filtered_sellers = [{k: v for k, v in seller.items() if k in field_list} for seller in sellers]
|
|
120
|
+
console.print(json.dumps(filtered_sellers, indent=2))
|
|
121
|
+
else:
|
|
122
|
+
console.print(json.dumps(sellers, indent=2))
|
|
123
|
+
else:
|
|
124
|
+
if not sellers:
|
|
125
|
+
console.print("[yellow]No sellers found.[/yellow]")
|
|
126
|
+
else:
|
|
127
|
+
table = Table(title="Sellers")
|
|
128
|
+
|
|
129
|
+
# Define column styles
|
|
130
|
+
field_styles = {
|
|
131
|
+
"id": "cyan",
|
|
132
|
+
"name": "green",
|
|
133
|
+
"display_name": "blue",
|
|
134
|
+
"seller_type": "magenta",
|
|
135
|
+
"contact_email": "yellow",
|
|
136
|
+
"secondary_contact_email": "yellow",
|
|
137
|
+
"homepage": "blue",
|
|
138
|
+
"description": "white",
|
|
139
|
+
"business_registration": "white",
|
|
140
|
+
"tax_id": "white",
|
|
141
|
+
"account_manager_id": "cyan",
|
|
142
|
+
"created_at": "white",
|
|
143
|
+
"updated_at": "white",
|
|
144
|
+
"status": "green",
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Define column headers
|
|
148
|
+
field_headers = {
|
|
149
|
+
"id": "ID",
|
|
150
|
+
"name": "Name",
|
|
151
|
+
"display_name": "Display Name",
|
|
152
|
+
"seller_type": "Type",
|
|
153
|
+
"contact_email": "Contact Email",
|
|
154
|
+
"secondary_contact_email": "Secondary Email",
|
|
155
|
+
"homepage": "Homepage",
|
|
156
|
+
"description": "Description",
|
|
157
|
+
"business_registration": "Business Reg",
|
|
158
|
+
"tax_id": "Tax ID",
|
|
159
|
+
"account_manager_id": "Account Manager ID",
|
|
160
|
+
"created_at": "Created At",
|
|
161
|
+
"updated_at": "Updated At",
|
|
162
|
+
"status": "Status",
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Add columns based on requested fields
|
|
166
|
+
for field in field_list:
|
|
167
|
+
header = field_headers.get(field, field.title())
|
|
168
|
+
style = field_styles.get(field, "white")
|
|
169
|
+
table.add_column(header, style=style)
|
|
170
|
+
|
|
171
|
+
# Add rows
|
|
172
|
+
for seller in sellers:
|
|
173
|
+
row = []
|
|
174
|
+
for field in field_list:
|
|
175
|
+
value = seller.get(field)
|
|
176
|
+
if value is None:
|
|
177
|
+
row.append("N/A")
|
|
178
|
+
elif isinstance(value, dict | list):
|
|
179
|
+
row.append(str(value)[:50]) # Truncate complex types
|
|
180
|
+
else:
|
|
181
|
+
row.append(str(value))
|
|
182
|
+
table.add_row(*row)
|
|
183
|
+
|
|
184
|
+
console.print(table)
|
|
185
|
+
console.print(f"\n[green]Total:[/green] {len(sellers)} seller(s)")
|
|
186
|
+
except ValueError as e:
|
|
187
|
+
console.print(f"[red]✗[/red] {e}", style="bold red")
|
|
188
|
+
raise typer.Exit(code=1)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
console.print(f"[red]✗[/red] Failed to query sellers: {e}", style="bold red")
|
|
191
|
+
raise typer.Exit(code=1)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@app.command("providers")
|
|
195
|
+
def query_providers(
|
|
196
|
+
format: str = typer.Option(
|
|
197
|
+
"table",
|
|
198
|
+
"--format",
|
|
199
|
+
"-f",
|
|
200
|
+
help="Output format: table, json",
|
|
201
|
+
),
|
|
202
|
+
fields: str = typer.Option(
|
|
203
|
+
"id,name,display_name,status",
|
|
204
|
+
"--fields",
|
|
205
|
+
help=(
|
|
206
|
+
"Comma-separated list of fields to display. Available fields: "
|
|
207
|
+
"id, name, display_name, contact_email, secondary_contact_email, "
|
|
208
|
+
"homepage, description, status, created_at, updated_at"
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
skip: int = typer.Option(
|
|
212
|
+
0,
|
|
213
|
+
"--skip",
|
|
214
|
+
help="Number of records to skip (for pagination)",
|
|
215
|
+
),
|
|
216
|
+
limit: int = typer.Option(
|
|
217
|
+
100,
|
|
218
|
+
"--limit",
|
|
219
|
+
help="Maximum number of records to return (default: 100)",
|
|
220
|
+
),
|
|
221
|
+
):
|
|
222
|
+
"""Query all providers from the backend.
|
|
223
|
+
|
|
224
|
+
Examples:
|
|
225
|
+
# Use default fields
|
|
226
|
+
unitysvc_services query providers
|
|
227
|
+
|
|
228
|
+
# Retrieve more than 100 records
|
|
229
|
+
unitysvc_services query providers --limit 500
|
|
230
|
+
|
|
231
|
+
# Pagination: skip first 100, get next 100
|
|
232
|
+
unitysvc_services query providers --skip 100 --limit 100
|
|
233
|
+
|
|
234
|
+
# Show only specific fields
|
|
235
|
+
unitysvc_services query providers --fields id,name,contact_email
|
|
236
|
+
|
|
237
|
+
# Show all available fields
|
|
238
|
+
unitysvc_services query providers --fields \\
|
|
239
|
+
id,name,display_name,contact_email,homepage,status,created_at,updated_at
|
|
240
|
+
"""
|
|
241
|
+
# Parse fields list
|
|
242
|
+
field_list = [f.strip() for f in fields.split(",")]
|
|
243
|
+
|
|
244
|
+
# Define allowed fields from ProviderPublic model
|
|
245
|
+
allowed_fields = {
|
|
246
|
+
"id",
|
|
247
|
+
"name",
|
|
248
|
+
"display_name",
|
|
249
|
+
"contact_email",
|
|
250
|
+
"secondary_contact_email",
|
|
251
|
+
"homepage",
|
|
252
|
+
"description",
|
|
253
|
+
"status",
|
|
254
|
+
"created_at",
|
|
255
|
+
"updated_at",
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# Validate fields
|
|
259
|
+
invalid_fields = [f for f in field_list if f not in allowed_fields]
|
|
260
|
+
if invalid_fields:
|
|
261
|
+
console.print(
|
|
262
|
+
f"[red]✗[/red] Invalid field(s): {', '.join(invalid_fields)}",
|
|
263
|
+
style="bold red",
|
|
264
|
+
)
|
|
265
|
+
console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
|
266
|
+
raise typer.Exit(code=1)
|
|
267
|
+
|
|
268
|
+
async def _query_providers_async():
|
|
269
|
+
async with ServiceDataQuery() as query:
|
|
270
|
+
providers = await query.get("/publish/providers", {"skip": skip, "limit": limit})
|
|
271
|
+
return providers.get("data", providers) if isinstance(providers, dict) else providers
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
providers = asyncio.run(_query_providers_async())
|
|
275
|
+
|
|
276
|
+
if format == "json":
|
|
277
|
+
# For JSON, filter fields if not all are requested
|
|
278
|
+
if set(field_list) != allowed_fields:
|
|
279
|
+
filtered_providers = [{k: v for k, v in provider.items() if k in field_list} for provider in providers]
|
|
280
|
+
console.print(json.dumps(filtered_providers, indent=2))
|
|
281
|
+
else:
|
|
282
|
+
console.print(json.dumps(providers, indent=2))
|
|
283
|
+
else:
|
|
284
|
+
if not providers:
|
|
285
|
+
console.print("[yellow]No providers found.[/yellow]")
|
|
286
|
+
else:
|
|
287
|
+
table = Table(title="Providers")
|
|
288
|
+
|
|
289
|
+
# Define column styles
|
|
290
|
+
field_styles = {
|
|
291
|
+
"id": "cyan",
|
|
292
|
+
"name": "green",
|
|
293
|
+
"display_name": "blue",
|
|
294
|
+
"contact_email": "yellow",
|
|
295
|
+
"secondary_contact_email": "yellow",
|
|
296
|
+
"homepage": "blue",
|
|
297
|
+
"description": "white",
|
|
298
|
+
"status": "green",
|
|
299
|
+
"created_at": "magenta",
|
|
300
|
+
"updated_at": "magenta",
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# Define column headers
|
|
304
|
+
field_headers = {
|
|
305
|
+
"id": "ID",
|
|
306
|
+
"name": "Name",
|
|
307
|
+
"display_name": "Display Name",
|
|
308
|
+
"contact_email": "Contact Email",
|
|
309
|
+
"secondary_contact_email": "Secondary Email",
|
|
310
|
+
"homepage": "Homepage",
|
|
311
|
+
"description": "Description",
|
|
312
|
+
"status": "Status",
|
|
313
|
+
"created_at": "Created At",
|
|
314
|
+
"updated_at": "Updated At",
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
# Add columns based on requested fields
|
|
318
|
+
for field in field_list:
|
|
319
|
+
header = field_headers.get(field, field.title())
|
|
320
|
+
style = field_styles.get(field, "white")
|
|
321
|
+
table.add_column(header, style=style)
|
|
322
|
+
|
|
323
|
+
# Add rows
|
|
324
|
+
for provider in providers:
|
|
325
|
+
row = []
|
|
326
|
+
for field in field_list:
|
|
327
|
+
value = provider.get(field)
|
|
328
|
+
if value is None:
|
|
329
|
+
row.append("N/A")
|
|
330
|
+
elif isinstance(value, dict | list):
|
|
331
|
+
row.append(str(value)[:50]) # Truncate complex types
|
|
332
|
+
else:
|
|
333
|
+
row.append(str(value))
|
|
334
|
+
table.add_row(*row)
|
|
335
|
+
|
|
336
|
+
console.print(table)
|
|
337
|
+
console.print(f"\n[green]Total:[/green] {len(providers)} provider(s)")
|
|
338
|
+
except ValueError as e:
|
|
339
|
+
console.print(f"[red]✗[/red] {e}", style="bold red")
|
|
340
|
+
raise typer.Exit(code=1)
|
|
341
|
+
except Exception as e:
|
|
342
|
+
console.print(f"[red]✗[/red] Failed to query providers: {e}", style="bold red")
|
|
343
|
+
raise typer.Exit(code=1)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@app.command("offerings")
|
|
347
|
+
def query_offerings(
|
|
348
|
+
format: str = typer.Option(
|
|
349
|
+
"table",
|
|
350
|
+
"--format",
|
|
351
|
+
"-f",
|
|
352
|
+
help="Output format: table, json",
|
|
353
|
+
),
|
|
354
|
+
fields: str = typer.Option(
|
|
355
|
+
"id,name,service_type,provider_name,status",
|
|
356
|
+
"--fields",
|
|
357
|
+
help=(
|
|
358
|
+
"Comma-separated list of fields to display. Available fields: "
|
|
359
|
+
"id, provider_id, status, price, service_name, "
|
|
360
|
+
"service_type, provider_name"
|
|
361
|
+
),
|
|
362
|
+
),
|
|
363
|
+
skip: int = typer.Option(
|
|
364
|
+
0,
|
|
365
|
+
"--skip",
|
|
366
|
+
help="Number of records to skip (for pagination)",
|
|
367
|
+
),
|
|
368
|
+
limit: int = typer.Option(
|
|
369
|
+
100,
|
|
370
|
+
"--limit",
|
|
371
|
+
help="Maximum number of records to return (default: 100)",
|
|
372
|
+
),
|
|
373
|
+
provider: str | None = typer.Option(
|
|
374
|
+
None,
|
|
375
|
+
"--provider",
|
|
376
|
+
"-p",
|
|
377
|
+
help="Filter by provider name (case-insensitive partial match)",
|
|
378
|
+
),
|
|
379
|
+
service_type: str | None = typer.Option(
|
|
380
|
+
None,
|
|
381
|
+
"--service-type",
|
|
382
|
+
help="Filter by service type (exact match, e.g., llm, vectordb, embedding)",
|
|
383
|
+
),
|
|
384
|
+
name: str | None = typer.Option(
|
|
385
|
+
None,
|
|
386
|
+
"--name",
|
|
387
|
+
help="Filter by service name (case-insensitive partial match)",
|
|
388
|
+
),
|
|
389
|
+
):
|
|
390
|
+
"""Query all service offerings from UnitySVC backend.
|
|
391
|
+
|
|
392
|
+
Examples:
|
|
393
|
+
# Use default fields
|
|
394
|
+
unitysvc_services query offerings
|
|
395
|
+
|
|
396
|
+
# Show only specific fields
|
|
397
|
+
unitysvc_services query offerings --fields id,name,status
|
|
398
|
+
|
|
399
|
+
# Filter by provider name
|
|
400
|
+
unitysvc_services query offerings --provider openai
|
|
401
|
+
|
|
402
|
+
# Filter by service type
|
|
403
|
+
unitysvc_services query offerings --service-type llm
|
|
404
|
+
|
|
405
|
+
# Filter by service name
|
|
406
|
+
unitysvc_services query offerings --name llama
|
|
407
|
+
|
|
408
|
+
# Combine multiple filters
|
|
409
|
+
unitysvc_services query offerings --service-type llm --provider openai
|
|
410
|
+
|
|
411
|
+
# Retrieve more than 100 records
|
|
412
|
+
unitysvc_services query offerings --limit 500
|
|
413
|
+
|
|
414
|
+
# Pagination: skip first 100, get next 100
|
|
415
|
+
unitysvc_services query offerings --skip 100 --limit 100
|
|
416
|
+
|
|
417
|
+
# Show all available fields
|
|
418
|
+
unitysvc_services query offerings --fields \\
|
|
419
|
+
id,service_name,service_type,provider_name,status,price,provider_id
|
|
420
|
+
"""
|
|
421
|
+
# Parse fields list
|
|
422
|
+
field_list = [f.strip() for f in fields.split(",")]
|
|
423
|
+
|
|
424
|
+
# Define allowed fields from ServiceOfferingPublic model
|
|
425
|
+
allowed_fields = {
|
|
426
|
+
"id",
|
|
427
|
+
"provider_id",
|
|
428
|
+
"status",
|
|
429
|
+
"price",
|
|
430
|
+
"name",
|
|
431
|
+
"service_type",
|
|
432
|
+
"provider_name",
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
# Validate fields
|
|
436
|
+
invalid_fields = [f for f in field_list if f not in allowed_fields]
|
|
437
|
+
if invalid_fields:
|
|
438
|
+
console.print(
|
|
439
|
+
f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
|
|
440
|
+
style="bold red",
|
|
441
|
+
)
|
|
442
|
+
console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
|
443
|
+
raise typer.Exit(code=1)
|
|
444
|
+
|
|
445
|
+
async def _query_offerings_async():
|
|
446
|
+
async with ServiceDataQuery() as query:
|
|
447
|
+
params: dict[str, Any] = {"skip": skip, "limit": limit}
|
|
448
|
+
if provider:
|
|
449
|
+
params["provider_name"] = provider
|
|
450
|
+
if service_type:
|
|
451
|
+
params["service_type"] = service_type
|
|
452
|
+
if name:
|
|
453
|
+
params["name"] = name
|
|
454
|
+
|
|
455
|
+
offerings = await query.get("/publish/offerings", params)
|
|
456
|
+
return offerings.get("data", offerings) if isinstance(offerings, dict) else offerings
|
|
457
|
+
|
|
458
|
+
try:
|
|
459
|
+
offerings = asyncio.run(_query_offerings_async())
|
|
460
|
+
|
|
461
|
+
if format == "json":
|
|
462
|
+
# For JSON, filter fields if not all are requested
|
|
463
|
+
if set(field_list) != allowed_fields:
|
|
464
|
+
filtered_offerings = [{k: v for k, v in offering.items() if k in field_list} for offering in offerings]
|
|
465
|
+
console.print(json.dumps(filtered_offerings, indent=2))
|
|
466
|
+
else:
|
|
467
|
+
console.print(json.dumps(offerings, indent=2))
|
|
468
|
+
else:
|
|
469
|
+
if not offerings:
|
|
470
|
+
console.print("[yellow]No service offerings found.[/yellow]")
|
|
471
|
+
else:
|
|
472
|
+
table = Table(title="Service Offerings")
|
|
473
|
+
|
|
474
|
+
# Add columns dynamically based on selected fields
|
|
475
|
+
for field in field_list:
|
|
476
|
+
# Capitalize and format field names for display
|
|
477
|
+
column_name = field.replace("_", " ").title()
|
|
478
|
+
table.add_column(column_name)
|
|
479
|
+
|
|
480
|
+
# Add rows
|
|
481
|
+
for offering in offerings:
|
|
482
|
+
row = []
|
|
483
|
+
for field in field_list:
|
|
484
|
+
value = offering.get(field)
|
|
485
|
+
if value is None:
|
|
486
|
+
row.append("N/A")
|
|
487
|
+
elif isinstance(value, dict | list):
|
|
488
|
+
row.append(str(value)[:50]) # Truncate complex types
|
|
489
|
+
else:
|
|
490
|
+
row.append(str(value))
|
|
491
|
+
table.add_row(*row)
|
|
492
|
+
|
|
493
|
+
console.print(table)
|
|
494
|
+
console.print(f"\n[green]Total:[/green] {len(offerings)} service offering(s)")
|
|
495
|
+
except ValueError as e:
|
|
496
|
+
console.print(f"[red]✗[/red] {e}", style="bold red")
|
|
497
|
+
raise typer.Exit(code=1)
|
|
498
|
+
except Exception as e:
|
|
499
|
+
console.print(f"[red]✗[/red] Failed to query service offerings: {e}", style="bold red")
|
|
500
|
+
raise typer.Exit(code=1)
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
@app.command("listings")
|
|
504
|
+
def query_listings(
|
|
505
|
+
format: str = typer.Option(
|
|
506
|
+
"table",
|
|
507
|
+
"--format",
|
|
508
|
+
"-f",
|
|
509
|
+
help="Output format: table, json",
|
|
510
|
+
),
|
|
511
|
+
fields: str = typer.Option(
|
|
512
|
+
"id,service_name,service_type,seller_name,listing_type,status",
|
|
513
|
+
"--fields",
|
|
514
|
+
help=(
|
|
515
|
+
"Comma-separated list of fields to display. Available fields: "
|
|
516
|
+
"id, offering_id, offering_status, seller_id, status, created_at, updated_at, "
|
|
517
|
+
"parameters_schema, parameters_ui_schema, tags, service_name, "
|
|
518
|
+
"service_type, provider_name, seller_name, listing_type"
|
|
519
|
+
),
|
|
520
|
+
),
|
|
521
|
+
skip: int = typer.Option(
|
|
522
|
+
0,
|
|
523
|
+
"--skip",
|
|
524
|
+
help="Number of records to skip (for pagination)",
|
|
525
|
+
),
|
|
526
|
+
limit: int = typer.Option(
|
|
527
|
+
100,
|
|
528
|
+
"--limit",
|
|
529
|
+
help="Maximum number of records to return (default: 100)",
|
|
530
|
+
),
|
|
531
|
+
seller: str | None = typer.Option(
|
|
532
|
+
None,
|
|
533
|
+
"--seller",
|
|
534
|
+
help="Filter by seller name (case-insensitive partial match)",
|
|
535
|
+
),
|
|
536
|
+
provider: str | None = typer.Option(
|
|
537
|
+
None,
|
|
538
|
+
"--provider",
|
|
539
|
+
"-p",
|
|
540
|
+
help="Filter by provider name (case-insensitive partial match)",
|
|
541
|
+
),
|
|
542
|
+
services: str | None = typer.Option(
|
|
543
|
+
None,
|
|
544
|
+
"--services",
|
|
545
|
+
"-s",
|
|
546
|
+
help="Comma-separated list of service names (case-insensitive partial match)",
|
|
547
|
+
),
|
|
548
|
+
service_type: str | None = typer.Option(
|
|
549
|
+
None,
|
|
550
|
+
"--service-type",
|
|
551
|
+
help="Filter by service type (exact match, e.g., llm, vectordb, embedding)",
|
|
552
|
+
),
|
|
553
|
+
listing_type: str | None = typer.Option(
|
|
554
|
+
None,
|
|
555
|
+
"--listing-type",
|
|
556
|
+
help="Filter by listing type (exact match: regular, byop, self_hosted)",
|
|
557
|
+
),
|
|
558
|
+
):
|
|
559
|
+
"""Query all service listings from UnitySVC backend.
|
|
560
|
+
|
|
561
|
+
Examples:
|
|
562
|
+
# Use default fields
|
|
563
|
+
unitysvc_services query listings
|
|
564
|
+
|
|
565
|
+
# Show only specific fields
|
|
566
|
+
unitysvc_services query listings --fields id,service_name,status
|
|
567
|
+
|
|
568
|
+
# Filter by seller name
|
|
569
|
+
unitysvc_services query listings --seller chutes
|
|
570
|
+
|
|
571
|
+
# Filter by provider name
|
|
572
|
+
unitysvc_services query listings --provider openai
|
|
573
|
+
|
|
574
|
+
# Filter by service names (comma-separated)
|
|
575
|
+
unitysvc_services query listings --services "gpt-4,claude-3"
|
|
576
|
+
|
|
577
|
+
# Filter by service type
|
|
578
|
+
unitysvc_services query listings --service-type llm
|
|
579
|
+
|
|
580
|
+
# Filter by listing type
|
|
581
|
+
unitysvc_services query listings --listing-type byop
|
|
582
|
+
|
|
583
|
+
# Combine multiple filters
|
|
584
|
+
unitysvc_services query listings --service-type llm --listing-type regular
|
|
585
|
+
|
|
586
|
+
# Retrieve more than 100 records
|
|
587
|
+
unitysvc_services query listings --limit 500
|
|
588
|
+
|
|
589
|
+
# Pagination: skip first 100, get next 100
|
|
590
|
+
unitysvc_services query listings --skip 100 --limit 100
|
|
591
|
+
|
|
592
|
+
# Show all available fields
|
|
593
|
+
unitysvc_services query listings --fields \\
|
|
594
|
+
id,name,service_name,service_type,seller_name,listing_type,status,provider_name
|
|
595
|
+
"""
|
|
596
|
+
# Parse fields list
|
|
597
|
+
field_list = [f.strip() for f in fields.split(",")]
|
|
598
|
+
|
|
599
|
+
# Define allowed fields from ServiceListingPublic model
|
|
600
|
+
allowed_fields = {
|
|
601
|
+
"id",
|
|
602
|
+
"name",
|
|
603
|
+
"offering_id",
|
|
604
|
+
"offering_status",
|
|
605
|
+
"seller_id",
|
|
606
|
+
"status",
|
|
607
|
+
"created_at",
|
|
608
|
+
"updated_at",
|
|
609
|
+
"parameters_schema",
|
|
610
|
+
"parameters_ui_schema",
|
|
611
|
+
"tags",
|
|
612
|
+
"service_name",
|
|
613
|
+
"service_type",
|
|
614
|
+
"provider_name",
|
|
615
|
+
"seller_name",
|
|
616
|
+
"listing_type",
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
# Validate fields
|
|
620
|
+
invalid_fields = [f for f in field_list if f not in allowed_fields]
|
|
621
|
+
if invalid_fields:
|
|
622
|
+
console.print(
|
|
623
|
+
f"[red]Error:[/red] Invalid field(s): {', '.join(invalid_fields)}",
|
|
624
|
+
style="bold red",
|
|
625
|
+
)
|
|
626
|
+
console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
|
627
|
+
raise typer.Exit(code=1)
|
|
628
|
+
|
|
629
|
+
async def _query_listings_async():
|
|
630
|
+
async with ServiceDataQuery() as query:
|
|
631
|
+
params: dict[str, Any] = {"skip": skip, "limit": limit}
|
|
632
|
+
if seller:
|
|
633
|
+
params["seller_name"] = seller
|
|
634
|
+
if provider:
|
|
635
|
+
params["provider_name"] = provider
|
|
636
|
+
if services:
|
|
637
|
+
params["service_name"] = services
|
|
638
|
+
if service_type:
|
|
639
|
+
params["service_type"] = service_type
|
|
640
|
+
if listing_type:
|
|
641
|
+
params["listing_type"] = listing_type
|
|
642
|
+
|
|
643
|
+
listings = await query.get("/publish/listings", params)
|
|
644
|
+
return listings.get("data", listings) if isinstance(listings, dict) else listings
|
|
645
|
+
|
|
646
|
+
try:
|
|
647
|
+
listings = asyncio.run(_query_listings_async())
|
|
648
|
+
|
|
649
|
+
if format == "json":
|
|
650
|
+
# For JSON, filter fields if not all are requested
|
|
651
|
+
if set(field_list) != allowed_fields:
|
|
652
|
+
filtered_listings = [{k: v for k, v in listing.items() if k in field_list} for listing in listings]
|
|
653
|
+
console.print(json.dumps(filtered_listings, indent=2))
|
|
654
|
+
else:
|
|
655
|
+
console.print(json.dumps(listings, indent=2))
|
|
656
|
+
else:
|
|
657
|
+
if not listings:
|
|
658
|
+
console.print("[yellow]No service listings found.[/yellow]")
|
|
659
|
+
else:
|
|
660
|
+
table = Table(title="Service Listings")
|
|
661
|
+
|
|
662
|
+
# Add columns dynamically based on selected fields
|
|
663
|
+
for field in field_list:
|
|
664
|
+
# Capitalize and format field names for display
|
|
665
|
+
column_name = field.replace("_", " ").title()
|
|
666
|
+
table.add_column(column_name)
|
|
667
|
+
|
|
668
|
+
# Add rows
|
|
669
|
+
for listing in listings:
|
|
670
|
+
row = []
|
|
671
|
+
for field in field_list:
|
|
672
|
+
value = listing.get(field)
|
|
673
|
+
if value is None:
|
|
674
|
+
row.append("N/A")
|
|
675
|
+
elif isinstance(value, dict | list):
|
|
676
|
+
row.append(str(value)[:50]) # Truncate complex types
|
|
677
|
+
else:
|
|
678
|
+
row.append(str(value))
|
|
679
|
+
table.add_row(*row)
|
|
680
|
+
|
|
681
|
+
console.print(table)
|
|
682
|
+
console.print(f"\n[green]Total:[/green] {len(listings)} service listing(s)")
|
|
683
|
+
except ValueError as e:
|
|
684
|
+
console.print(f"[red]✗[/red] {e}", style="bold red")
|
|
685
|
+
raise typer.Exit(code=1)
|
|
686
|
+
except Exception as e:
|
|
687
|
+
console.print(f"[red]✗[/red] Failed to query service listings: {e}", style="bold red")
|
|
688
|
+
raise typer.Exit(code=1)
|