catalogentry

package
v1.49.25 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package catalogentry is the CMS source-of-truth for the platform product catalog — the single list docs.<brand>, the console sidebar, and pricing all derive from. commerce owns the DATA (source + seed + edits); the shape is the @hanzo/products contract (that package owns the schema + the iconKey→component and brandColor→css code-maps). Pricing is native: an entry references a pricing plan by key (PricingId), or carries a fixed PriceCents.

Conformance (GET /v1/commerce/catalog → the @hanzo/products CatalogEntry):

  • iconKey is a @hanzogui/lucide-icons-2 export NAME ("Brain") — never a component.
  • brandColor is a swatch KEY ("violet") — never hex. @hanzo/products maps key→css.
  • category is EXACTLY one of the 10 canonical categories (others are dropped by scope).
  • route is "/<slug>", apiPath is /v1-prefixed, docsUrl is /docs/services/<slug>.
  • pricingId is a pricing plans/<key>.json key, or null.
  • brands is a category-derived convenience — NOT a hand-authored filter; the server scopes by CATEGORY (categoriesForBrand), matching @hanzo/products catalogForBrand.

Index

Constants

View Source
const (
	StatusEnabled  = "enabled"  // in-console module that works
	StatusExternal = "external" // live external brand surface
	StatusSoon     = "soon"     // primitive shipped, no console surface yet
)

Status enumerates a product's honest enablement state (mirrors the console registry): a working in-console module, a live external surface, or a primitive that ships with no console surface yet. No fabricated states.

View Source
const (
	FamilyEnso       = "enso"
	FamilyZen        = "zen"
	FamilyThirdParty = "third-party"
)

Model families. Enso and Zen are OURS and are presented as ours — never described in terms of an upstream model. Everything we resell is third-party, named plainly by its VENDOR ("Meta", never "Meta Llama").

View Source
const (
	CategoryEnso  = "enso"
	CategoryZen   = "zen"
	CategoryModel = "model" // third-party, grouped for display by ModelSpec.Vendor
)

Model categories. Following the infra-tier precedent exactly: model rows get their own categories, surfaced under the brand-neutral "models" SCOPE, so they never pollute the ten canonical cloud-primitive product categories. Our two families are their own categories — that IS the split between ours and resold, expressed as data rather than as a naming convention.

View Source
const (
	RateIn         = "in"
	RateOut        = "out"
	RateCacheRead  = "cacheRead"
	RateCacheWrite = "cacheWrite"
)

Rate keys. A single-component price (a VM, a plan) leaves Key empty.

View Source
const (
	UnitMTok   = "mtok" // per million tokens
	UnitMonth  = "month"
	UnitHour   = "hour"
	UnitImage  = "image"
	UnitCall   = "call"
	UnitSecond = "second"
)

Rate units.

View Source
const DefaultMarkup = "1.20"

DefaultMarkup is the retail multiple applied to a synced upstream cost when an entry sets no Markup of its own. It matches the 1.20 spread the OpenRouter resale already ran at, so making markup per-entry changes no customer's price on the day it lands.

Variables

This section is empty.

Functions

func CategoryForFamily added in v1.49.25

func CategoryForFamily(family string) string

CategoryForFamily maps a family to the category its entries live in, so a syncer never has to know the taxonomy.

func CategorySlug

func CategorySlug(label string) string

CategorySlug slugifies a category label the way @hanzo/products categorySlug does: simple lowercase ("AI"→"ai", "Web3"→"web3").

func IsModel added in v1.49.25

func IsModel(e *CatalogEntry) bool

IsModel reports whether an entry is a model row.

func Query

func RateMarginPct added in v1.49.25

func RateMarginPct(r Rate, markup string) *float64

RateMarginPct returns the gross margin of a rate's retail price over its upstream cost, as a percentage rounded to two decimals: (price-cost)/price. nil when either side is unknown, so an uncomputable margin is an absent field rather than a misleading 0 or 100.

func Seed

func Seed(db *datastore.Datastore) (created int, err error)

Seed loads the embedded @hanzo/products snapshot into db (which MUST be namespaced to the catalog-owning "system" org). IDEMPOTENT + NON-DESTRUCTIVE: an entry whose slug already exists is left UNTOUCHED so CMS edits are never clobbered; only missing slugs are created. Order is the snapshot index (stable display order within a category). Returns the number created.

func SeedIfEmpty

func SeedIfEmpty(db *datastore.Datastore) (created int, err error)

