| @@ -0,0 +1,164 @@ | |||||
| # Video Pricing Table Testing | |||||
| This guide verifies task video billing with model-level pricing tables. | |||||
| ## Prerequisites | |||||
| - Admin session cookie for `/api/option/model_pricing`. | |||||
| - API token for `/v1/videos`. | |||||
| - A usable Hailuo channel for `hailuo-video`. | |||||
| - A usable Seedance/Doubao channel for `seedance-2`; for deterministic usage billing, point it at a mock upstream that returns `usage.total_tokens`. | |||||
| - `QuotaPerUnit = 500000` unless your environment overrides it. | |||||
| Set shell variables: | |||||
| ```bash | |||||
| BASE_URL=http://127.0.0.1:3000 | |||||
| ADMIN_COOKIE='session=...' | |||||
| API_TOKEN='sk-...' | |||||
| ``` | |||||
| ## Hailuo Per Call | |||||
| Configure an exact per-call matrix: | |||||
| ```bash | |||||
| curl -sS -X PUT "$BASE_URL/api/option/model_pricing/hailuo-video" \ | |||||
| -H "Cookie: $ADMIN_COOKIE" \ | |||||
| -H 'Content-Type: application/json' \ | |||||
| -d '{ | |||||
| "schema_version": 1, | |||||
| "scope": "model", | |||||
| "billing_unit": "per_call", | |||||
| "preconsume_strategy": "exact", | |||||
| "dimensions": [ | |||||
| {"key": "resolution", "source": "request.resolution", "type": "string", "default": null}, | |||||
| {"key": "duration", "source": "request.duration", "type": "number", "default": null} | |||||
| ], | |||||
| "table": [ | |||||
| {"resolution": "768P", "duration": 10, "price": 0.04, "source": "manual"} | |||||
| ], | |||||
| "fallback": {"strategy": "reject"} | |||||
| }' | |||||
| ``` | |||||
| Submit a matched request: | |||||
| ```bash | |||||
| curl -sS -X POST "$BASE_URL/v1/videos" \ | |||||
| -H "Authorization: Bearer $API_TOKEN" \ | |||||
| -H 'Content-Type: application/json' \ | |||||
| -d '{ | |||||
| "model": "hailuo-video", | |||||
| "prompt": "a pricing smoke test", | |||||
| "resolution": "768P", | |||||
| "duration": 10 | |||||
| }' | |||||
| ``` | |||||
| Expected quota: | |||||
| ```text | |||||
| actual_quota = price_usd * QuotaPerUnit * group_ratio | |||||
| = 0.04 * 500000 * 1 | |||||
| = 20000 | |||||
| ``` | |||||
| Submit an unsupported row and expect HTTP 400 with no quota change: | |||||
| ```bash | |||||
| curl -i -X POST "$BASE_URL/v1/videos" \ | |||||
| -H "Authorization: Bearer $API_TOKEN" \ | |||||
| -H 'Content-Type: application/json' \ | |||||
| -d '{ | |||||
| "model": "hailuo-video", | |||||
| "prompt": "a pricing reject test", | |||||
| "resolution": "1080P", | |||||
| "duration": 10 | |||||
| }' | |||||
| ``` | |||||
| ## Seedance Per 1M Tokens | |||||
| Configure a usage matrix: | |||||
| ```bash | |||||
| curl -sS -X PUT "$BASE_URL/api/option/model_pricing/seedance-2" \ | |||||
| -H "Cookie: $ADMIN_COOKIE" \ | |||||
| -H 'Content-Type: application/json' \ | |||||
| -d '{ | |||||
| "schema_version": 1, | |||||
| "scope": "model", | |||||
| "billing_unit": "per_1m_tokens", | |||||
| "preconsume_strategy": "minimum", | |||||
| "dimensions": [ | |||||
| {"key": "resolution", "source": "request.resolution", "type": "string", "default": null} | |||||
| ], | |||||
| "table": [ | |||||
| {"resolution": "720P", "price": 0.5, "source": "manual"} | |||||
| ], | |||||
| "fallback": {"strategy": "reject"} | |||||
| }' | |||||
| ``` | |||||
| Submit: | |||||
| ```bash | |||||
| curl -sS -X POST "$BASE_URL/v1/videos" \ | |||||
| -H "Authorization: Bearer $API_TOKEN" \ | |||||
| -H 'Content-Type: application/json' \ | |||||
| -d '{ | |||||
| "model": "seedance-2", | |||||
| "prompt": "a usage pricing smoke test", | |||||
| "resolution": "720P", | |||||
| "duration": 5 | |||||
| }' | |||||
| ``` | |||||
| Fetch until complete: | |||||
| ```bash | |||||
| TASK_ID=task_xxx | |||||
| curl -sS "$BASE_URL/v1/videos/$TASK_ID" \ | |||||
| -H "Authorization: Bearer $API_TOKEN" | |||||
| ``` | |||||
| When the upstream completion response contains `usage.total_tokens = 1000`, expected final quota is: | |||||
| ```text | |||||
| actual_quota = total_tokens * token_unit_price_usd / 1_000_000 * QuotaPerUnit * group_ratio | |||||
| = 1000 * 0.5 / 1_000_000 * 500000 * 1 | |||||
| = 250 | |||||
| ``` | |||||
| The settlement must use the frozen `token_unit_price_usd` stored on the task billing context. Changing or deleting the current `ModelPricingRules` option after submit must not affect the completion calculation for that task. | |||||
| ## Delete Config And Legacy Fallback | |||||
| Delete a matrix config: | |||||
| ```bash | |||||
| curl -sS -X DELETE "$BASE_URL/api/option/model_pricing/hailuo-video" \ | |||||
| -H "Cookie: $ADMIN_COOKIE" | |||||
| curl -sS -X DELETE "$BASE_URL/api/option/model_pricing/seedance-2" \ | |||||
| -H "Cookie: $ADMIN_COOKIE" | |||||
| ``` | |||||
| After deletion, requests for that model return to the legacy `ModelPrice` path. The `ModelPrice` option itself is not removed or modified by the model pricing table API. | |||||
| ## Smoke Script | |||||
| The same flow can be run with: | |||||
| ```bash | |||||
| "C:/Users/28221/.conda/envs/py312/python.exe" test-scripts/test_video_pricing.py | |||||
| ``` | |||||
| Required environment: | |||||
| ```bash | |||||
| NEW_API_BASE_URL=http://127.0.0.1:3000 | |||||
| NEW_API_ADMIN_USERNAME=root | |||||
| NEW_API_ADMIN_PASSWORD=123456 | |||||
| NEW_API_TOKEN=sk-... | |||||
| ``` | |||||