unitysvc-services 0.1.4__py3-none-any.whl → 0.1.5__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/api.py +58 -15
- unitysvc_services/cli.py +2 -1
- unitysvc_services/models/base.py +12 -0
- unitysvc_services/populate.py +18 -0
- unitysvc_services/publisher.py +329 -199
- unitysvc_services/query.py +310 -302
- unitysvc_services/test.py +769 -0
- unitysvc_services/utils.py +53 -0
- unitysvc_services/validator.py +19 -7
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/METADATA +40 -33
- unitysvc_services-0.1.5.dist-info/RECORD +26 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/entry_points.txt +1 -0
- unitysvc_services-0.1.4.dist-info/RECORD +0 -25
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/WHEEL +0 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {unitysvc_services-0.1.4.dist-info → unitysvc_services-0.1.5.dist-info}/top_level.txt +0 -0
unitysvc_services/query.py
CHANGED
@@ -18,101 +18,11 @@ class ServiceDataQuery(UnitySvcAPI):
|
|
18
18
|
"""Query service data from UnitySVC backend endpoints.
|
19
19
|
|
20
20
|
Inherits HTTP methods with automatic curl fallback from UnitySvcAPI.
|
21
|
-
Provides
|
22
|
-
|
23
|
-
Provides sync wrapper methods for CLI usage that wrap async base class methods.
|
21
|
+
Provides async methods for querying public service data.
|
22
|
+
Use with async context manager for proper resource cleanup.
|
24
23
|
"""
|
25
24
|
|
26
|
-
|
27
|
-
"""List all service offerings from the backend (sync wrapper).
|
28
|
-
|
29
|
-
Args:
|
30
|
-
skip: Number of records to skip (for pagination)
|
31
|
-
limit: Maximum number of records to return
|
32
|
-
"""
|
33
|
-
result: dict[str, Any] = asyncio.run(super().get("/publish/offerings", {"skip": skip, "limit": limit}))
|
34
|
-
return result.get("data", result) if isinstance(result, dict) else result
|
35
|
-
|
36
|
-
def list_service_listings(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
|
37
|
-
"""List all service listings from the backend (sync wrapper).
|
38
|
-
|
39
|
-
Args:
|
40
|
-
skip: Number of records to skip (for pagination)
|
41
|
-
limit: Maximum number of records to return
|
42
|
-
"""
|
43
|
-
result: dict[str, Any] = asyncio.run(super().get("/publish/listings", {"skip": skip, "limit": limit}))
|
44
|
-
return result.get("data", result) if isinstance(result, dict) else result
|
45
|
-
|
46
|
-
def list_providers(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
|
47
|
-
"""List all providers from the backend (sync wrapper).
|
48
|
-
|
49
|
-
Args:
|
50
|
-
skip: Number of records to skip (for pagination)
|
51
|
-
limit: Maximum number of records to return
|
52
|
-
"""
|
53
|
-
result: dict[str, Any] = asyncio.run(super().get("/publish/providers", {"skip": skip, "limit": limit}))
|
54
|
-
return result.get("data", result) if isinstance(result, dict) else result
|
55
|
-
|
56
|
-
def list_sellers(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
|
57
|
-
"""List all sellers from the backend (sync wrapper).
|
58
|
-
|
59
|
-
Args:
|
60
|
-
skip: Number of records to skip (for pagination)
|
61
|
-
limit: Maximum number of records to return
|
62
|
-
"""
|
63
|
-
result: dict[str, Any] = asyncio.run(super().get("/publish/sellers", {"skip": skip, "limit": limit}))
|
64
|
-
return result.get("data", result) if isinstance(result, dict) else result
|
65
|
-
|
66
|
-
def get(self, endpoint: str, params: dict[str, Any] | None = None) -> dict[str, Any]: # type: ignore[override]
|
67
|
-
"""Sync wrapper for base class async get() method.
|
68
|
-
|
69
|
-
Args:
|
70
|
-
endpoint: API endpoint path
|
71
|
-
params: Query parameters
|
72
|
-
|
73
|
-
Returns:
|
74
|
-
JSON response as dictionary
|
75
|
-
"""
|
76
|
-
return asyncio.run(super().get(endpoint, params))
|
77
|
-
|
78
|
-
def post( # type: ignore[override]
|
79
|
-
self, endpoint: str, json_data: dict[str, Any] | None = None, params: dict[str, Any] | None = None
|
80
|
-
) -> dict[str, Any]:
|
81
|
-
"""Sync wrapper for base class async post() method.
|
82
|
-
|
83
|
-
Args:
|
84
|
-
endpoint: API endpoint path
|
85
|
-
json_data: JSON body data
|
86
|
-
params: Query parameters
|
87
|
-
|
88
|
-
Returns:
|
89
|
-
JSON response as dictionary
|
90
|
-
"""
|
91
|
-
return asyncio.run(super().post(endpoint, json_data, params))
|
92
|
-
|
93
|
-
def check_task(self, task_id: str, poll_interval: float = 2.0, timeout: float = 300.0) -> dict[str, Any]: # type: ignore[override]
|
94
|
-
"""Sync wrapper for base class async check_task() method.
|
95
|
-
|
96
|
-
Args:
|
97
|
-
task_id: Celery task ID to poll
|
98
|
-
poll_interval: Seconds between status checks
|
99
|
-
timeout: Maximum seconds to wait
|
100
|
-
|
101
|
-
Returns:
|
102
|
-
Task result dictionary
|
103
|
-
|
104
|
-
Raises:
|
105
|
-
ValueError: If task fails or times out
|
106
|
-
"""
|
107
|
-
return asyncio.run(super().check_task(task_id, poll_interval, timeout))
|
108
|
-
|
109
|
-
def __enter__(self):
|
110
|
-
"""Sync context manager entry for CLI usage."""
|
111
|
-
return self
|
112
|
-
|
113
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
114
|
-
"""Sync context manager exit for CLI usage."""
|
115
|
-
asyncio.run(self.aclose())
|
25
|
+
pass
|
116
26
|
|
117
27
|
|
118
28
|
@app.command("sellers")
|
@@ -195,80 +105,84 @@ def query_sellers(
|
|
195
105
|
console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
196
106
|
raise typer.Exit(code=1)
|
197
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
|
+
|
198
113
|
try:
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
if
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
console.print(json.dumps(filtered_sellers, indent=2))
|
207
|
-
else:
|
208
|
-
console.print(json.dumps(sellers, indent=2))
|
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))
|
209
121
|
else:
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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 = []
|
252
174
|
for field in field_list:
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
elif isinstance(value, dict | list):
|
265
|
-
row.append(str(value)[:50]) # Truncate complex types
|
266
|
-
else:
|
267
|
-
row.append(str(value))
|
268
|
-
table.add_row(*row)
|
269
|
-
|
270
|
-
console.print(table)
|
271
|
-
console.print(f"\n[green]Total:[/green] {len(sellers)} seller(s)")
|
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)")
|
272
186
|
except ValueError as e:
|
273
187
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
274
188
|
raise typer.Exit(code=1)
|
@@ -351,74 +265,76 @@ def query_providers(
|
|
351
265
|
console.print(f"[yellow]Allowed fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
352
266
|
raise typer.Exit(code=1)
|
353
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
|
+
|
354
273
|
try:
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
if
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
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]")
|
367
286
|
else:
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
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 = []
|
402
326
|
for field in field_list:
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
elif isinstance(value, dict | list):
|
415
|
-
row.append(str(value)[:50]) # Truncate complex types
|
416
|
-
else:
|
417
|
-
row.append(str(value))
|
418
|
-
table.add_row(*row)
|
419
|
-
|
420
|
-
console.print(table)
|
421
|
-
console.print(f"\n[green]Total:[/green] {len(providers)} provider(s)")
|
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)")
|
422
338
|
except ValueError as e:
|
423
339
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
424
340
|
raise typer.Exit(code=1)
|
@@ -440,7 +356,7 @@ def query_offerings(
|
|
440
356
|
"--fields",
|
441
357
|
help=(
|
442
358
|
"Comma-separated list of fields to display. Available fields: "
|
443
|
-
"id,
|
359
|
+
"id, provider_id, status, price, service_name, "
|
444
360
|
"service_type, provider_name"
|
445
361
|
),
|
446
362
|
),
|
@@ -454,6 +370,21 @@ def query_offerings(
|
|
454
370
|
"--limit",
|
455
371
|
help="Maximum number of records to return (default: 100)",
|
456
372
|
),
|
373
|
+
provider_name: str | None = typer.Option(
|
374
|
+
None,
|
375
|
+
"--provider-name",
|
376
|
+
help="Filter by provider name (case-insensitive partial match)",
|
377
|
+
),
|
378
|
+
service_type: str | None = typer.Option(
|
379
|
+
None,
|
380
|
+
"--service-type",
|
381
|
+
help="Filter by service type (exact match, e.g., llm, vectordb, embedding)",
|
382
|
+
),
|
383
|
+
name: str | None = typer.Option(
|
384
|
+
None,
|
385
|
+
"--name",
|
386
|
+
help="Filter by service name (case-insensitive partial match)",
|
387
|
+
),
|
457
388
|
):
|
458
389
|
"""Query all service offerings from UnitySVC backend.
|
459
390
|
|
@@ -464,6 +395,18 @@ def query_offerings(
|
|
464
395
|
# Show only specific fields
|
465
396
|
unitysvc_services query offerings --fields id,name,status
|
466
397
|
|
398
|
+
# Filter by provider name
|
399
|
+
unitysvc_services query offerings --provider-name openai
|
400
|
+
|
401
|
+
# Filter by service type
|
402
|
+
unitysvc_services query offerings --service-type llm
|
403
|
+
|
404
|
+
# Filter by service name
|
405
|
+
unitysvc_services query offerings --name llama
|
406
|
+
|
407
|
+
# Combine multiple filters
|
408
|
+
unitysvc_services query offerings --service-type llm --provider-name openai
|
409
|
+
|
467
410
|
# Retrieve more than 100 records
|
468
411
|
unitysvc_services query offerings --limit 500
|
469
412
|
|
@@ -472,7 +415,7 @@ def query_offerings(
|
|
472
415
|
|
473
416
|
# Show all available fields
|
474
417
|
unitysvc_services query offerings --fields \\
|
475
|
-
id,service_name,service_type,provider_name,status,price,
|
418
|
+
id,service_name,service_type,provider_name,status,price,provider_id
|
476
419
|
"""
|
477
420
|
# Parse fields list
|
478
421
|
field_list = [f.strip() for f in fields.split(",")]
|
@@ -480,7 +423,6 @@ def query_offerings(
|
|
480
423
|
# Define allowed fields from ServiceOfferingPublic model
|
481
424
|
allowed_fields = {
|
482
425
|
"id",
|
483
|
-
"definition_id",
|
484
426
|
"provider_id",
|
485
427
|
"status",
|
486
428
|
"price",
|
@@ -499,46 +441,56 @@ def query_offerings(
|
|
499
441
|
console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
500
442
|
raise typer.Exit(code=1)
|
501
443
|
|
444
|
+
async def _query_offerings_async():
|
445
|
+
async with ServiceDataQuery() as query:
|
446
|
+
params: dict[str, Any] = {"skip": skip, "limit": limit}
|
447
|
+
if provider_name:
|
448
|
+
params["provider_name"] = provider_name
|
449
|
+
if service_type:
|
450
|
+
params["service_type"] = service_type
|
451
|
+
if name:
|
452
|
+
params["name"] = name
|
453
|
+
|
454
|
+
offerings = await query.get("/publish/offerings", params)
|
455
|
+
return offerings.get("data", offerings) if isinstance(offerings, dict) else offerings
|
456
|
+
|
502
457
|
try:
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
if
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
{k: v for k, v in offering.items() if k in field_list} for offering in offerings
|
511
|
-
]
|
512
|
-
console.print(json.dumps(filtered_offerings, indent=2))
|
513
|
-
else:
|
514
|
-
console.print(json.dumps(offerings, indent=2))
|
458
|
+
offerings = asyncio.run(_query_offerings_async())
|
459
|
+
|
460
|
+
if format == "json":
|
461
|
+
# For JSON, filter fields if not all are requested
|
462
|
+
if set(field_list) != allowed_fields:
|
463
|
+
filtered_offerings = [{k: v for k, v in offering.items() if k in field_list} for offering in offerings]
|
464
|
+
console.print(json.dumps(filtered_offerings, indent=2))
|
515
465
|
else:
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
466
|
+
console.print(json.dumps(offerings, indent=2))
|
467
|
+
else:
|
468
|
+
if not offerings:
|
469
|
+
console.print("[yellow]No service offerings found.[/yellow]")
|
470
|
+
else:
|
471
|
+
table = Table(title="Service Offerings")
|
472
|
+
|
473
|
+
# Add columns dynamically based on selected fields
|
474
|
+
for field in field_list:
|
475
|
+
# Capitalize and format field names for display
|
476
|
+
column_name = field.replace("_", " ").title()
|
477
|
+
table.add_column(column_name)
|
520
478
|
|
521
|
-
|
479
|
+
# Add rows
|
480
|
+
for offering in offerings:
|
481
|
+
row = []
|
522
482
|
for field in field_list:
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
elif isinstance(value, dict | list):
|
535
|
-
row.append(str(value)[:50]) # Truncate complex types
|
536
|
-
else:
|
537
|
-
row.append(str(value))
|
538
|
-
table.add_row(*row)
|
539
|
-
|
540
|
-
console.print(table)
|
541
|
-
console.print(f"\n[green]Total:[/green] {len(offerings)} service offering(s)")
|
483
|
+
value = offering.get(field)
|
484
|
+
if value is None:
|
485
|
+
row.append("N/A")
|
486
|
+
elif isinstance(value, dict | list):
|
487
|
+
row.append(str(value)[:50]) # Truncate complex types
|
488
|
+
else:
|
489
|
+
row.append(str(value))
|
490
|
+
table.add_row(*row)
|
491
|
+
|
492
|
+
console.print(table)
|
493
|
+
console.print(f"\n[green]Total:[/green] {len(offerings)} service offering(s)")
|
542
494
|
except ValueError as e:
|
543
495
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
544
496
|
raise typer.Exit(code=1)
|
@@ -575,6 +527,31 @@ def query_listings(
|
|
575
527
|
"--limit",
|
576
528
|
help="Maximum number of records to return (default: 100)",
|
577
529
|
),
|
530
|
+
seller_name: str | None = typer.Option(
|
531
|
+
None,
|
532
|
+
"--seller-name",
|
533
|
+
help="Filter by seller name (case-insensitive partial match)",
|
534
|
+
),
|
535
|
+
provider_name: str | None = typer.Option(
|
536
|
+
None,
|
537
|
+
"--provider-name",
|
538
|
+
help="Filter by provider name (case-insensitive partial match)",
|
539
|
+
),
|
540
|
+
service_name: str | None = typer.Option(
|
541
|
+
None,
|
542
|
+
"--service-name",
|
543
|
+
help="Filter by service name (case-insensitive partial match)",
|
544
|
+
),
|
545
|
+
service_type: str | None = typer.Option(
|
546
|
+
None,
|
547
|
+
"--service-type",
|
548
|
+
help="Filter by service type (exact match, e.g., llm, vectordb, embedding)",
|
549
|
+
),
|
550
|
+
listing_type: str | None = typer.Option(
|
551
|
+
None,
|
552
|
+
"--listing-type",
|
553
|
+
help="Filter by listing type (exact match: regular, byop, self_hosted)",
|
554
|
+
),
|
578
555
|
):
|
579
556
|
"""Query all service listings from UnitySVC backend.
|
580
557
|
|
@@ -585,6 +562,21 @@ def query_listings(
|
|
585
562
|
# Show only specific fields
|
586
563
|
unitysvc_services query listings --fields id,service_name,status
|
587
564
|
|
565
|
+
# Filter by seller name
|
566
|
+
unitysvc_services query listings --seller-name chutes
|
567
|
+
|
568
|
+
# Filter by provider name
|
569
|
+
unitysvc_services query listings --provider-name openai
|
570
|
+
|
571
|
+
# Filter by service type
|
572
|
+
unitysvc_services query listings --service-type llm
|
573
|
+
|
574
|
+
# Filter by listing type
|
575
|
+
unitysvc_services query listings --listing-type byop
|
576
|
+
|
577
|
+
# Combine multiple filters
|
578
|
+
unitysvc_services query listings --service-type llm --listing-type regular
|
579
|
+
|
588
580
|
# Retrieve more than 100 records
|
589
581
|
unitysvc_services query listings --limit 500
|
590
582
|
|
@@ -628,44 +620,60 @@ def query_listings(
|
|
628
620
|
console.print(f"[yellow]Available fields:[/yellow] {', '.join(sorted(allowed_fields))}")
|
629
621
|
raise typer.Exit(code=1)
|
630
622
|
|
623
|
+
async def _query_listings_async():
|
624
|
+
async with ServiceDataQuery() as query:
|
625
|
+
params: dict[str, Any] = {"skip": skip, "limit": limit}
|
626
|
+
if seller_name:
|
627
|
+
params["seller_name"] = seller_name
|
628
|
+
if provider_name:
|
629
|
+
params["provider_name"] = provider_name
|
630
|
+
if service_name:
|
631
|
+
params["service_name"] = service_name
|
632
|
+
if service_type:
|
633
|
+
params["service_type"] = service_type
|
634
|
+
if listing_type:
|
635
|
+
params["listing_type"] = listing_type
|
636
|
+
|
637
|
+
listings = await query.get("/publish/listings", params)
|
638
|
+
return listings.get("data", listings) if isinstance(listings, dict) else listings
|
639
|
+
|
631
640
|
try:
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
if
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
console.print(json.dumps(filtered_listings, indent=2))
|
640
|
-
else:
|
641
|
-
console.print(json.dumps(listings, indent=2))
|
641
|
+
listings = asyncio.run(_query_listings_async())
|
642
|
+
|
643
|
+
if format == "json":
|
644
|
+
# For JSON, filter fields if not all are requested
|
645
|
+
if set(field_list) != allowed_fields:
|
646
|
+
filtered_listings = [{k: v for k, v in listing.items() if k in field_list} for listing in listings]
|
647
|
+
console.print(json.dumps(filtered_listings, indent=2))
|
642
648
|
else:
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
649
|
+
console.print(json.dumps(listings, indent=2))
|
650
|
+
else:
|
651
|
+
if not listings:
|
652
|
+
console.print("[yellow]No service listings found.[/yellow]")
|
653
|
+
else:
|
654
|
+
table = Table(title="Service Listings")
|
655
|
+
|
656
|
+
# Add columns dynamically based on selected fields
|
657
|
+
for field in field_list:
|
658
|
+
# Capitalize and format field names for display
|
659
|
+
column_name = field.replace("_", " ").title()
|
660
|
+
table.add_column(column_name)
|
647
661
|
|
648
|
-
|
662
|
+
# Add rows
|
663
|
+
for listing in listings:
|
664
|
+
row = []
|
649
665
|
for field in field_list:
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
elif isinstance(value, dict | list):
|
662
|
-
row.append(str(value)[:50]) # Truncate complex types
|
663
|
-
else:
|
664
|
-
row.append(str(value))
|
665
|
-
table.add_row(*row)
|
666
|
-
|
667
|
-
console.print(table)
|
668
|
-
console.print(f"\n[green]Total:[/green] {len(listings)} service listing(s)")
|
666
|
+
value = listing.get(field)
|
667
|
+
if value is None:
|
668
|
+
row.append("N/A")
|
669
|
+
elif isinstance(value, dict | list):
|
670
|
+
row.append(str(value)[:50]) # Truncate complex types
|
671
|
+
else:
|
672
|
+
row.append(str(value))
|
673
|
+
table.add_row(*row)
|
674
|
+
|
675
|
+
console.print(table)
|
676
|
+
console.print(f"\n[green]Total:[/green] {len(listings)} service listing(s)")
|
669
677
|
except ValueError as e:
|
670
678
|
console.print(f"[red]✗[/red] {e}", style="bold red")
|
671
679
|
raise typer.Exit(code=1)
|