SeedIfEmpty seeds the catalog only when it is currently empty — a cheap single count query gates the full per-row create, so it is safe to call on every bootstrap. Once any entry exists (seeded or CMS-created) it never re-runs, so CMS state stays authoritative.

func SeedInfra added in v1.49.9

func SeedInfra(db *datastore.Datastore) (created int, err error)

SeedInfra loads the embedded infra-tier snapshot into db (which MUST be namespaced to the catalog-owning "system" org). IDEMPOTENT + NON-DESTRUCTIVE, exactly like Seed: an entry whose slug already exists is left UNTOUCHED so CMS edits are never clobbered; only missing slugs are created. Returns the number created.

func SeedInfraIfEmpty added in v1.49.9

func SeedInfraIfEmpty(db *datastore.Datastore) (created int, err error)

SeedInfraIfEmpty seeds the infra tiers only when NONE are present yet — a cheap per-category count gates the full per-row create, so it is safe to call on every bootstrap (mirrors SeedIfEmpty). Once any infra tier exists (seeded or CMS-edited) it never re-runs, so CMS state — including deletions — stays authoritative. The gate is SEPARATE from the whole-catalog SeedIfEmpty gate, so the infra tiers seed independently of the 95-row hanzo product snapshot.

Types

type AdminCatalog added in v1.49.7

type AdminCatalog struct {
	Brand      string      `json:"brand"`
	Categories []Category  `json:"categories"`
	Products   []AdminItem `json:"products"`
}

AdminCatalog is the projection returned by GET /v1/commerce/admin/catalog. It mirrors Catalog but its products carry cost + marginPct so admin.hanzo.ai can administrate margin.

func ProjectAdmin added in v1.49.7

func ProjectAdmin(db *datastore.Datastore, brand string) (AdminCatalog, error)

ProjectAdmin returns the admin brand-scoped catalog: the public projection PLUS cost + marginPct on every entry. Callers MUST gate this on owner=="admin"; the projection itself only adds the administrative economics. MarginPct falls back to the derived (price-cost)/price margin when the entry stores no explicit override.

type AdminItem added in v1.49.7

type AdminItem struct {
	Item
	CostCents currency.Cents `json:"costCents"`
	MarginPct float64        `json:"marginPct"`

	// Markup is the entry's retail multiple over cost, and AdminRates is the
	// per-component cost/price/margin the CTO reads "on each / all".
	Markup     string          `json:"markup,omitempty"`
	AdminRates []AdminRateView `json:"adminRates,omitempty"`
}

AdminItem is the admin projection of a CatalogEntry: the full public Item PLUS the administrative economics (cost + margin) the public projection withholds. Only the owner=="admin" admin catalog emits it, so upstream cost and target margin never reach a public reader.

type AdminRateView added in v1.49.25

type AdminRateView struct {
	RateView
	Cost      string   `json:"cost,omitempty"`
	MarginPct *float64 `json:"marginPct,omitempty"`
}

AdminRateView is the admin projection of one Rate: the retail price PLUS the upstream cost we pay and the resulting margin. MarginPct is a pointer so an uncomputable margin is an absent field, never a fabricated 0 — and a NEGATIVE margin is shown plainly rather than suppressed, because selling under cost is a decision that must be visible.

type Catalog

type Catalog struct {
	Brand      string     `json:"brand"`
	Categories []Category `json:"categories"`
	Products   []Item     `json:"products"`
}

Catalog is the full PUBLIC projection returned by GET /v1/commerce/catalog. `products` is the CatalogEntry[] @hanzo/products consumes; brand + categories are additive envelope fields. It carries metadata + the public price (Item.PriceCents) + the plan reference (Item.PricingId) + the public pricing block (Item.Pricing) — NEVER cost or margin.

func Project

func Project(db *datastore.Datastore, brand string) (Catalog, error)

Project returns the PUBLIC brand-scoped catalog: metadata + public price + plan reference + public pricing block, NEVER cost or margin.

type CatalogEntry

