본문으로 건너뛰기
cccskills
무료GitHub에 공개

add-pricing-field

Add a model pricing field throughout Bifrost, from datasheet parsing and database storage to cost calculations, overrides, documentation, and UI controls. Verify calculation and repeat-sync behavior with tests, then check the repository for missed integration points.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

Wire a new model-pricing field (a datasheet cost key like `cost_per_request`, `output_cost_per_video_per_second_720p`, etc.) end-to-end through Bifrost's pricing engine - Options struct, DB table + migration, datasheet sync upsert columns, cost calculation, custom pricing overrides, public API, OpenAPI docs, MDX docs, and the UI override form. Ends with a repo-wide probe to confirm nothing was missed. Invoked with /add-pricing-field <field_name> or /add-pricing-field (asks for the field).

설치 방법 보기

이럴 때 유용합니다

  • Adding a new model pricing field
  • Implementing surcharges or conditional pricing rates
  • Exposing pricing overrides in the UI and documentation
  • Testing that pricing values survive repeated datasheet syncs

포함된 파일(1)

  • SKILL.md15.8 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Add Pricing Field

Add a new per-model pricing field to Bifrost's cost engine so it is parsed from the upstream datasheet, persisted, billed correctly, overridable, and documented — with no silent gap.

The pricing engine has one source-of-truth shape (Options in framework/modelcatalog/datasheet/types.go) that gets mechanically mirrored into ~8 other places. Missing any one of them produces a field that looks wired (compiles, shows up in one API) but silently doesn't bill, doesn't survive the 24h datasheet resync, or can't be overridden — so treat every step below as mandatory, not optional.

Before You Start

Ask (or infer from context) three things about the new field:

  1. Field name — the exact upstream datasheet JSON key (e.g. cost_per_request, output_cost_per_image_above_8_and_8_pixels). This becomes the Go field name (PascalCase) and the DB column name (as-is, snake_case).
  2. Semantics — what usage quantity does it multiply, and is it additive on top of another cost (like a flat per-request surcharge) or does it replace/tier an existing rate (like a pixel-threshold override)? This determines where in cost.go it plugs in — read the existing compute*Cost functions for the closest analog before writing new logic.
  3. Which request type(s) it applies to — drives which compute*Cost function to touch and the UI's requestTypeGroups tagging. At the Go level this includes container (schemas. ContainerCreateRequest), but the override UI's REQUEST_TYPE_GROUPS only has 7 groups (chat/ text/responses, embedding, rerank, audio, image, video, ocr) — there is no dedicated container group. A container-priced field (e.g. code_interpreter_cost_per_session) still needs a requestTypeGroups entry in Step 9, so tag it onto the existing group it's conceptually closest to (that field uses "chat") rather than inventing an unsupported "container" value.

If any of these is unclear from the user's message, ask before writing code — silently guessing the billing semantics of a money field is the one mistake in this skill that isn't easily caught by tests.

Key File Paths

