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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Base data models for service listings.
|
|
2
|
+
|
|
3
|
+
This module defines `ServiceListingData`, a base model containing the core fields
|
|
4
|
+
for service listing data that is shared between:
|
|
5
|
+
- unitysvc-services (CLI): Used for file-based listing definitions
|
|
6
|
+
- unitysvc (backend): Used for API payloads and database operations
|
|
7
|
+
|
|
8
|
+
The `ListingV1` model extends this with file-specific fields like `schema_version`
|
|
9
|
+
and `time_created` for data file validation.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, Field
|
|
15
|
+
|
|
16
|
+
from .base import CurrencyEnum, ListingStatusEnum
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ServiceListingData(BaseModel):
|
|
20
|
+
"""
|
|
21
|
+
Base data structure for service listing information.
|
|
22
|
+
|
|
23
|
+
This model contains the core fields needed to describe a service listing,
|
|
24
|
+
without file-specific validation fields. It serves as:
|
|
25
|
+
|
|
26
|
+
1. The base class for `ListingV1` in unitysvc-services (with additional
|
|
27
|
+
schema_version and time_created fields for file validation)
|
|
28
|
+
|
|
29
|
+
2. The data structure imported by unitysvc backend for:
|
|
30
|
+
- API payload validation
|
|
31
|
+
- Database comparison logic in find_and_compare_service_listing()
|
|
32
|
+
- Publish operations from CLI
|
|
33
|
+
|
|
34
|
+
Key characteristics:
|
|
35
|
+
- Uses string identifiers (service_name, provider_name, seller_name)
|
|
36
|
+
that get resolved to database IDs by the backend
|
|
37
|
+
- Contains all user-provided data without system-generated IDs
|
|
38
|
+
- Does not include permission/audit fields (handled by backend CRUD layer)
|
|
39
|
+
- Uses dict types for nested structures to maintain flexibility between
|
|
40
|
+
file definitions and database operations
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# Reference to service offering - required for backend resolution
|
|
44
|
+
service_name: str | None = Field(
|
|
45
|
+
default=None,
|
|
46
|
+
description=(
|
|
47
|
+
"Name of the service (ServiceV1.name), optional if only one service is defined under the same directory."
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
service_version: str | None = Field(
|
|
51
|
+
default=None,
|
|
52
|
+
description="Version of the service offering",
|
|
53
|
+
)
|
|
54
|
+
provider_name: str | None = Field(
|
|
55
|
+
default=None,
|
|
56
|
+
description="Provider name (resolved from directory structure if not specified)",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Seller info
|
|
60
|
+
seller_name: str | None = Field(
|
|
61
|
+
default=None,
|
|
62
|
+
description="Name of the seller offering this service listing",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Listing identification
|
|
66
|
+
name: str | None = Field(
|
|
67
|
+
default=None,
|
|
68
|
+
max_length=255,
|
|
69
|
+
description="Name identifier for the service listing (defaults to 'default' if not provided)",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Display name for UI
|
|
73
|
+
display_name: str | None = Field(
|
|
74
|
+
default=None,
|
|
75
|
+
max_length=200,
|
|
76
|
+
description="Human-readable listing name (e.g., 'Premium GPT-4 Access', 'Enterprise AI Services')",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Status - seller-accessible statuses
|
|
80
|
+
listing_status: ListingStatusEnum = Field(
|
|
81
|
+
default=ListingStatusEnum.draft,
|
|
82
|
+
description="Listing status: draft (skip publish), ready (ready for admin review), or deprecated (retired)",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Customer pricing
|
|
86
|
+
customer_price: dict[str, Any] | None = Field(
|
|
87
|
+
default=None,
|
|
88
|
+
description="Customer pricing: What the customer pays for each unit of service usage",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Currency for customer_price
|
|
92
|
+
currency: CurrencyEnum = Field(
|
|
93
|
+
default=CurrencyEnum.USD,
|
|
94
|
+
description="Currency for customer_price (indexed for filtering)",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Access interfaces
|
|
98
|
+
user_access_interfaces: list[dict[str, Any]] | None = Field(
|
|
99
|
+
default=None,
|
|
100
|
+
description="List of user access interfaces for the listing",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Documents
|
|
104
|
+
documents: list[dict[str, Any]] | None = Field(
|
|
105
|
+
default=None,
|
|
106
|
+
description="List of documents associated with the listing (e.g., service level agreements)",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# User parameters
|
|
110
|
+
user_parameters_schema: dict[str, Any] | None = Field(
|
|
111
|
+
default=None,
|
|
112
|
+
description="JSON Schema for user parameters",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
user_parameters_ui_schema: dict[str, Any] | None = Field(
|
|
116
|
+
default=None,
|
|
117
|
+
description="UI schema for user parameters form rendering",
|
|
118
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pydantic import ConfigDict, Field, field_validator
|
|
4
|
+
|
|
5
|
+
from .base import (
|
|
6
|
+
AccessInterface,
|
|
7
|
+
Document,
|
|
8
|
+
Pricing,
|
|
9
|
+
validate_name,
|
|
10
|
+
)
|
|
11
|
+
from .listing_data import ServiceListingData
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ListingV1(ServiceListingData):
|
|
15
|
+
"""
|
|
16
|
+
Service listing model for file-based definitions (listing_v1 schema).
|
|
17
|
+
|
|
18
|
+
Extends ServiceListingData with:
|
|
19
|
+
- schema_version: Schema identifier for file validation
|
|
20
|
+
- time_created: Timestamp for file creation
|
|
21
|
+
- Typed models (AccessInterface, Document, Pricing) instead of dicts
|
|
22
|
+
- Field validators for name format
|
|
23
|
+
|
|
24
|
+
This model is used for validating listing.json/listing.toml files
|
|
25
|
+
created by the CLI tool.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
model_config = ConfigDict(extra="forbid")
|
|
29
|
+
|
|
30
|
+
# File-specific fields for validation
|
|
31
|
+
schema_version: str = Field(default="listing_v1", description="Schema identifier", alias="schema")
|
|
32
|
+
time_created: datetime
|
|
33
|
+
|
|
34
|
+
# Override with typed models instead of dicts for file validation
|
|
35
|
+
# (listing_status, user_parameters_schema, user_parameters_ui_schema are inherited from ServiceListingData)
|
|
36
|
+
user_access_interfaces: list[AccessInterface] = Field( # type: ignore[assignment]
|
|
37
|
+
description="List of user access interfaces for the listing"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
customer_price: Pricing | None = Field( # type: ignore[assignment]
|
|
41
|
+
default=None,
|
|
42
|
+
description="Customer pricing information",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
documents: list[Document] | None = Field( # type: ignore[assignment]
|
|
46
|
+
default=None,
|
|
47
|
+
description="List of documents associated with the listing (e.g. service level agreements)",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@field_validator("name")
|
|
51
|
+
@classmethod
|
|
52
|
+
def validate_name_format(cls, v: str | None) -> str | None:
|
|
53
|
+
"""Validate that listing name uses valid identifiers (allows slashes for hierarchical names)."""
|
|
54
|
+
if v is None:
|
|
55
|
+
return v
|
|
56
|
+
return validate_name(v, "listing", allow_slash=True)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Base data model for providers.
|
|
2
|
+
|
|
3
|
+
This module defines `ProviderData`, a base model containing the core fields
|
|
4
|
+
for provider data that is shared between:
|
|
5
|
+
- unitysvc-services (CLI): Used for file-based provider definitions
|
|
6
|
+
- unitysvc (backend): Used for API payloads and database operations
|
|
7
|
+
|
|
8
|
+
The `ProviderV1` model extends this with file-specific fields like `schema_version`
|
|
9
|
+
and `time_created` for data file validation.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, EmailStr, Field, HttpUrl
|
|
15
|
+
|
|
16
|
+
from .base import ProviderStatusEnum
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ProviderData(BaseModel):
|
|
20
|
+
"""
|
|
21
|
+
Base data structure for provider information.
|
|
22
|
+
|
|
23
|
+
This model contains the core fields needed to describe a provider,
|
|
24
|
+
without file-specific validation fields. It serves as:
|
|
25
|
+
|
|
26
|
+
1. The base class for `ProviderV1` in unitysvc-services (with additional
|
|
27
|
+
schema_version, time_created, and services_populator fields for file validation)
|
|
28
|
+
|
|
29
|
+
2. The data structure imported by unitysvc backend for:
|
|
30
|
+
- API payload validation
|
|
31
|
+
- Database comparison logic in find_and_compare_provider()
|
|
32
|
+
- Publish operations from CLI
|
|
33
|
+
|
|
34
|
+
Key characteristics:
|
|
35
|
+
- Uses string identifiers that match database requirements
|
|
36
|
+
- Contains all user-provided data without system-generated IDs
|
|
37
|
+
- Does not include permission/audit fields (handled by backend CRUD layer)
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Provider identification
|
|
41
|
+
name: str = Field(
|
|
42
|
+
description="Unique provider identifier (URL-friendly, e.g., 'fireworks', 'anthropic')",
|
|
43
|
+
min_length=2,
|
|
44
|
+
max_length=100,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
display_name: str | None = Field(
|
|
48
|
+
default=None,
|
|
49
|
+
max_length=200,
|
|
50
|
+
description="Human-readable provider name (e.g., 'Fireworks AI', 'Anthropic')",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Contact information
|
|
54
|
+
contact_email: EmailStr = Field(description="Primary contact email for the provider")
|
|
55
|
+
|
|
56
|
+
secondary_contact_email: EmailStr | None = Field(
|
|
57
|
+
default=None,
|
|
58
|
+
description="Secondary contact email",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
homepage: HttpUrl = Field(description="Provider's homepage URL")
|
|
62
|
+
|
|
63
|
+
# Provider information
|
|
64
|
+
description: str | None = Field(
|
|
65
|
+
default=None,
|
|
66
|
+
description="Brief description of the provider",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Status
|
|
70
|
+
status: ProviderStatusEnum = Field(
|
|
71
|
+
default=ProviderStatusEnum.active,
|
|
72
|
+
description="Provider status: active, disabled, or draft (skip publish)",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Documents (as dicts for flexibility)
|
|
76
|
+
documents: list[dict[str, Any]] | None = Field(
|
|
77
|
+
default=None,
|
|
78
|
+
description="List of documents associated with the provider",
|
|
79
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pydantic import ConfigDict, Field, HttpUrl, field_validator
|
|
5
|
+
|
|
6
|
+
from .base import AccessInterface, Document, validate_name
|
|
7
|
+
from .provider_data import ProviderData
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ProviderV1(ProviderData):
|
|
11
|
+
"""
|
|
12
|
+
Provider information for service providers (provider_v1 schema).
|
|
13
|
+
|
|
14
|
+
Extends ProviderData with:
|
|
15
|
+
- schema_version: Schema identifier for file validation
|
|
16
|
+
- time_created: Timestamp for file creation
|
|
17
|
+
- services_populator: How to automatically populate service data
|
|
18
|
+
- provider_access_info: Parameters for accessing service provider
|
|
19
|
+
- logo, terms_of_service: Convenience fields (converted to documents during import)
|
|
20
|
+
- Typed Document model instead of dict for file validation
|
|
21
|
+
- Field validators for name format
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
model_config = ConfigDict(extra="forbid")
|
|
25
|
+
|
|
26
|
+
# File-specific fields for validation
|
|
27
|
+
schema_version: str = Field(default="provider_v1", description="Schema identifier", alias="schema")
|
|
28
|
+
time_created: datetime
|
|
29
|
+
|
|
30
|
+
# How to automatically populate service data, if available
|
|
31
|
+
services_populator: dict[str, Any] | None = None
|
|
32
|
+
|
|
33
|
+
# Parameters for accessing service provider (base_url, api_key)
|
|
34
|
+
provider_access_info: AccessInterface = Field(description="Dictionary of upstream access interface")
|
|
35
|
+
|
|
36
|
+
# Convenience fields for logo and terms of service (converted to documents during import)
|
|
37
|
+
logo: str | HttpUrl | None = None
|
|
38
|
+
|
|
39
|
+
terms_of_service: None | str | HttpUrl = Field(
|
|
40
|
+
default=None,
|
|
41
|
+
description="Either a path to a .md file or a URL to terms of service",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Override with typed Document model for file validation
|
|
45
|
+
documents: list[Document] | None = Field( # type: ignore[assignment]
|
|
46
|
+
default=None,
|
|
47
|
+
description="List of documents associated with the provider (e.g. logo)",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@field_validator("name")
|
|
51
|
+
@classmethod
|
|
52
|
+
def validate_name_format(cls, v: str) -> str:
|
|
53
|
+
"""Validate that provider name uses URL-safe identifiers."""
|
|
54
|
+
return validate_name(v, "provider", allow_slash=False)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Base data model for sellers.
|
|
2
|
+
|
|
3
|
+
This module defines `SellerData`, a base model containing the core fields
|
|
4
|
+
for seller data that is shared between:
|
|
5
|
+
- unitysvc-services (CLI): Used for file-based seller definitions
|
|
6
|
+
- unitysvc (backend): Used for API payloads and database operations
|
|
7
|
+
|
|
8
|
+
The `SellerV1` model extends this with file-specific fields like `schema_version`
|
|
9
|
+
and `time_created` for data file validation.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, EmailStr, Field, HttpUrl
|
|
15
|
+
|
|
16
|
+
from .base import SellerStatusEnum, SellerTypeEnum
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SellerData(BaseModel):
|
|
20
|
+
"""
|
|
21
|
+
Base data structure for seller information.
|
|
22
|
+
|
|
23
|
+
This model contains the core fields needed to describe a seller,
|
|
24
|
+
without file-specific validation fields. It serves as:
|
|
25
|
+
|
|
26
|
+
1. The base class for `SellerV1` in unitysvc-services (with additional
|
|
27
|
+
schema_version and time_created fields for file validation)
|
|
28
|
+
|
|
29
|
+
2. The data structure imported by unitysvc backend for:
|
|
30
|
+
- API payload validation
|
|
31
|
+
- Database comparison logic in find_and_compare_seller()
|
|
32
|
+
- Publish operations from CLI
|
|
33
|
+
|
|
34
|
+
Key characteristics:
|
|
35
|
+
- Uses string identifiers that match database requirements
|
|
36
|
+
- account_manager is a string (username/email) that gets resolved to account_manager_id
|
|
37
|
+
- Contains all user-provided data without system-generated IDs
|
|
38
|
+
- Does not include permission/audit fields (handled by backend CRUD layer)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Seller identification
|
|
42
|
+
name: str = Field(
|
|
43
|
+
description="Unique seller identifier (URL-friendly, e.g., 'acme-corp', 'john-doe')",
|
|
44
|
+
min_length=2,
|
|
45
|
+
max_length=100,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
display_name: str | None = Field(
|
|
49
|
+
default=None,
|
|
50
|
+
max_length=200,
|
|
51
|
+
description="Human-readable seller name (e.g., 'ACME Corporation', 'John Doe')",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Seller type
|
|
55
|
+
seller_type: SellerTypeEnum = Field(
|
|
56
|
+
default=SellerTypeEnum.individual,
|
|
57
|
+
description="Type of seller entity",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Contact information
|
|
61
|
+
contact_email: EmailStr = Field(description="Primary contact email for the seller")
|
|
62
|
+
|
|
63
|
+
secondary_contact_email: EmailStr | None = Field(
|
|
64
|
+
default=None,
|
|
65
|
+
description="Secondary contact email",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
account_manager: str | None = Field(
|
|
69
|
+
default=None,
|
|
70
|
+
max_length=100,
|
|
71
|
+
description="Email or username of the user managing this seller account",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
homepage: HttpUrl | None = Field(
|
|
75
|
+
default=None,
|
|
76
|
+
description="Seller's homepage URL",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Business information
|
|
80
|
+
description: str | None = Field(
|
|
81
|
+
default=None,
|
|
82
|
+
max_length=1000,
|
|
83
|
+
description="Brief description of the seller",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
business_registration: str | None = Field(
|
|
87
|
+
default=None,
|
|
88
|
+
max_length=100,
|
|
89
|
+
description="Business registration number (if organization)",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
tax_id: str | None = Field(
|
|
93
|
+
default=None,
|
|
94
|
+
max_length=100,
|
|
95
|
+
description="Tax identification number (EIN, VAT, etc.)",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Stripe Connect integration
|
|
99
|
+
stripe_connect_id: str | None = Field(
|
|
100
|
+
default=None,
|
|
101
|
+
max_length=255,
|
|
102
|
+
description="Stripe Connect account ID for payment processing",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Status
|
|
106
|
+
status: SellerStatusEnum = Field(
|
|
107
|
+
default=SellerStatusEnum.active,
|
|
108
|
+
description="Seller status: active, disabled, or draft (skip publish)",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
is_verified: bool = Field(
|
|
112
|
+
default=False,
|
|
113
|
+
description="Whether the seller has been verified (KYC/business verification)",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Documents (as dicts for flexibility)
|
|
117
|
+
documents: list[dict[str, Any]] | None = Field(
|
|
118
|
+
default=None,
|
|
119
|
+
description="List of documents associated with the seller",
|
|
120
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pydantic import ConfigDict, Field, HttpUrl, field_validator
|
|
4
|
+
|
|
5
|
+
from .base import Document, validate_name
|
|
6
|
+
from .seller_data import SellerData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SellerV1(SellerData):
|
|
10
|
+
"""
|
|
11
|
+
Seller information for marketplace sellers (seller_v1 schema).
|
|
12
|
+
|
|
13
|
+
Extends SellerData with:
|
|
14
|
+
- schema_version: Schema identifier for file validation
|
|
15
|
+
- time_created: Timestamp for file creation
|
|
16
|
+
- logo: Convenience field (converted to documents during import)
|
|
17
|
+
- Typed Document model instead of dict for file validation
|
|
18
|
+
- Field validators for name format
|
|
19
|
+
|
|
20
|
+
Each repository can only have one seller.json file at the root of the data directory.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
model_config = ConfigDict(extra="forbid")
|
|
24
|
+
|
|
25
|
+
# File-specific fields for validation
|
|
26
|
+
schema_version: str = Field(default="seller_v1", description="Schema identifier", alias="schema")
|
|
27
|
+
time_created: datetime
|
|
28
|
+
|
|
29
|
+
# Convenience field for logo (converted to documents during import)
|
|
30
|
+
logo: str | HttpUrl | None = None
|
|
31
|
+
|
|
32
|
+
# Override with typed Document model for file validation
|
|
33
|
+
documents: list[Document] | None = Field( # type: ignore[assignment]
|
|
34
|
+
default=None,
|
|
35
|
+
description="List of documents associated with the seller (e.g. business registration, tax documents)",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@field_validator("name")
|
|
39
|
+
@classmethod
|
|
40
|
+
def validate_name_format(cls, v: str) -> str:
|
|
41
|
+
"""Validate that seller name uses URL-safe identifiers."""
|
|
42
|
+
return validate_name(v, "seller", allow_slash=False)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Base data model for service offerings.
|
|
2
|
+
|
|
3
|
+
This module defines `ServiceOfferingData`, a base model containing the core fields
|
|
4
|
+
for service offering data that is shared between:
|
|
5
|
+
- unitysvc-services (CLI): Used for file-based service definitions
|
|
6
|
+
- unitysvc (backend): Used for API payloads and database operations
|
|
7
|
+
|
|
8
|
+
The `ServiceV1` model extends this with file-specific fields like `schema_version`
|
|
9
|
+
and `time_created` for data file validation.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, Field
|
|
15
|
+
|
|
16
|
+
from .base import CurrencyEnum, ServiceTypeEnum, UpstreamStatusEnum
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ServiceOfferingData(BaseModel):
|
|
20
|
+
"""
|
|
21
|
+
Base data structure for service offering information.
|
|
22
|
+
|
|
23
|
+
This model contains the core fields needed to describe a service offering,
|
|
24
|
+
without file-specific validation fields. It serves as:
|
|
25
|
+
|
|
26
|
+
1. The base class for `ServiceV1` in unitysvc-services (with additional
|
|
27
|
+
schema_version and time_created fields for file validation)
|
|
28
|
+
|
|
29
|
+
2. The data structure imported by unitysvc backend for:
|
|
30
|
+
- API payload validation
|
|
31
|
+
- Database comparison logic in find_and_compare_service_offering()
|
|
32
|
+
- Publish operations from CLI
|
|
33
|
+
|
|
34
|
+
Key characteristics:
|
|
35
|
+
- Uses string identifiers (provider_name) that get resolved to database IDs
|
|
36
|
+
- upstream_status maps to database 'status' field
|
|
37
|
+
- Contains all user-provided data without system-generated IDs
|
|
38
|
+
- Does not include permission/audit fields (handled by backend CRUD layer)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Service identification
|
|
42
|
+
name: str = Field(
|
|
43
|
+
description="Technical service name (e.g., 'gpt-4')",
|
|
44
|
+
max_length=100,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
display_name: str | None = Field(
|
|
48
|
+
default=None,
|
|
49
|
+
max_length=150,
|
|
50
|
+
description="Human-friendly common name (e.g., 'GPT-4 Turbo')",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
version: str | None = Field(
|
|
54
|
+
default=None,
|
|
55
|
+
max_length=50,
|
|
56
|
+
description="Service version if applicable",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
service_type: ServiceTypeEnum = Field(
|
|
60
|
+
default=ServiceTypeEnum.llm,
|
|
61
|
+
description="Category for grouping/comparison",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
description: str | None = Field(
|
|
65
|
+
default=None,
|
|
66
|
+
description="Service description",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
tagline: str | None = Field(
|
|
70
|
+
default=None,
|
|
71
|
+
description="Short elevator pitch or description for the service",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Provider info (resolved by publish layer)
|
|
75
|
+
provider_name: str | None = Field(
|
|
76
|
+
default=None,
|
|
77
|
+
description="Provider name (resolved from directory structure if not specified)",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Status
|
|
81
|
+
upstream_status: UpstreamStatusEnum = Field(
|
|
82
|
+
default=UpstreamStatusEnum.ready,
|
|
83
|
+
description="Status of the service from upstream service provider",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Technical details
|
|
87
|
+
details: dict[str, Any] | None = Field(
|
|
88
|
+
default=None,
|
|
89
|
+
description="Static technical specifications and features",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Pricing
|
|
93
|
+
seller_price: dict[str, Any] | None = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
description="Seller pricing: The agreed rate between seller and UnitySVC",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Access interface
|
|
99
|
+
upstream_access_interface: dict[str, Any] | None = Field(
|
|
100
|
+
default=None,
|
|
101
|
+
description="How to access the service from upstream",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Documents
|
|
105
|
+
documents: list[dict[str, Any]] | None = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
description="List of documents associated with the service",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Currency for seller_price
|
|
111
|
+
currency: CurrencyEnum = Field(
|
|
112
|
+
default=CurrencyEnum.USD,
|
|
113
|
+
description="Currency for seller_price",
|
|
114
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pydantic import ConfigDict, Field, HttpUrl, field_validator
|
|
5
|
+
|
|
6
|
+
from .base import (
|
|
7
|
+
AccessInterface,
|
|
8
|
+
Document,
|
|
9
|
+
Pricing,
|
|
10
|
+
TagEnum,
|
|
11
|
+
validate_name,
|
|
12
|
+
)
|
|
13
|
+
from .service_data import ServiceOfferingData
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ServiceV1(ServiceOfferingData):
|
|
17
|
+
"""
|
|
18
|
+
Service offering model for file-based definitions (service_v1 schema).
|
|
19
|
+
|
|
20
|
+
Extends ServiceOfferingData with:
|
|
21
|
+
- schema_version: Schema identifier for file validation
|
|
22
|
+
- time_created: Timestamp for file creation
|
|
23
|
+
- logo: Convenience field (converted to documents during import)
|
|
24
|
+
- tags: Tags for the service (e.g., bring your own API key)
|
|
25
|
+
- Typed models (AccessInterface, Document, Pricing) instead of dicts
|
|
26
|
+
- Field validators for name format
|
|
27
|
+
|
|
28
|
+
This model is used for validating service.json/service.toml files
|
|
29
|
+
created by the CLI tool.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
model_config = ConfigDict(extra="forbid")
|
|
33
|
+
|
|
34
|
+
# File-specific fields for validation
|
|
35
|
+
schema_version: str = Field(default="service_v1", description="Schema identifier", alias="schema")
|
|
36
|
+
time_created: datetime
|
|
37
|
+
|
|
38
|
+
# Override to make required in file validation (base has Optional for API flexibility)
|
|
39
|
+
display_name: str = Field( # type: ignore[assignment]
|
|
40
|
+
max_length=150,
|
|
41
|
+
description="Human-friendly common name (e.g., 'GPT-4 Turbo')",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
description: str = Field( # type: ignore[assignment]
|
|
45
|
+
description="Service description",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Required in file for static information
|
|
49
|
+
details: dict[str, Any] = Field( # type: ignore[assignment]
|
|
50
|
+
description="Dictionary of static features and information",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Convenience field for logo (converted to documents during import)
|
|
54
|
+
logo: str | HttpUrl | None = None
|
|
55
|
+
|
|
56
|
+
# Tags for the service
|
|
57
|
+
tags: list[TagEnum] | None = Field(
|
|
58
|
+
default=None,
|
|
59
|
+
description="List of tags for the service, e.g., bring your own API key",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Override with typed models for file validation
|
|
63
|
+
upstream_access_interface: AccessInterface = Field( # type: ignore[assignment]
|
|
64
|
+
description="Dictionary of upstream access interface",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
documents: list[Document] | None = Field( # type: ignore[assignment]
|
|
68
|
+
default=None,
|
|
69
|
+
description="List of documents associated with the service (e.g. tech spec.)",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
seller_price: Pricing | None = Field( # type: ignore[assignment]
|
|
73
|
+
default=None,
|
|
74
|
+
description="Seller pricing information",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@field_validator("name")
|
|
78
|
+
@classmethod
|
|
79
|
+
def validate_name_format(cls, v: str) -> str:
|
|
80
|
+
"""Validate that service name uses valid identifiers (allows slashes for hierarchical names)."""
|
|
81
|
+
return validate_name(v, "service", allow_slash=True)
|