type CatalogEntry struct {
	mixin.Model[CatalogEntry]

	Slug        string `json:"slug"`                             // stable id / path segment, e.g. "gateway"
	Name        string `json:"name"`                             // "Gateway"
	Category    string `json:"category"`                         // one of the 10 canonical categories
	Description string `json:"description" datastore:",noindex"` // one-line (additive; not in the core contract)
	Gcp         string `json:"gcp,omitempty"`                    // GCP product it stands in for

	// Presentation KEYS (CMS-editable; @hanzo/products resolves them).
	IconKey    string `json:"iconKey"`    // lucide export name, e.g. "Network"
	BrandColor string `json:"brandColor"` // swatch key, e.g. "blue"

	Route     string `json:"route"`               // marketing "/<slug>"
	DocsUrl   string `json:"docsUrl"`             // https://docs.hanzo.ai/docs/services/<slug>
	ApiPath   string `json:"apiPath"`             // /v1-prefixed path, "/v1/<slug>"
	ApiRoute  string `json:"apiRoute,omitempty"`  // host-qualified "api.hanzo.ai/v1/<slug>"
	GithubUrl string `json:"githubUrl,omitempty"` // source runtime repo URL
	External  bool   `json:"external,omitempty"`  // leaf that links out to another brand surface

	// Pricing is the public pricing block; Private is the admin-only unit
	// economics (cost + margin) — stored as noindex blobs, projected apart:
	// Pricing rides the public projection, Private only the super-admin list.
	Pricing  *Pricing   `json:"pricing,omitempty" datastore:"-"`
	Pricing_ string     `json:"-" datastore:",noindex"`
	Private  *Economics `json:"private,omitempty" datastore:"-"`
	Private_ string     `json:"-" datastore:",noindex"`

	// Rates is the entry's price VECTOR — the one way a price is expressed. A
	// model charges per input/output/cache component and per context rung; a VM
	// charges one amount per month. Both are Rates; the VM is the one-element
	// case. Each rate carries the upstream COST (synced) and the retail PRICE
	// (set in admin), so margin is derived and displayed per component.
	//
	// PriceCents/CostCents below are the LEGACY SCALAR form, still written by
	// the infra-tier seed and read by the pricing service. Every projection goes
	// through RatesOf, which synthesizes the one-element vector from them, so a
	// reader has exactly one shape regardless of which form a row was written
	// in. New writers use Rates.
	Rates  []Rate `json:"rates,omitempty" datastore:"-"`
	Rates_ string `json:"-" datastore:",noindex"`

	// Markup is the retail multiple over a synced upstream cost, as an exact
	// decimal string ("1.20"), applied to any rate that carries no explicit
	// Price. Empty ⇒ DefaultMarkup. It is PER ENTRY and editable — never a
	// global constant buried in a service's env, which is a margin nobody can
	// read.
	Markup string `json:"markup,omitempty"`

	// Spec is the model DESCRIPTOR + routing policy, present only on model rows
	// (see model.go). Stored as a noindex blob like Pricing/Private.
	Spec  *ModelSpec `json:"spec,omitempty" datastore:"-"`
	Spec_ string     `json:"-" datastore:",noindex"`

	// PricingId references a pricing plan by key (plans/<key>.json); empty ⇒
	// projected as JSON null. PriceCents is an optional native fixed-price
	// override (additive to the contract) — the PUBLIC price a customer pays.
	PricingId  string         `json:"pricingId"`
	PriceCents currency.Cents `json:"priceCents,omitempty"`
	Currency   currency.Type  `json:"currency,omitempty"`

	// CostCents is the platform's own unit cost for this product (what Hanzo
	// pays upstream) and MarginPct is the target gross margin over it. Both are
	// administrative economics — the margin surface admin.hanzo.ai edits (HIP-0106)
	// — and are projected ONLY by the owner=="admin" admin catalog, NEVER by the
	// public projection. When MarginPct is unset the admin projection derives it
	// from (PriceCents-CostCents)/PriceCents so a stored override and a computed
	// value read the same way.
	CostCents currency.Cents `json:"costCents,omitempty"`
	MarginPct float64        `json:"marginPct,omitempty"`

	Status string `json:"status" orm:"default:enabled"` // enabled|external|soon
	Repo   string `json:"repo,omitempty"`               // source repo, e.g. "hanzoai/ai"
	Admin  bool   `json:"admin,omitempty"`              // admin-gated surface

	// Brands is a category-derived convenience preserved from the seed (the
	// server filters by category, not by this list). Stored as a noindex blob.
	Brands  []string `json:"brands,omitempty" datastore:"-"`
	Brands_ string   `json:"-" datastore:",noindex"`

	Order     int  `json:"order"`                        // display rank within a category
	Published bool `json:"published" orm:"default:true"` // gates from the public projection

	// ProductId optionally links this catalog surface to a real commerce
	// product for checkout/subscription. Empty = presentation/pricing only.
	ProductId string `json:"productId,omitempty"`

	Metadata  Map    `json:"metadata,omitempty" datastore:"-"`
	Metadata_ string `json:"-" datastore:",noindex"`
}