ConcernFileWhat to add
Canonical structframework/modelcatalog/datasheet/types.goField on Options (with json:"..." tag)
Entry↔Table mappingsame file, convertEntryToTablePricing / convertTablePricingToEntryMapping line in both directions
DB tableframework/configstore/tables/modelpricing.goField on TableModelPricing (gorm column: tag)
Migrationframework/configstore/migrations.goNew migrationAdd<X>Column func + registration in the migrations slice
Datasheet sync upsertframework/configstore/rdb.go, pricingSyncUpdateColumnsColumn name string — easy to miss, silently breaks resync
Custom pricing overridesframework/modelcatalog/datasheet/overrides.go, patchPricing{dst: &patched.X, src: override.X} entry
Cost calculationframework/modelcatalog/datasheet/cost.goWire into the relevant compute*Cost function (or computeCostFromInput if it's a flat cross-cutting surcharge)
Public API summaryframework/modelcatalog/modelinfo.go, ApplyModelInfoOnly if the field belongs in schemas.Pricing (prompt/completion/request/image/web_search/internal_reasoning/cache read+write) — check core/schemas/models.go Pricing struct first
OpenAPI sourcedocs/openapi/schemas/management/governance.yaml, PricingPatch schemaNew property with type: number, minimum: 0, description
OpenAPI bundledocs/openapi/openapi.jsonRegenerate — do not hand-edit (see Step 7)
Field reference docsdocs/providers/custom-pricing.mdxRow in the relevant section's table
Architecture excerptdocs/architecture/framework/model-catalog.mdxOptional — file says "excerpt", but keep the "Costs - Other"-style section current when touching an adjacent field
UI override typeui/lib/types/governance.ts, PricingOverridePatchfield_name?: number;
UI override formui/app/workspace/custom-pricing/overrides/pricingFields.ts, PRICING_FIELDS{ key, label, group, requestTypeGroups } entry
Testsframework/modelcatalog/datasheet/cost_test.go, overrides_test.goOne cost-calculation test, one patchPricing test

Workflow

Step 1: Confirm Semantics Against the Closest Existing Field

Before writing anything, grep for the field family this belongs to (image / video / audio / cache / tiered-token / flat-fee) and read its existing compute*Cost handling in cost.go end to end. Prefer the closest existing pattern. If no pattern matches, stop and ask for confirmation before introducing a new billing path — do not force the field into an ill-fitting pattern. In particular check:

  • Is it additive (billed once regardless of/on top of usage — like search_context_cost_per_query or code_interpreter_cost_per_session)? → wire into the relevant compute function's return, or into computeCostFromInput directly if it applies across every request type.
  • Is it a threshold tier (like output_cost_per_image_above_2048_and_2048_pixels)? → add a case pixels >= threshold && pricing.X != nil: branch in the existing switch in the matching compute*Cost function, ordered from largest threshold to smallest.
  • Is it a rate substitute for an existing base rate under some condition (service tier, region, fast mode)? → follow the tiered*Rate helper pattern already used for priority/flex/fast tiers.

Step 2: Options Struct + Conversions

In framework/modelcatalog/datasheet/types.go:

  1. Add the field to Options, in the section matching its family (// Costs - Image, // Costs - Other, etc.), with the exact upstream JSON tag.
  2. Add the corresponding line to convertEntryToTablePricing (Entry → TableModelPricing).
  3. Add the corresponding line to convertTablePricingToEntry (TableModelPricing → Entry).

Step 3: DB Table + Migration

  1. framework/configstore/tables/modelpricing.go: add the field to TableModelPricing with gorm:"default:null;column:<snake_case>" and matching json tag, in the matching comment section.
  2. framework/configstore/migrations.go:
    • Add a new migrationAdd<Name>Column function, modeled on migrationAddOCRPricingColumns — use addColumnIfNotExists / dropColumnIfExists, the configstore package-local aliases for migrator.AddColumnIfNotExists / migrator.DropColumnIfExists, never a bare ALTER TABLE.
    • Register it as a new entry at the end of the migrations slice: {IDs: []string{"add_<x>_column"}, run: migrationAdd<Name>Column}.

Step 4: Datasheet Sync Upsert Columns — Do Not Skip

In framework/configstore/rdb.go, add the column name to pricingSyncUpdateColumns, in the comment section matching its family. This is the step most likely to be silently forgotten: Create() on a brand-new row writes every column, so a fresh sync looks fine in testing — the bug only shows up on the second sync of an existing model, when ON CONFLICT DO UPDATE silently drops the field because it isn't in the explicit update-column list. If you skip this, the field works until the next 24h resync, then quietly reverts to null forever.

Step 5: Custom Pricing Overrides

In framework/modelcatalog/datasheet/overrides.go, add {dst: &patched.X, src: override.X} to the field list in patchPricing. No handler changes are needed — CreatePricingOverrideRequest/ UpdatePricingOverrideRequest embed Options generically via the Patch field.

Step 6: Cost Calculation

Wire the field into cost.go per the semantics decided in Step 1. If it's a flat, cross-cutting surcharge (bills once per request regardless of type), add it in computeCostFromInput after the per-request-type switch, additive on the switch's result — but leave the default: branch (unrecognized request types) returning 0 unconditionally, so an unmapped type never gets billed just because a pricing row happens to carry the new field.

computeCostFromInput is not on every path, though: calculateCostWithCache's direct-cache-hit branch returns 0 before reaching it (no provider call happened at all), and its semantic-cache-hit branch bills only computeCacheEmbeddingCost, bypassing computeCostFromInput entirely. A flat surcharge wired only into computeCostFromInput therefore never fires on either cache-hit path. Whether that's correct depends on what the field means — "per LLM call" (skip on cache hits, no LLM call was made) vs. "per billed request regardless of cache" (should still fire). Don't assume either answer silently: state the two cache-hit branches' behavior to the user and confirm which one the new field should have before finalizing the wiring.

Step 7: Public API

Always check core/schemas/models.go's Pricing struct against the new field. It intentionally exposes only a handful of fields (Prompt, Completion, Request, Image, WebSearch, InternalReasoning, InputCacheRead, InputCacheWrite) — not every Options field belongs here. If the new field maps onto one of these existing (possibly still-unpopulated) slots, populate it in ApplyModelInfo (framework/modelcatalog/modelinfo.go) via formatCost. If it doesn't map to any existing slot, do not add a new field to the public Pricing struct without the user explicitly asking for a wider public surface — but the check itself is not optional.

Step 8: OpenAPI + Docs

  1. docs/openapi/schemas/management/governance.yaml — add the property to the PricingPatch schema (type: number, minimum: 0, plus description if the field's meaning isn't self-evident from its name).
  2. Regenerate the bundle — never hand-edit openapi.json. Run the bundler in a subshell so the working directory doesn't leak into the following diff, then inspect content (not just --stat, which won't confirm which lines changed) from the repo root:
    (cd docs/openapi && python3 bundle.py)
    git diff -- docs/openapi/openapi.json
    
    Confirm only the new field's lines appear in the diff.
  3. docs/providers/custom-pricing.mdx — add a row to the field-description table in the matching section (Text/Cache/Image/Audio-Video/Other/OCR).
  4. docs/architecture/framework/model-catalog.mdx — this Go excerpt is explicitly non-exhaustive about the full Options struct, but always add the new field to the section it belongs to so the excerpt doesn't drift stale relative to the fields it does list.

Per house convention, explain the exact doc lines you're about to add and get a quick confirmation before writing to .mdx/.yaml files — unless the user's request already explicitly named docs as in-scope for this change.

Step 9: UI

  1. ui/lib/types/governance.ts — add field_name?: number; to PricingOverridePatch, in the matching comment section.
  2. ui/app/workspace/custom-pricing/overrides/pricingFields.ts — add an entry to PRICING_FIELDS: key (exact JSON field name), a short human label, group (which visual section it renders under — usually matches an existing sibling field's group), and requestTypeGroups (which of chat/embedding/rerank/audio/image/video/ocr it applies to, per Step 1's semantics — list every group it can price for a cross-cutting flat fee).

Step 10: Tests

  • framework/modelcatalog/datasheet/cost_test.go — one test exercising the new field through Store.CalculateCost end-to-end (build a pricing row, a response, assert the dollar amount), modeled on the nearest existing TestCalculateCost_* test.
  • framework/modelcatalog/datasheet/overrides_test.go — one patchPricing test asserting the new field passes through from Options to TableModelPricing.
  • framework/configstore/rdb_test.go — a regression test for the sync-upsert path itself, since neither test above would catch a forgotten pricingSyncUpdateColumns entry (Step 4's top risk). Model it on TestUpsertModelPricesBatch_SQLite: upsert a row, re-upsert it with the new field set to a non-null value, then assert the re-fetched row still has it — this is exactly the ON CONFLICT DO UPDATE path that silently drops columns missing from the update-column list.

Step 11: Build + Test

set -euo pipefail
cd framework && go build ./... && go test ./modelcatalog/... ./configstore/...
cd ../transports && go build ./...
cd ../ui && ./node_modules/.bin/tsc --noEmit -p tsconfig.json

Run tsc bare, not piped through grep — a filter on the output launders tsc's own exit code into grep's, so a real compiler failure with no matching text would falsely report success (and a clean run with no matching text would falsely report failure). If you want to scan the output for just the touched files, capture it to a variable first and check tsc's exit status separately:

tsc_out=$(./node_modules/.bin/tsc --noEmit -p tsconfig.json 2>&1); tsc_status=$?
echo "$tsc_out" | grep -i "pricingFields\|governance.ts" || true
[ "$tsc_status" -eq 0 ]

Step 12: Repo-Wide Probe (Do Not Skip)

Before calling this done, grep the whole repo for a sibling field already known to be fully wired (e.g. search_context_cost_per_query or code_interpreter_cost_per_session) and check every hit — this catches fixture files, example configs, helm values, and any other place that enumerates pricing fields you didn't think to check:

grep -rln "search_context_cost_per_query\|SearchContextCostPerQuery" \
  --include="*.go" --include="*.ts" --include="*.tsx" --include="*.json" \
  --include="*.yaml" --include="*.yml" --include="*.mdx" --include="*.md" . \
  | grep -v node_modules | grep -v "/out/" | grep -v "\.next/"

For each hit not already covered by Steps 2–9, decide: is it an enumerated schema that needs the new field (fix it), or realistic fixture/example data that's intentionally non-exhaustive (leave it)? State which for anything ambiguous rather than silently skipping it.

Report a final table of every file touched, one row per file, so the user can review the full diff surface before it's committed.


Important Rules

  • Never hand-edit docs/openapi/openapi.json — always regenerate via docs/openapi/bundle.py after editing the source YAML.
  • Never skip pricingSyncUpdateColumns in rdb.go — this is the single most common way a new pricing field silently stops working after the first datasheet resync.
  • Never add a bare ALTER TABLE migration — use the configstore package-local aliases addColumnIfNotExists / dropColumnIfExists (which wrap migrator.AddColumnIfNotExists / migrator.DropColumnIfExists) so concurrent/rolling-deploy migrations stay idempotent.
  • Never widen core/schemas/models.go's public Pricing struct just to surface a new field — that struct is a deliberately small summary; ask the user first if they want it there.
  • Always confirm the default: case in computeCostFromInput's request-type switch keeps returning 0 — an unmapped request type must never get billed just because a resolved pricing row happens to carry the new field.
  • Always finish with the Step 12 repo-wide probe — it is what catches the locations this checklist's authors didn't think of.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Compare Bifrost HTTP API code with OpenAPI, its API specification, to find missing routes and mismatched methods, authentication, parameters, and responses. Get an audit report with code references and recommended fixes.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Find API routes missing from OpenAPI documentation
  • Check documented authentication against middleware behavior
  • Compare request parameters and response formats
maximhq/bifrost9천2026년 10월 20일 업데이트

Prepare Bifrost releases by turning Git history into release summaries and module changelogs. Coordinate version updates across dependent modules, document database migration risks, and include closed issues and outside contributor credits.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Preparing changelogs for a Bifrost release
  • Coordinating version updates across dependent modules
  • Documenting database migration rollback limits
maximhq/bifrost9천2026년 10월 20일 업데이트

Create, update, or review Bifrost feature documentation for Mintlify using MDX, a format combining Markdown and components. Research the interface and backend, validate configuration examples, and organize pages with screenshot placeholders and navigation entries.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Write documentation for a new Bifrost feature
  • Update guides after interface or API changes
  • Validate config.json examples against the schema
maximhq/bifrost9천2026년 10월 20일 업데이트

Turn a Bifrost PR or GitHub issue into API regression tests that catch recurring bugs. Review existing coverage, add cases to the Postman collection, and validate collection processing and provider filtering.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Add regression coverage for a merged PR
  • Turn an issue's reproduction steps into API tests
  • Test streaming and non-streaming responses
maximhq/bifrost9천2026년 10월 20일 업데이트

Update Bifrost Helm charts, the packages used to configure deployments, while keeping settings, schemas, templates, and documentation aligned. Check the published release and group changes into the next unreleased version.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Add Bifrost settings to the Helm chart
  • Check configuration fields missing from the chart
  • Combine changes into one unreleased version
maximhq/bifrost9천2026년 10월 20일 업데이트

Investigate Bifrost GitHub issues by examining reports, related code, and library documentation. Produce an implementation plan with impact analysis and regression tests, including a demonstrated failing test for bugs before requesting approval to fix them.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Investigating the cause of a reported bug
  • Planning implementation for a feature request
  • Assessing effects on APIs, UI, and dependent code
maximhq/bifrost9천2026년 10월 20일 업데이트

maximhq의 스킬 모두 보기

이 스킬의 문제 신고