feat: show Ollama plugin badge and available LLM models on marketplace cards
Backend: - Add attributes field to MarketplaceOfferView schema - Include attributes in _to_offer_view() response Frontend: - Add attributes type with Ollama fields to OfferRecord - Show purple 'Ollama' plugin badge on cards with models - Display available LLM model tags (gemma3, deepseek-r1, etc.) - Add plugin-badge and model-tag CSS styles
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -87,4 +87,5 @@ class MarketplaceService:
|
||||
cuda_version=offer.cuda_version,
|
||||
price_per_hour=offer.price_per_hour,
|
||||
region=offer.region,
|
||||
attributes=offer.attributes,
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -151,6 +151,14 @@ function renderOffers(offers: MarketplaceOffer[]): void {
|
||||
<span class="spec-value">${formatNumber(offer.capacity)} units</span>
|
||||
</div>
|
||||
</div>
|
||||
${offer.attributes?.models?.length ? `
|
||||
<div class="offer-plugins">
|
||||
<span class="plugin-badge">Ollama</span>
|
||||
</div>
|
||||
<div class="offer-models">
|
||||
<span class="models-label">Available Models</span>
|
||||
<div class="model-tags">${offer.attributes.models.map(m => `<span class="model-tag">${m}</span>`).join('')}</div>
|
||||
</div>` : ''}
|
||||
<div class="offer-pricing">
|
||||
<div class="offer-price">${formatNumber(offer.price_per_hour ?? offer.price, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} <small>credits/hr</small></div>
|
||||
<div class="offer-sla">${offer.sla}</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user