CatalogEntry is one product in the platform catalog. Slug (== id) is the stable, globally-unique key the entry is addressed by.

func New

func (*CatalogEntry) Load

func (e *CatalogEntry) Load(ps []datastore.Property) (err error)

func (*CatalogEntry) Save

func (e *CatalogEntry) Save() ([]datastore.Property, error)

type Category

type Category struct {
	ID    string `json:"id"`    // slugified label, e.g. "ai"
	Label string `json:"label"` // "AI"
	Order int    `json:"order"` // display rank
}

Category is one taxonomy entry in the projection.

type Economics added in v1.49.7

type Economics struct {
	Cost      string   `json:"cost"`
	MarginPct *float64 `json:"marginPct,omitempty"`
}

Economics is the PRIVATE, admin-only unit economics for a capability. It is NEVER included in the public projection — only the super-admin ListEntries surface returns it. Cost is a display string ("$0.00893 / hour …") or "TODO"; MarginPct is the gross margin percent, nil when not yet computed (so an unknown margin is an absent field, never a fabricated 0).

type InfraSeedRow added in v1.49.9

type InfraSeedRow struct {
	Slug        string                 `json:"slug"`
	Name        string                 `json:"name"`
	Category    string                 `json:"category"`
	Description string                 `json:"description"`
	PriceCents  currency.Cents         `json:"priceCents"`
	Currency    currency.Type          `json:"currency"`
	Order       int                    `json:"order"`
	Metadata    map[string]interface{} `json:"metadata"`
}

InfraSeedRow is one infra-tier seed record: a catalog-entry addressed by a globally-unique slug, carrying its display price (PriceCents) and the full structured spec in the Metadata JSON hatch (vcpus/memoryGB/… for cloud, gpu/vram/price for gpu, replicas/ramGiB/…/usage for datastore). It is a deliberately SMALLER shape than SeedRow — infra tiers are pricing rows, not console modules, so they carry no iconKey/brandColor/route/apiPath.

func InfraSeedRows added in v1.49.9

func InfraSeedRows() ([]InfraSeedRow, error)

InfraSeedRows returns the parsed embedded infra-tier snapshot.

type Item

type Item struct {
	ID         string   `json:"id"` // == Slug (stable, unique)
	Name       string   `json:"name"`
	Category   string   `json:"category"`
	BrandColor string   `json:"brandColor"`
	IconKey    string   `json:"iconKey"`
	Slug       string   `json:"slug"`
	Route      string   `json:"route"`
	DocsUrl    string   `json:"docsUrl"`
	ApiPath    string   `json:"apiPath"`
	ApiRoute   string   `json:"apiRoute,omitempty"`  // host-qualified api.hanzo.ai/v1/<slug>
	GithubUrl  string   `json:"githubUrl,omitempty"` // source runtime repo URL
	PricingId  *string  `json:"pricingId"`           // string OR null
	Brands     []string `json:"brands,omitempty"`    // category-derived convenience

	// Additive (client ignores unknowns).
	Description string         `json:"description,omitempty"`
	Gcp         string         `json:"gcp,omitempty"`
	Status      string         `json:"status,omitempty"`
	Repo        string         `json:"repo,omitempty"`
	External    bool           `json:"external,omitempty"`
	Admin       bool           `json:"admin,omitempty"`
	PriceCents  currency.Cents `json:"priceCents,omitempty"`
	Currency    currency.Type  `json:"currency,omitempty"`
	Order       int            `json:"order,omitempty"`
	ProductId   string         `json:"productId,omitempty"`

	// Pricing is the PUBLIC pricing block. Private economics (cost/margin) are
	// deliberately absent here — they ride only the owner=="admin" admin
	// projection (AdminItem), never this public projection.
	Pricing *Pricing `json:"pricing,omitempty"`

	// Rates is the public price VECTOR — retail only, one element per metered
	// component and context rung. Every entry projects it, synthesized from the
	// legacy scalar when a row predates Rates, so a reader has one shape.
	Rates []RateView `json:"rates,omitempty"`

	// Spec is the model descriptor + routing policy, present on model rows. It
	// is projected to EVERY caller: minTier and enabled decide what a caller may
	// USE, never what they may SEE.
	Spec *ModelSpec `json:"spec,omitempty"`

	// Metadata is the structured-spec JSON hatch (the entry's Metadata map),
	// projected verbatim. Empty for console products (omitted); for the infra
	// tiers it carries the machine-readable spec (vcpus/memoryGB/… for cloud,
	// gpu/vram/price for gpu, replicas/ramGiB/…/usage for datastore) that the
	// pricing service maps back into its cloudPlans/gpuTiers/datastore shapes.
	// It is public presentation/pricing data only — never cost or margin.
	Metadata map[string]interface{} `json:"metadata,omitempty"`
}

