unitysvc-services 0.2.5__tar.gz → 0.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unitysvc_services-0.2.5/src/unitysvc_services.egg-info → unitysvc_services-0.2.7}/PKG-INFO +1 -1
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/pyproject.toml +1 -1
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/base.py +123 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/listing_v1.py +26 -3
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/provider_v1.py +17 -2
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/seller_v1.py +8 -2
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/service_v1.py +8 -1
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/publisher.py +413 -137
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/query.py +5 -4
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/validator.py +79 -23
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7/src/unitysvc_services.egg-info}/PKG-INFO +1 -1
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/CONTRIBUTING.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/HISTORY.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/LICENSE +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/MANIFEST.in +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/README.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/api-reference.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/cli-reference.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/contributing.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/data-structure.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/development.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/file-schemas.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/getting-started.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/index.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/installation.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/usage.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/docs/workflows.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/setup.cfg +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/__init__.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/cli.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/format_data.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/list.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/__init__.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/populate.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/py.typed +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/scaffold.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/update.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/utils.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services.egg-info/SOURCES.txt +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services.egg-info/dependency_links.txt +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services.egg-info/entry_points.txt +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services.egg-info/requires.txt +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services.egg-info/top_level.txt +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/__init__.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/README.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider1/README.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider1/provider.toml +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider1/services/service1/code-example.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider1/services/service1/service.toml +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider1/services/service1/svcreseller.toml +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider1/terms-of-service.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider2/README.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider2/provider.json +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider2/services/service2/code-example.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider2/services/service2/service.json +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider2/services/service2/svcreseller.json +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/provider2/terms-of-service.md +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/example_data/seller.json +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/test_utils.py +0 -0
- {unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/tests/test_validator.py +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import re
|
1
2
|
from enum import StrEnum
|
2
3
|
from typing import Any
|
3
4
|
|
@@ -210,6 +211,7 @@ class ProviderStatusEnum(StrEnum):
|
|
210
211
|
"""Provider status enum."""
|
211
212
|
|
212
213
|
active = "active"
|
214
|
+
pending = "pending"
|
213
215
|
disabled = "disabled"
|
214
216
|
incomplete = "incomplete" # Provider information is incomplete
|
215
217
|
|
@@ -218,6 +220,7 @@ class SellerStatusEnum(StrEnum):
|
|
218
220
|
"""Seller status enum."""
|
219
221
|
|
220
222
|
active = "active"
|
223
|
+
pending = "pending"
|
221
224
|
disabled = "disabled"
|
222
225
|
incomplete = "incomplete" # Seller information is incomplete
|
223
226
|
|
@@ -366,3 +369,123 @@ class Pricing(BaseModel):
|
|
366
369
|
|
367
370
|
# Optional reference to upstream pricing
|
368
371
|
reference: str | None = Field(default=None, description="Reference URL to upstream pricing")
|
372
|
+
|
373
|
+
|
374
|
+
def validate_name(name: str, entity_type: str, display_name: str | None = None, *, allow_slash: bool = False) -> str:
|
375
|
+
"""
|
376
|
+
Validate that a name field uses valid identifiers.
|
377
|
+
|
378
|
+
Name format rules:
|
379
|
+
- Only letters (upper/lowercase), numbers, dots, dashes, and underscores allowed
|
380
|
+
- If allow_slash=True, slashes are also allowed for hierarchical names
|
381
|
+
- Must start and end with alphanumeric characters (not special characters)
|
382
|
+
- Cannot have consecutive slashes (when allow_slash=True)
|
383
|
+
- Cannot be empty
|
384
|
+
|
385
|
+
Args:
|
386
|
+
name: The name value to validate
|
387
|
+
entity_type: Type of entity (provider, seller, service, listing) for error messages
|
388
|
+
display_name: Optional display name to suggest a valid name from
|
389
|
+
allow_slash: Whether to allow slashes for hierarchical names (default: False)
|
390
|
+
|
391
|
+
Returns:
|
392
|
+
The validated name (unchanged if valid)
|
393
|
+
|
394
|
+
Raises:
|
395
|
+
ValueError: If the name doesn't match the required pattern
|
396
|
+
|
397
|
+
Examples:
|
398
|
+
Without slashes (providers, sellers):
|
399
|
+
- name='amazon-bedrock' or name='Amazon-Bedrock'
|
400
|
+
- name='fireworks.ai' or name='Fireworks.ai'
|
401
|
+
- name='llama-3.1' or name='Llama-3.1'
|
402
|
+
|
403
|
+
With slashes (services, listings):
|
404
|
+
- name='gpt-4' or name='GPT-4'
|
405
|
+
- name='models/gpt-4' or name='models/GPT-4'
|
406
|
+
- name='black-forest-labs/FLUX.1-dev'
|
407
|
+
- name='api/v1/completion'
|
408
|
+
"""
|
409
|
+
# Build pattern based on allow_slash parameter
|
410
|
+
if allow_slash:
|
411
|
+
# Pattern: starts with alphanumeric, can contain alphanumeric/dot/dash/underscore/slash, ends with alphanumeric
|
412
|
+
name_pattern = r"^[a-zA-Z0-9]([a-zA-Z0-9._/-]*[a-zA-Z0-9])?$"
|
413
|
+
allowed_chars = "letters, numbers, dots, dashes, underscores, and slashes"
|
414
|
+
else:
|
415
|
+
# Pattern: starts with alphanumeric, can contain alphanumeric/dot/dash/underscore, ends with alphanumeric
|
416
|
+
name_pattern = r"^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$"
|
417
|
+
allowed_chars = "letters, numbers, dots, dashes, and underscores"
|
418
|
+
|
419
|
+
# Check for consecutive slashes if slashes are allowed
|
420
|
+
if allow_slash and "//" in name:
|
421
|
+
raise ValueError(f"Invalid {entity_type} name '{name}'. Name cannot contain consecutive slashes.")
|
422
|
+
|
423
|
+
if not re.match(name_pattern, name):
|
424
|
+
# Build helpful error message
|
425
|
+
error_msg = (
|
426
|
+
f"Invalid {entity_type} name '{name}'. "
|
427
|
+
f"Name must contain only {allowed_chars}. "
|
428
|
+
f"It must start and end with an alphanumeric character.\n"
|
429
|
+
)
|
430
|
+
|
431
|
+
# Suggest a valid name based on display_name if available
|
432
|
+
if display_name:
|
433
|
+
suggested_name = suggest_valid_name(display_name, allow_slash=allow_slash)
|
434
|
+
if suggested_name and suggested_name != name:
|
435
|
+
error_msg += f" Suggestion: Set name='{suggested_name}' and display_name='{display_name}'\n"
|
436
|
+
|
437
|
+
# Add appropriate examples based on allow_slash
|
438
|
+
if allow_slash:
|
439
|
+
error_msg += (
|
440
|
+
" Examples:\n"
|
441
|
+
" - name='gpt-4' or name='GPT-4'\n"
|
442
|
+
" - name='models/gpt-4' or name='models/GPT-4'\n"
|
443
|
+
" - name='black-forest-labs/FLUX.1-dev'\n"
|
444
|
+
" - name='api/v1/completion'"
|
445
|
+
)
|
446
|
+
else:
|
447
|
+
error_msg += (
|
448
|
+
" Note: Use 'display_name' field for brand names with spaces and special characters.\n"
|
449
|
+
" Examples:\n"
|
450
|
+
" - name='amazon-bedrock' or name='Amazon-Bedrock'\n"
|
451
|
+
" - name='fireworks.ai' or name='Fireworks.ai'\n"
|
452
|
+
" - name='llama-3.1' or name='Llama-3.1'"
|
453
|
+
)
|
454
|
+
|
455
|
+
raise ValueError(error_msg)
|
456
|
+
|
457
|
+
return name
|
458
|
+
|
459
|
+
|
460
|
+
def suggest_valid_name(display_name: str, *, allow_slash: bool = False) -> str:
|
461
|
+
"""
|
462
|
+
Suggest a valid name based on a display name.
|
463
|
+
|
464
|
+
Replaces invalid characters with hyphens and ensures it follows the naming rules.
|
465
|
+
Preserves the original case.
|
466
|
+
|
467
|
+
Args:
|
468
|
+
display_name: The display name to convert
|
469
|
+
allow_slash: Whether to allow slashes for hierarchical names (default: False)
|
470
|
+
|
471
|
+
Returns:
|
472
|
+
A suggested valid name
|
473
|
+
"""
|
474
|
+
if allow_slash:
|
475
|
+
# Replace characters that aren't alphanumeric, dot, dash, underscore, or slash with hyphens
|
476
|
+
suggested = re.sub(r"[^a-zA-Z0-9._/-]+", "-", display_name)
|
477
|
+
# Remove leading/trailing special characters
|
478
|
+
suggested = suggested.strip("._/-")
|
479
|
+
# Collapse multiple consecutive dashes
|
480
|
+
suggested = re.sub(r"-+", "-", suggested)
|
481
|
+
# Remove consecutive slashes
|
482
|
+
suggested = re.sub(r"/+", "/", suggested)
|
483
|
+
else:
|
484
|
+
# Replace characters that aren't alphanumeric, dot, dash, or underscore with hyphens
|
485
|
+
suggested = re.sub(r"[^a-zA-Z0-9._-]+", "-", display_name)
|
486
|
+
# Remove leading/trailing dots, dashes, or underscores
|
487
|
+
suggested = suggested.strip("._-")
|
488
|
+
# Collapse multiple consecutive dashes
|
489
|
+
suggested = re.sub(r"-+", "-", suggested)
|
490
|
+
|
491
|
+
return suggested
|
{unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/listing_v1.py
RENAMED
@@ -1,9 +1,15 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
-
from pydantic import BaseModel, ConfigDict, Field
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
5
5
|
|
6
|
-
from unitysvc_services.models.base import
|
6
|
+
from unitysvc_services.models.base import (
|
7
|
+
AccessInterface,
|
8
|
+
Document,
|
9
|
+
ListingStatusEnum,
|
10
|
+
Pricing,
|
11
|
+
validate_name,
|
12
|
+
)
|
7
13
|
|
8
14
|
|
9
15
|
class ListingV1(BaseModel):
|
@@ -27,10 +33,19 @@ class ListingV1(BaseModel):
|
|
27
33
|
|
28
34
|
seller_name: str | None = Field(default=None, description="Name of the seller offering this service listing")
|
29
35
|
|
30
|
-
name: str = Field(
|
36
|
+
name: str | None = Field(
|
37
|
+
default=None,
|
31
38
|
max_length=255,
|
32
39
|
description="Name identifier for the service listing, default to filename",
|
33
40
|
)
|
41
|
+
|
42
|
+
# Display name for UI (human-readable listing name)
|
43
|
+
display_name: str | None = Field(
|
44
|
+
default=None,
|
45
|
+
max_length=200,
|
46
|
+
description="Human-readable listing name (e.g., 'Premium GPT-4 Access', 'Enterprise AI Services')",
|
47
|
+
)
|
48
|
+
|
34
49
|
# unique name for each provider, usually following upstream naming convention
|
35
50
|
# status of the service, public, deprecated etc
|
36
51
|
listing_status: ListingStatusEnum = Field(
|
@@ -67,3 +82,11 @@ class ListingV1(BaseModel):
|
|
67
82
|
user_parameters_ui_schema: dict[str, Any] | None = Field(
|
68
83
|
default=None, description="Dictionary of user parameters UI schema"
|
69
84
|
)
|
85
|
+
|
86
|
+
@field_validator("name")
|
87
|
+
@classmethod
|
88
|
+
def validate_name_format(cls, v: str | None) -> str | None:
|
89
|
+
"""Validate that listing name uses valid identifiers (allows slashes for hierarchical names)."""
|
90
|
+
if v is None:
|
91
|
+
return v
|
92
|
+
return validate_name(v, "listing", allow_slash=True)
|
{unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/provider_v1.py
RENAMED
@@ -1,9 +1,9 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
-
from pydantic import BaseModel, ConfigDict, EmailStr, Field, HttpUrl
|
4
|
+
from pydantic import BaseModel, ConfigDict, EmailStr, Field, HttpUrl, field_validator
|
5
5
|
|
6
|
-
from unitysvc_services.models.base import AccessInterface, Document, ProviderStatusEnum
|
6
|
+
from unitysvc_services.models.base import AccessInterface, Document, ProviderStatusEnum, validate_name
|
7
7
|
|
8
8
|
|
9
9
|
class ProviderV1(BaseModel):
|
@@ -26,6 +26,13 @@ class ProviderV1(BaseModel):
|
|
26
26
|
# name of the provider should be the same as directory name
|
27
27
|
name: str
|
28
28
|
|
29
|
+
# Display name for UI (human-readable brand name)
|
30
|
+
display_name: str | None = Field(
|
31
|
+
default=None,
|
32
|
+
max_length=200,
|
33
|
+
description="Human-readable provider name (e.g., 'Amazon Bedrock', 'Fireworks.ai')",
|
34
|
+
)
|
35
|
+
|
29
36
|
# this field is added for convenience. It will be converted to
|
30
37
|
# documents during importing.
|
31
38
|
logo: str | HttpUrl | None = None
|
@@ -57,3 +64,11 @@ class ProviderV1(BaseModel):
|
|
57
64
|
default=ProviderStatusEnum.active,
|
58
65
|
description="Provider status: active, disabled, or incomplete",
|
59
66
|
)
|
67
|
+
|
68
|
+
@field_validator("name")
|
69
|
+
@classmethod
|
70
|
+
def validate_name_format(cls, v: str) -> str:
|
71
|
+
"""Validate that provider name uses URL-safe identifiers."""
|
72
|
+
# Note: display_name is not available in the validator context for suggesting
|
73
|
+
# Display name will be shown in error if user provides it
|
74
|
+
return validate_name(v, "provider", allow_slash=False)
|
{unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/seller_v1.py
RENAMED
@@ -1,8 +1,8 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
|
3
|
-
from pydantic import BaseModel, ConfigDict, EmailStr, Field, HttpUrl
|
3
|
+
from pydantic import BaseModel, ConfigDict, EmailStr, Field, HttpUrl, field_validator
|
4
4
|
|
5
|
-
from unitysvc_services.models.base import Document, SellerStatusEnum, SellerTypeEnum
|
5
|
+
from unitysvc_services.models.base import Document, SellerStatusEnum, SellerTypeEnum, validate_name
|
6
6
|
|
7
7
|
|
8
8
|
class SellerV1(BaseModel):
|
@@ -108,3 +108,9 @@ class SellerV1(BaseModel):
|
|
108
108
|
default=False,
|
109
109
|
description="Whether the seller has been verified (KYC/business verification)",
|
110
110
|
)
|
111
|
+
|
112
|
+
@field_validator("name")
|
113
|
+
@classmethod
|
114
|
+
def validate_name_format(cls, v: str) -> str:
|
115
|
+
"""Validate that seller name uses URL-safe identifiers."""
|
116
|
+
return validate_name(v, "seller", allow_slash=False)
|
{unitysvc_services-0.2.5 → unitysvc_services-0.2.7}/src/unitysvc_services/models/service_v1.py
RENAMED
@@ -1,7 +1,7 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
-
from pydantic import BaseModel, ConfigDict, Field, HttpUrl
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, field_validator
|
5
5
|
|
6
6
|
from unitysvc_services.models.base import (
|
7
7
|
AccessInterface,
|
@@ -10,6 +10,7 @@ from unitysvc_services.models.base import (
|
|
10
10
|
ServiceTypeEnum,
|
11
11
|
TagEnum,
|
12
12
|
UpstreamStatusEnum,
|
13
|
+
validate_name,
|
13
14
|
)
|
14
15
|
|
15
16
|
|
@@ -78,3 +79,9 @@ class ServiceV1(BaseModel):
|
|
78
79
|
# a list of pricing models
|
79
80
|
#
|
80
81
|
upstream_price: Pricing | None = Field(description="List of pricing information")
|
82
|
+
|
83
|
+
@field_validator("name")
|
84
|
+
@classmethod
|
85
|
+
def validate_name_format(cls, v: str) -> str:
|
86
|
+
"""Validate that service name uses valid identifiers (allows slashes for hierarchical names)."""
|
87
|
+
return validate_name(v, "service", allow_slash=True)
|