diff --git a/apps/coordinator-api/src/app/schemas.py b/apps/coordinator-api/src/app/schemas.py index 4f140749..432142f5 100644 --- a/apps/coordinator-api/src/app/schemas.py +++ b/apps/coordinator-api/src/app/schemas.py @@ -196,6 +196,7 @@ class MarketplaceOfferView(BaseModel): cuda_version: Optional[str] = None price_per_hour: Optional[float] = None region: Optional[str] = None + attributes: Optional[dict] = None class MarketplaceStatsView(BaseModel): diff --git a/apps/coordinator-api/src/app/services/marketplace.py b/apps/coordinator-api/src/app/services/marketplace.py index d34a913c..0fd871ba 100644 --- a/apps/coordinator-api/src/app/services/marketplace.py +++ b/apps/coordinator-api/src/app/services/marketplace.py @@ -87,4 +87,5 @@ class MarketplaceService: cuda_version=offer.cuda_version, price_per_hour=offer.price_per_hour, region=offer.region, + attributes=offer.attributes, ) diff --git a/apps/marketplace-web/src/lib/api.ts b/apps/marketplace-web/src/lib/api.ts index 97e69ad7..9ac8c60d 100644 --- a/apps/marketplace-web/src/lib/api.ts +++ b/apps/marketplace-web/src/lib/api.ts @@ -16,6 +16,13 @@ interface OfferRecord { cuda_version?: string; price_per_hour?: number; region?: string; + attributes?: { + ollama_host?: string; + models?: string[]; + vram_mb?: number; + driver?: string; + [key: string]: unknown; + }; } interface OffersResponse { diff --git a/apps/marketplace-web/src/main.ts b/apps/marketplace-web/src/main.ts index 0d901ef9..82b4e1ff 100644 --- a/apps/marketplace-web/src/main.ts +++ b/apps/marketplace-web/src/main.ts @@ -151,6 +151,14 @@ function renderOffers(offers: MarketplaceOffer[]): void { ${formatNumber(offer.capacity)} units + ${offer.attributes?.models?.length ? ` +
+ Ollama +
+
+ Available Models +
${offer.attributes.models.map(m => `${m}`).join('')}
+
` : ''}
${formatNumber(offer.price_per_hour ?? offer.price, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} credits/hr
${offer.sla}
diff --git a/apps/marketplace-web/src/style.css b/apps/marketplace-web/src/style.css index 761a3d5b..af6eb8f6 100644 --- a/apps/marketplace-web/src/style.css +++ b/apps/marketplace-web/src/style.css @@ -203,6 +203,54 @@ body { color: #64748b; } +.offer-plugins { + margin-bottom: 8px; +} + +.plugin-badge { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 10px; + border-radius: 999px; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + background: linear-gradient(135deg, #6366f1, #8b5cf6); + color: #ffffff; +} + +.offer-models { + margin-bottom: 16px; +} + +.models-label { + display: block; + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.06em; + color: #94a3b8; + margin-bottom: 8px; +} + +.model-tags { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.model-tag { + display: inline-block; + padding: 3px 10px; + border-radius: 8px; + font-size: 0.78rem; + font-weight: 500; + background: #f1f5f9; + color: #334155; + border: 1px solid #e2e8f0; +} + .status-pill { display: inline-flex; align-items: center;