Item is the public projection of a CatalogEntry — the exact @hanzo/products CatalogEntry shape. Presentation keys (iconKey, brandColor) are strings resolved client-side; pricingId is null when unset. Fields beyond the core contract (gcp, repo, admin, status, description, priceCents, order, productId) are additive — the client ignores unknowns.

type ModelRow added in v1.49.25

type ModelRow struct {
	Slug        string    `json:"slug"`
	Name        string    `json:"name"`
	Description string    `json:"description,omitempty"`
	Spec        ModelSpec `json:"spec"`
	Costs       []Rate    `json:"costs,omitempty"`
	Order       int       `json:"order,omitempty"`
	Metadata    Map       `json:"metadata,omitempty"`
}

ModelRow is what a SYNCER publishes for one model — the seam between the families and commerce. The family repos (enso, zen) and the upstream (OpenRouter) own the STRUCTURE: which SKUs exist, what each means, and the per-rung shape of their cost. Commerce holds the NUMBERS and admin edits them.

Costs is a cost-only rate vector: a syncer states what we PAY, per component and per context rung, and nothing else. UpsertModels enforces that.

type ModelSpec added in v1.49.25

type ModelSpec struct {
	Vendor string `json:"vendor,omitempty"` // plain vendor name: "Meta", never "Meta Llama"
	Family string `json:"family"`           // enso | zen | third-party

	// Routing: which family service answers this slug, and under what id
	// upstream. Routing is DATA on the one catalog — never a hardcoded pin in
	// a router, which is how a model gets listed that nothing can serve.
	Serves   string `json:"serves,omitempty"`   // enso | zen | openrouter | …
	Upstream string `json:"upstream,omitempty"` // model id at Serves, when it differs from Slug

	Modality      string   `json:"modality,omitempty"` // chat | embedding | image | audio | video | rerank
	ContextWindow int      `json:"contextWindow,omitempty"`
	MaxOutput     int      `json:"maxOutput,omitempty"`
	Capabilities  []string `json:"capabilities,omitempty"` // vision, tools, …

	// MinTier is the entitlement floor ("" | trial | paid). It gates USE, and
	// is projected to every caller so the reason is honest and legible.
	MinTier string `json:"minTier,omitempty"`

	// Enabled says the model is routable right now. A listed-but-disabled model
	// (upstream withdrawn, capacity pulled) stays VISIBLE with its price and
	// Unavailable set — deleting the row would break the billing history that
	// references it.
	Enabled     bool   `json:"enabled"`
	Unavailable string `json:"unavailable,omitempty"` // honest reason when !Enabled
}

ModelSpec is the model DESCRIPTOR: what the model is, and the routing policy that says exactly how it may be used. It rides one entry as a noindex blob, alongside the generic price vector.

VISIBILITY AND ENTITLEMENT ARE DIFFERENT THINGS and this type keeps them apart. CatalogEntry.Published is the ONLY hide switch and exists for a genuinely private entry (a BYO-key model scoped to one org). Everything else — MinTier, Enabled, Unavailable — decides what a caller may USE and what they are CHARGED, never what they may SEE. A model a caller cannot yet afford is an upsell: it appears with its price and an honest reason, never omitted. A customer seeing fewer models than an anonymous visitor is the bug, not the feature.

type Pricing added in v1.49.7

type Pricing struct {
	PublicPrice string   `json:"publicPrice"`
	PlanTiers   []string `json:"planTiers,omitempty"`
	UsageMeter  string   `json:"usageMeter,omitempty"`
}

Pricing is the PUBLIC pricing block for a capability — projected to everyone. PublicPrice is a human display string ("From $0.10 / 1M input tokens"), or the literal "TODO" when a real number is not yet sourced (never a fabricated one). PlanTiers are subscription plan keys (api/billing/plans/<key>.json) the capability is included in; UsageMeter is the metering unit ("per_mtok").

type Rate added in v1.49.25

