unitysvc-core 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ """UnitySVC Core — shared data models and validation helpers.
2
+
3
+ This package is audience-neutral. It contains the pydantic models, JSON
4
+ schemas, and validator consumed by the UnitySVC backend, the customer SDK,
5
+ the admin CLI, and the seller SDK.
6
+
7
+ It intentionally does NOT include any CLI, HTTP client, or audience-
8
+ specific helpers (seller catalog builders, customer query helpers, etc.).
9
+ Those live in the corresponding audience packages (``unitysvc-sellers``,
10
+ ``unitysvc`` customer SDK, ``unitysvc-admin``).
11
+ """
12
+
13
+ __author__ = """Bo Peng"""
14
+ __email__ = "bo.peng@unitysvc.com"
15
+
16
+ # Shared file / data utilities used by the backend and SDK consumers
17
+ from .utils import (
18
+ compute_file_hash,
19
+ generate_content_based_key,
20
+ get_basename,
21
+ get_file_extension,
22
+ mime_type_to_extension,
23
+ )
24
+
25
+ __all__ = [
26
+ # File utilities
27
+ "compute_file_hash",
28
+ "generate_content_based_key",
29
+ "get_basename",
30
+ "get_file_extension",
31
+ "mime_type_to_extension",
32
+ ]
@@ -0,0 +1,171 @@
1
+ from .base import (
2
+ AccessMethodEnum,
3
+ AuthMethodEnum,
4
+ ContentFilterEnum,
5
+ CurrencyEnum,
6
+ DocumentCategoryEnum,
7
+ DocumentContextEnum,
8
+ ListingStatusEnum,
9
+ MimeTypeEnum,
10
+ OfferingStatusEnum,
11
+ OveragePolicyEnum,
12
+ PriceRuleApplyAtEnum,
13
+ PriceRuleStatusEnum,
14
+ PricingTypeEnum,
15
+ ProviderStatusEnum,
16
+ QuotaResetCycleEnum,
17
+ RateLimitUnitEnum,
18
+ RequestTransformEnum,
19
+ SellerTypeEnum,
20
+ ServiceGroupStatusEnum,
21
+ ServiceTypeEnum,
22
+ TimeWindowEnum,
23
+ UpstreamStatusEnum, # Backwards compatibility alias for OfferingStatusEnum
24
+ )
25
+ from .documents import DocumentData
26
+ from .listing_data import ServiceListingData
27
+ from .listing_v1 import ListingV1
28
+ from .offering_data import ServiceOfferingData
29
+ from .offering_v1 import OfferingV1
30
+ from .pricing import (
31
+ AddPriceData,
32
+ BasePriceData,
33
+ ConstantPriceData,
34
+ CountPriceData,
35
+ DataPriceData,
36
+ ExprPriceData,
37
+ FirstPriceData,
38
+ GraduatedPriceData,
39
+ GraduatedTier,
40
+ ImagePriceData,
41
+ MaxPriceData,
42
+ MinPriceData,
43
+ MultiplyPriceData,
44
+ PercentageStr,
45
+ PriceStr,
46
+ PriceTier,
47
+ Pricing,
48
+ RevenueSharePriceData,
49
+ StepPriceData,
50
+ TieredPriceData,
51
+ TimePriceData,
52
+ TokenPriceData,
53
+ UsageData,
54
+ validate_pricing,
55
+ )
56
+ from .promotion_data import (
57
+ PROMOTION_SCHEMA_VERSION,
58
+ PromotionData,
59
+ describe_scope,
60
+ is_promotion_file,
61
+ strip_schema_field,
62
+ validate_promotion,
63
+ )
64
+ from .promotion_v1 import PromotionV1
65
+ from .provider_data import ProviderData
66
+ from .provider_v1 import ProviderV1
67
+ from .service import (
68
+ AccessInterfaceData,
69
+ RateLimit,
70
+ ServiceConstraints,
71
+ UpstreamAccessConfigData,
72
+ )
73
+ from .service_group_data import (
74
+ SERVICE_GROUP_SCHEMA_VERSION,
75
+ ServiceGroupData,
76
+ is_service_group_file,
77
+ validate_service_group,
78
+ )
79
+ from .service_group_v1 import ServiceGroupV1
80
+ from .validators import (
81
+ SUPPORTED_SERVICE_OPTIONS,
82
+ suggest_valid_name,
83
+ validate_name,
84
+ validate_service_options,
85
+ )
86
+
87
+ __all__ = [
88
+ # V1 models (for file validation)
89
+ "ProviderV1",
90
+ "OfferingV1",
91
+ "ListingV1",
92
+ "PromotionV1",
93
+ "ServiceGroupV1",
94
+ # Data models (for API/backend use)
95
+ "ProviderData",
96
+ "ServiceOfferingData",
97
+ "ServiceListingData",
98
+ "PromotionData",
99
+ "ServiceGroupData",
100
+ # Shared / access models
101
+ "DocumentData",
102
+ "AccessInterfaceData",
103
+ "UpstreamAccessConfigData",
104
+ "RateLimit",
105
+ "ServiceConstraints",
106
+ # Enums
107
+ "AccessMethodEnum",
108
+ "AuthMethodEnum",
109
+ "ContentFilterEnum",
110
+ "CurrencyEnum",
111
+ "DocumentCategoryEnum",
112
+ "DocumentContextEnum",
113
+ "ListingStatusEnum",
114
+ "MimeTypeEnum",
115
+ "OfferingStatusEnum",
116
+ "OveragePolicyEnum",
117
+ "PriceRuleApplyAtEnum",
118
+ "PriceRuleStatusEnum",
119
+ "PricingTypeEnum",
120
+ "ProviderStatusEnum",
121
+ "QuotaResetCycleEnum",
122
+ "RateLimitUnitEnum",
123
+ "RequestTransformEnum",
124
+ "SellerTypeEnum",
125
+ "ServiceGroupStatusEnum",
126
+ "ServiceTypeEnum",
127
+ "TimeWindowEnum",
128
+ "UpstreamStatusEnum", # Backwards compatibility alias for OfferingStatusEnum
129
+ # Pricing — primitives
130
+ "PriceStr",
131
+ "PercentageStr",
132
+ "UsageData",
133
+ "Pricing",
134
+ "validate_pricing",
135
+ "BasePriceData",
136
+ # Pricing — simple types
137
+ "TokenPriceData",
138
+ "TimePriceData",
139
+ "DataPriceData",
140
+ "CountPriceData",
141
+ "ImagePriceData",
142
+ "StepPriceData",
143
+ "RevenueSharePriceData",
144
+ "ConstantPriceData",
145
+ # Pricing — composite types
146
+ "AddPriceData",
147
+ "MultiplyPriceData",
148
+ "MaxPriceData",
149
+ "MinPriceData",
150
+ "FirstPriceData",
151
+ "TieredPriceData",
152
+ "GraduatedPriceData",
153
+ "ExprPriceData",
154
+ "PriceTier",
155
+ "GraduatedTier",
156
+ # Validators
157
+ "SUPPORTED_SERVICE_OPTIONS",
158
+ "validate_name",
159
+ "validate_service_options",
160
+ "suggest_valid_name",
161
+ # Promotions
162
+ "PROMOTION_SCHEMA_VERSION",
163
+ "is_promotion_file",
164
+ "describe_scope",
165
+ "strip_schema_field",
166
+ "validate_promotion",
167
+ # Service Groups
168
+ "SERVICE_GROUP_SCHEMA_VERSION",
169
+ "is_service_group_file",
170
+ "validate_service_group",
171
+ ]
@@ -0,0 +1,317 @@
1
+ """Enum types and basic definitions shared across the data models.
2
+
3
+ This module contains only enums and simple constants. Data classes with
4
+ behavior (pricing, documents, service constraints, etc.) and validation
5
+ functions live in their own modules:
6
+
7
+ - ``pricing.py`` — pricing types and cost calculation
8
+ - ``documents.py`` — DocumentData
9
+ - ``service.py`` — RateLimit, ServiceConstraints, AccessInterfaceData, UpstreamAccessConfigData
10
+ - ``validators.py`` — validate_name, validate_service_options, suggest_valid_name
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from enum import StrEnum
16
+
17
+
18
+ class AccessMethodEnum(StrEnum):
19
+ http = "http"
20
+ websocket = "websocket"
21
+ grpc = "grpc"
22
+ smtp = "smtp"
23
+
24
+
25
+ class CurrencyEnum(StrEnum):
26
+ """Supported currency codes for pricing."""
27
+
28
+ # Traditional currencies
29
+ USD = "USD" # US Dollar
30
+ EUR = "EUR" # Euro
31
+ GBP = "GBP" # British Pound
32
+ JPY = "JPY" # Japanese Yen
33
+ CNY = "CNY" # Chinese Yuan
34
+ CAD = "CAD" # Canadian Dollar
35
+ AUD = "AUD" # Australian Dollar
36
+ CHF = "CHF" # Swiss Franc
37
+ INR = "INR" # Indian Rupee
38
+ KRW = "KRW" # Korean Won
39
+
40
+ # Cryptocurrencies
41
+ BTC = "BTC" # Bitcoin
42
+ ETH = "ETH" # Ethereum
43
+ USDT = "USDT" # Tether
44
+ USDC = "USDC" # USD Coin
45
+ TAO = "TAO" # Bittensor TAO
46
+
47
+ # Credits/Points (for platforms that use credits)
48
+ CREDITS = "CREDITS" # Generic credits system
49
+
50
+
51
+ class AuthMethodEnum(StrEnum):
52
+ api_key = "api_key"
53
+ oauth = "oauth"
54
+ jwt = "jwt"
55
+ bearer_token = "bearer_token"
56
+ basic_auth = "basic_auth"
57
+
58
+
59
+ class ContentFilterEnum(StrEnum):
60
+ adult = "adult"
61
+ violence = "violence"
62
+ hate_speech = "hate_speech"
63
+ profanity = "profanity"
64
+ pii = "pii" # Personally Identifiable Information
65
+
66
+
67
+ class DocumentContextEnum(StrEnum):
68
+ service_definition = "service_definition" # Documents belong to ServiceDefinition
69
+ service_offering = "service_offering" # Documents belong to ServiceOffering
70
+ service_listing = "service_listing" # Documents belong to ServiceListing
71
+ user = "user" # can be for seller, subscriber, consumer
72
+ # Backend-specific contexts
73
+ seller = "seller" # Documents belong to Seller
74
+ provider = "provider" # Documents belong to Provider
75
+ blog_post = "blog_post" # Documents belong to BlogPost
76
+ #
77
+ customer_statement = "customer_statement"
78
+ seller_invoice = "seller_invoice"
79
+
80
+
81
+ class DocumentCategoryEnum(StrEnum):
82
+ getting_started = "getting_started"
83
+ api_reference = "api_reference"
84
+ tutorial = "tutorial"
85
+ code_example = "code_example"
86
+ code_example_output = "code_example_output"
87
+ connectivity_test = "connectivity_test" # Test connectivity & performance (not visible to users)
88
+ request_template = "request_template" # Default request body for playground pre-fill
89
+ use_case = "use_case"
90
+ troubleshooting = "troubleshooting"
91
+ changelog = "changelog"
92
+ best_practice = "best_practice"
93
+ specification = "specification"
94
+ service_level_agreement = "service_level_agreement"
95
+ terms_of_service = "terms_of_service"
96
+ statement = "statement"
97
+ invoice = "invoice"
98
+ logo = "logo"
99
+ avatar = "avatar"
100
+ blog_content = "blog_content" # Main content for blog posts
101
+ blog_banner = "blog_banner" # Banner/cover image for blog posts
102
+ attachment = "attachment" # Attachments for markdown documents
103
+ other = "other"
104
+
105
+
106
+ class MimeTypeEnum(StrEnum):
107
+ markdown = "markdown"
108
+ python = "python"
109
+ javascript = "javascript"
110
+ bash = "bash"
111
+ html = "html"
112
+ json = "json"
113
+ text = "text"
114
+ pdf = "pdf"
115
+ jpeg = "jpeg"
116
+ png = "png"
117
+ svg = "svg"
118
+ url = "url"
119
+
120
+
121
+ class ServiceGroupStatusEnum(StrEnum):
122
+ draft = "draft"
123
+ active = "active"
124
+ private = "private"
125
+ archived = "archived"
126
+
127
+
128
+ class SellerTypeEnum(StrEnum):
129
+ individual = "individual"
130
+ organization = "organization"
131
+ partnership = "partnership"
132
+ corporation = "corporation"
133
+
134
+
135
+ class ListingStatusEnum(StrEnum):
136
+ """
137
+ Status values that sellers can set for listings.
138
+
139
+ Seller-accessible statuses:
140
+ - draft: Work in progress, skipped during publish (won't be sent to backend)
141
+ - ready: Complete and ready for admin review/testing
142
+ - deprecated: Retired/end of life, no longer offered
143
+
144
+ Note: Admin-managed workflow statuses (upstream_ready, downstream_ready, in_service)
145
+ are set by the backend admin after testing and validation. These are not included in this
146
+ enum since sellers cannot set them through the CLI tool.
147
+ """
148
+
149
+ draft = "draft"
150
+ ready = "ready"
151
+ deprecated = "deprecated"
152
+
153
+
154
+ class OveragePolicyEnum(StrEnum):
155
+ block = "block" # Block requests when quota exceeded
156
+ throttle = "throttle" # Reduce rate when quota exceeded
157
+ charge = "charge" # Allow with additional charges
158
+ queue = "queue" # Queue requests until quota resets
159
+
160
+
161
+ class PricingTypeEnum(StrEnum):
162
+ """
163
+ Pricing type determines the structure and calculation method.
164
+ The type is stored as the 'type' field in the pricing object.
165
+ """
166
+
167
+ # Basic pricing types
168
+ one_million_tokens = "one_million_tokens"
169
+ one_thousand_tokens = "one_thousand_tokens"
170
+ one_token = "one_token"
171
+ one_second = "one_second"
172
+ one_minute = "one_minute"
173
+ one_hour = "one_hour"
174
+ one_day = "one_day"
175
+ one_month = "one_month"
176
+ one_byte = "one_byte"
177
+ one_kilobyte = "one_kilobyte"
178
+ one_megabyte = "one_megabyte"
179
+ one_gigabyte = "one_gigabyte"
180
+ one_thousand = "one_thousand"
181
+ one_million = "one_million"
182
+ image = "image"
183
+ step = "step"
184
+ # Seller-only: seller receives a percentage of what customer pays
185
+ revenue_share = "revenue_share"
186
+ # Composite pricing types
187
+ constant = "constant" # Fixed amount (fee or discount)
188
+ add = "add" # Sum of multiple prices
189
+ multiply = "multiply" # Base price multiplied by factor
190
+ max = "max" # Highest of multiple prices (lenient)
191
+ min = "min" # Lowest of multiple prices (lenient)
192
+ first = "first" # First applicable price (lenient)
193
+ # Tiered pricing types
194
+ tiered = "tiered" # Volume-based tiers (all units at one tier's price)
195
+ graduated = "graduated" # Graduated tiers (each tier's units at that rate)
196
+ # Expression-based pricing (payout_price only)
197
+ expr = "expr" # Arbitrary expression using usage metrics
198
+
199
+
200
+
201
+ class QuotaResetCycleEnum(StrEnum):
202
+ daily = "daily"
203
+ weekly = "weekly"
204
+ monthly = "monthly"
205
+ yearly = "yearly"
206
+
207
+
208
+ class RateLimitUnitEnum(StrEnum):
209
+ requests = "requests"
210
+ tokens = "tokens"
211
+ input_tokens = "input_tokens"
212
+ output_tokens = "output_tokens"
213
+ bytes = "bytes"
214
+ concurrent = "concurrent"
215
+
216
+
217
+ class RequestTransformEnum(StrEnum):
218
+ # https://docs.api7.ai/hub/proxy-rewrite
219
+ proxy_rewrite = "proxy_rewrite"
220
+ # https://docs.api7.ai/hub/body-transformer
221
+ body_transformer = "body_transformer"
222
+ # Simple body replacement from rendered enrollment data
223
+ set_body = "set_body"
224
+
225
+
226
+ class ServiceTypeEnum(StrEnum):
227
+ """Broad service category — defines the access pattern and protocol.
228
+
229
+ AI modalities (vision, tools, rerank, etc.) are tracked via the
230
+ `capabilities` list on ServiceOffering, not service_type.
231
+ """
232
+
233
+ # === AI / ML services (HTTP API) ===
234
+ llm = "llm" # Language models, chat completions
235
+ embedding = "embedding" # Text/vector embedding
236
+ image_generation = "image_generation" # Image creation/editing
237
+
238
+ # === Communication services ===
239
+ notification = "notification" # Push notifications (HTTP)
240
+ email = "email" # Email delivery (SMTP gateway)
241
+
242
+ # === Content services ===
243
+ content = "content" # Downloadable files, datasets, images, software
244
+ streaming = "streaming" # Video, audio, live feeds (persistent connection)
245
+
246
+ # === Compute services ===
247
+ compute = "compute" # GPU instances, dev environments (SSH/WireGuard)
248
+
249
+ # === Infrastructure services ===
250
+ database = "database" # Managed DB/cache access (SSH tunnel)
251
+ monitoring = "monitoring" # Uptime checks, health monitoring
252
+ analytics = "analytics" # Recommendation, anomaly detection, forecasting
253
+
254
+
255
+ class TimeWindowEnum(StrEnum):
256
+ second = "second"
257
+ minute = "minute"
258
+ hour = "hour"
259
+ day = "day"
260
+ month = "month"
261
+
262
+
263
+ class OfferingStatusEnum(StrEnum):
264
+ """
265
+ Status values that sellers can set for service offerings.
266
+
267
+ Seller-accessible statuses:
268
+ - draft: Work in progress, skipped during publish
269
+ - ready: Complete and ready for admin review
270
+ - deprecated: Service is retired/end of life
271
+ """
272
+
273
+ draft = "draft"
274
+ ready = "ready"
275
+ deprecated = "deprecated"
276
+
277
+
278
+ # Backwards compatibility alias
279
+ UpstreamStatusEnum = OfferingStatusEnum
280
+
281
+
282
+ class ProviderStatusEnum(StrEnum):
283
+ """
284
+ Status values that sellers can set for providers.
285
+
286
+ Seller-accessible statuses:
287
+ - draft: Work in progress, skipped during publish
288
+ - ready: Complete and ready for admin review
289
+ - deprecated: Provider is retired/end of life
290
+ """
291
+
292
+ draft = "draft"
293
+ ready = "ready"
294
+ deprecated = "deprecated"
295
+
296
+
297
+ class PriceRuleApplyAtEnum(StrEnum):
298
+ """When the price rule is applied."""
299
+
300
+ request = "request" # Applied per API call
301
+ statement = "statement" # Applied during billing/statement generation
302
+
303
+
304
+
305
+ class PriceRuleStatusEnum(StrEnum):
306
+ """Seller-facing status values for promotions.
307
+
308
+ The backend may define additional statuses (scheduled, expired,
309
+ cancelled) for internal lifecycle management, but sellers only
310
+ interact with these three.
311
+ """
312
+
313
+ draft = "draft" # Not yet active, can be edited
314
+ active = "active" # Currently active and applied
315
+ paused = "paused" # Temporarily disabled
316
+
317
+
@@ -0,0 +1,46 @@
1
+ """Document data model — used by provider, offering, listing, and promotion files."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+
9
+ from .base import DocumentCategoryEnum, MimeTypeEnum
10
+
11
+
12
+ class DocumentData(BaseModel):
13
+ """Document data for SDK/API payloads.
14
+
15
+ Note: The document title is NOT stored here - it's the key in the documents dict.
16
+ When stored in the database, the backend extracts the key as the title field.
17
+ """
18
+
19
+ model_config = ConfigDict(extra="forbid")
20
+
21
+ description: str | None = Field(default=None, max_length=500, description="Document description")
22
+ mime_type: MimeTypeEnum = Field(description="Document MIME type")
23
+ version: str | None = Field(default=None, max_length=50, description="Document version")
24
+ category: DocumentCategoryEnum = Field(description="Document category for organization and filtering")
25
+ meta: dict[str, Any] | None = Field(
26
+ default=None,
27
+ description="JSON containing operation stats",
28
+ )
29
+ file_path: str | None = Field(
30
+ default=None,
31
+ max_length=1000,
32
+ description="Path to file to upload (mutually exclusive with external_url)",
33
+ )
34
+ external_url: str | None = Field(
35
+ default=None,
36
+ max_length=1000,
37
+ description="External URL for the document (mutually exclusive with object_key)",
38
+ )
39
+ sort_order: int = Field(default=0, description="Sort order within category")
40
+ is_active: bool = Field(default=True, description="Whether document is active")
41
+ is_public: bool = Field(
42
+ default=False,
43
+ description="Whether document is publicly accessible without authentication",
44
+ )
45
+
46
+
@@ -0,0 +1,115 @@
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-core (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
+ from uuid import UUID
14
+
15
+ from pydantic import BaseModel, Field
16
+
17
+ from .base import CurrencyEnum, ListingStatusEnum
18
+
19
+
20
+ class ServiceListingData(BaseModel):
21
+ """
22
+ Base data structure for service listing information.
23
+
24
+ This model contains the core fields needed to describe a service listing,
25
+ without file-specific validation fields. It serves as:
26
+
27
+ 1. The base class for `ListingV1` in unitysvc-core (with additional
28
+ schema_version and time_created fields for file validation)
29
+
30
+ 2. The data structure imported by unitysvc backend for:
31
+ - API payload validation
32
+ - Database comparison logic in find_and_compare_service_listing()
33
+ - Publish operations from CLI
34
+
35
+ Key characteristics:
36
+ - Contains all user-provided data without system-generated IDs
37
+ - Does not include permission/audit fields (handled by backend CRUD layer)
38
+ - Uses dict types for nested structures to maintain flexibility between
39
+ file definitions and database operations
40
+ - Service/provider relationships are determined by file location (SDK mode) or
41
+ by being published together in a single API call (API mode)
42
+ """
43
+
44
+ model_config = {"extra": "ignore"}
45
+
46
+ # Service ID for updates (set by SDK after first publish)
47
+ # When provided, updates the existing service. When absent, creates a new service.
48
+ service_id: UUID | None = Field(
49
+ default=None,
50
+ description="Service ID from previous publish. If provided, updates existing service. "
51
+ "Stored in override file (e.g., listing.override.json) by SDK after first publish.",
52
+ )
53
+
54
+ # Listing identification
55
+ name: str | None = Field(
56
+ default=None,
57
+ max_length=255,
58
+ description="Name identifier for the service listing (defaults to offering name if not provided)",
59
+ )
60
+
61
+ # Display name for UI (optional - falls back to Service.name derived from offering/listing name)
62
+ display_name: str | None = Field(
63
+ default=None,
64
+ max_length=200,
65
+ description="Human-readable listing name (e.g., 'Premium GPT-4 Access', 'Enterprise AI Services')",
66
+ )
67
+
68
+ # Status - seller-accessible statuses
69
+ status: ListingStatusEnum = Field(
70
+ default=ListingStatusEnum.draft,
71
+ description="Listing status: draft (skip publish), ready (ready for admin review), or deprecated (retired)",
72
+ )
73
+
74
+ # List pricing
75
+ list_price: dict[str, Any] | None = Field(
76
+ default=None,
77
+ description="List price: Listed price for customers per unit of service usage",
78
+ )
79
+
80
+ # Currency for list_price
81
+ currency: CurrencyEnum = Field(
82
+ default=CurrencyEnum.USD,
83
+ description="Currency for list_price (indexed for filtering)",
84
+ )
85
+
86
+ # Access interfaces (keyed by name)
87
+ user_access_interfaces: dict[str, dict[str, Any]] | None = Field(
88
+ default=None,
89
+ description="User access interfaces for the listing, keyed by name",
90
+ )
91
+
92
+ # Documents (keyed by title)
93
+ documents: dict[str, dict[str, Any]] | None = Field(
94
+ default=None,
95
+ description="Documents associated with the listing, keyed by title (e.g., service level agreements)",
96
+ )
97
+
98
+ # User parameters
99
+ user_parameters_schema: dict[str, Any] | None = Field(
100
+ default=None,
101
+ description="JSON Schema for user parameters",
102
+ )
103
+
104
+ user_parameters_ui_schema: dict[str, Any] | None = Field(
105
+ default=None,
106
+ description="UI schema for user parameters form rendering",
107
+ )
108
+
109
+ # Service-specific options
110
+ service_options: dict[str, Any] | None = Field(
111
+ default=None,
112
+ description="Service-specific options that modify backend behavior. "
113
+ "Keys are option names, values are option configurations. "
114
+ "The backend decides which options it supports.",
115
+ )