type Rate struct {
	Key        string `json:"key,omitempty"`        // "" (single) | in | out | cacheRead | cacheWrite
	Unit       string `json:"unit"`                 // mtok | month | hour | image | call | second
	MaxContext int    `json:"maxContext,omitempty"` // rung ceiling this rate applies up to; 0 = all
	Cost       string `json:"cost,omitempty"`       // upstream unit cost, USD — SYNCED
	Price      string `json:"price,omitempty"`      // retail unit price, USD — SET IN ADMIN
}

Rate is ONE metered component of an entry's economics, in ONE unit.

A price is a VECTOR, not a scalar: a model charges separately for input, output and cached reads, and may charge a different amount above a context rung. A VM is the degenerate one-element case (one rate, unit "month"). One shape expresses both, so there is exactly one way to read a price.

Cost is what WE pay upstream — written only by a sync, never by hand. Price is what the CUSTOMER pays — set in admin.hanzo.ai, never by a sync. Margin is DERIVED from the two and displayed; it is never stored as a separate truth. When Price is empty the retail price is Cost x the entry's Markup, so a long tail of synced models needs no per-row hand-pricing.

Amounts are exact decimal strings in USD per Unit. They are NOT cents: currency.Cents is an int64 and cannot represent $0.000015 per token, and a float would lose the upstream's published precision.

func RatesOf added in v1.49.25

func RatesOf(e *CatalogEntry) []Rate

RatesOf returns an entry's price vector: the stored Rates when it has them, else the degenerate vector synthesized from its scalar PriceCents/CostCents. This is the ONE read path for a price — every projection goes through it, so a model and a VM are read the same way.

func (Rate) RetailPrice added in v1.49.25

func (r Rate) RetailPrice(markup string) string

RetailPrice returns what the customer pays for this rate: the explicit Price when an admin set one, else Cost x markup. Empty when neither is known — an unknown price is an absent field, never a fabricated 0.

type RateView added in v1.49.25

type RateView struct {
	Key        string `json:"key,omitempty"`
	Unit       string `json:"unit"`
	MaxContext int    `json:"maxContext,omitempty"`
	Price      string `json:"price,omitempty"`
}

RateView is the PUBLIC projection of one Rate: what the customer pays, per unit and per context rung. Cost and margin are deliberately absent — they ride only AdminRateView.

type SeedRow

type SeedRow struct {
	ID         string     `json:"id"`
	Name       string     `json:"name"`
	Category   string     `json:"category"`
	BrandColor string     `json:"brandColor"`
	IconKey    string     `json:"iconKey"`
	Slug       string     `json:"slug"`
	Route      string     `json:"route"`
	DocsUrl    string     `json:"docsUrl"`
	ApiPath    string     `json:"apiPath"`
	ApiRoute   string     `json:"apiRoute"`
	GithubUrl  string     `json:"githubUrl"`
	External   bool       `json:"external"`
	PricingId  string     `json:"pricingId"` // null → ""
	Pricing    *Pricing   `json:"pricing"`
	Private    *Economics `json:"private"`
	Brands     []string   `json:"brands"`
	Repo       string     `json:"repo"`
	Admin      bool       `json:"admin"`
	Status     string     `json:"status"`
	Gcp        string     `json:"gcp"`
}

SeedRow is the @hanzo/products snapshot shape (the exact CatalogEntry contract). `id` == `slug`; `pricingId` may be JSON null (→ "").

func HanzoSeedRows

func HanzoSeedRows() ([]SeedRow, error)

HanzoSeedRows returns the parsed embedded snapshot.

type SyncStats added in v1.49.25

type SyncStats struct {
	Created int `json:"created"`
	Updated int `json:"updated"`
}

SyncStats reports what a sync did, so a run is legible without diffing rows.

func UpsertModels added in v1.49.25

func UpsertModels(db *datastore.Datastore, rows []ModelRow) (SyncStats, error)

UpsertModels lands a syncer's view of the model catalog.

THE ONE RULE, and the reason this function exists rather than a generic upsert: A SYNC OWNS COST, ADMIN OWNS PRICE. On an existing entry it refreshes the upstream cost and the machine-observable facts (what the model is, where it routes) and touches NOTHING else — not Price, not Markup, not MinTier, not Published, not Name. An upstream price move therefore surfaces as a margin moving in admin.hanzo.ai, and can never silently change what a customer pays.

The rate SET follows upstream (a withdrawn context rung disappears, a new one appears) but each surviving rate keeps the Price a human put on it, matched by rate identity (key, unit, rung).

A row for a slug that does not exist yet is CREATED whole — including its policy defaults — because at birth there is no human decision to preserve.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL