feat: sync deployed v1.0.0-rc.38-custom.42

Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
cneicy committed 2026-09-26 04:49:11 +08:00
1 parent aa6c3f50f4
commit ceddfd3d6b
828 files changed
+122253 -13702

No files matched your search

+35 -7
View File
@@ -2,15 +2,26 @@
Agent-only issue body. Humans use GitHub issue forms.
Keep every heading. If a section does not apply, write why; do not delete it.
Match the user's language in the filled answers. Quote what the user said; do not invent facts.
Match the user's language in the filled answers. Do not invent facts.
User request: quote the request the user made to the agent as faithfully as
possible. Keep the original language and line breaks. Do not rewrite,
summarize, translate, or turn it into an issue description.
Keep filled answers short and factual. Do not paste unfiltered AI-generated
text, speculation, or an implementation plan in the issue body or in later
comments. Extract the evidence maintainers need to review. Repeated
submissions of unfiltered AI-generated text may result in a block.
If a field below is missing from the conversation, ask the user that question
and wait. Ask for the facts themselves. Do not tell the user to confirm a
template, tick checkboxes, or acknowledge the guidelines.
Before filing, refuse out-of-scope requests listed below. Then search the docs,
DeepWiki, README, and the code. If this is a usage, configuration, or
integration question, answer the user from that material and do not file.
Do not file unless every required condition is met. If the request is out of
scope (including pass-through), is a usage/configuration/integration question,
is missing required facts, or the body would be unfiltered AI-generated text,
tell the user and **stop**. Do not create the issue. Do not file a partial
issue. Do not fill gaps with speculation.
-->
## Agent
@@ -23,7 +34,12 @@ integration question, answer the user from that material and do not file.
## User request
(verbatim or close paraphrase)
Quote the request the user made to the agent as faithfully as possible.
Keep the original language and line breaks. Do not rewrite, summarize,
translate, or turn it into an issue description.
- Verbatim:
- Later constraints or corrections from the user (quote, or none):
## Out of scope — refuse
@@ -35,7 +51,7 @@ accept it, point them to the right place when there is one, and **do not file**.
- Third-party API wrappers
- Compatibility issues from exposing a Codex endpoint as a general-purpose API through a reverse proxy
- Codex API-specific protocol or behavior treated as standard OpenAI API behavior (confirm with the channel or API provider)
- Pass-through mode forwarding (pass-through forwards as-is; verify upstream yourself)
- Pass-through mode forwarding (do not file: pass-through sends content as-is and does not go through new-api processing logic)
- Relay reports that only paste an upstream error, with no direct-upstream vs new-api comparison
- Third-party hosting sites, relay services, or API services (contact their operator)
- Usage, configuration, or integration questions (answer from docs and code instead)
@@ -43,6 +59,17 @@ accept it, point them to the right place when there is one, and **do not file**.
- Matched: yes/no
- If yes, what was told to the user (stop here; do not file):
## File gate — do not file unless all are satisfied
If any item fails, tell the user and **stop**. Do not create the issue.
- Out of scope (including pass-through): no
- Usage / configuration / integration (answered instead of filing): no
- Required facts present without invention (actual behavior, impact, frequency, evidence in new-api, applicable type fields): yes
- Body is short and factual; no unfiltered AI-generated text: yes
- File: yes/no
- If no, what was told to the user (stop here):
## Kind
- [ ] Bug
@@ -69,7 +96,7 @@ Search these yourself before filing. Do not send the user to "read the docs firs
## Problem facts
Ask the user for every item and write it down:
Ask the user for every item and write it down. Facts only; do not paste unfiltered AI-generated text:
- Actual behavior:
- Impact:
@@ -134,6 +161,7 @@ Fill every applicable type. Write "not applicable" for the rest. Ask the user fo
## Research
Open the docs and code. Do not write "already checked" without sources.
Do not replace the fields below with unfiltered AI-generated text.
### Docs
+39 -10
View File
@@ -2,7 +2,16 @@
Agent-only PR body. Humans use `.github/PULL_REQUEST_TEMPLATE.md`.
Keep every heading. If a section does not apply, write why; do not delete it.
Match the user's language in the filled answers. Quote what the user said; do not invent facts.
Match the user's language in the filled answers. Do not invent facts.
User request: quote the request the user made to the agent as faithfully as
possible. Keep the original language and line breaks. Do not rewrite,
summarize, translate, or turn it into a PR description.
Keep the body short and factual. Do not paste unfiltered AI-generated text
in the PR body or in later comments. Repeated submissions of this kind may
result in a block. Verification must be commands actually run and what was
observed. Stating only that `go build` or tests passed is not proof.
New features: link an issue; if none exists, file one first with `.agents/github/ISSUE.md`.
Large or directional changes: maintainer agreement on that issue before this PR.
@@ -12,10 +21,11 @@ frequency, evidence that the problem is in new-api, or the applicable
relay / billing / frontend / deployment items, ask the user those questions
and wait. Ask for the facts. Do not tell the user to confirm a template.
Before opening, refuse the same out-of-scope list as `.agents/github/ISSUE.md`
(Coding Plan, reverse-engineered channels, third-party wrappers, Codex reverse-proxy
compatibility, pass-through-only forwarding, third-party hosts, usage questions).
Tell the user and do not open a PR.
Do not open a PR unless every required condition is met. If the change is out
of scope (including pass-through-only forwarding), is a usage/configuration/
integration question, is missing required issue facts, has only `go build` /
tests-passed as verification, or the body would be unfiltered AI-generated
text, tell the user and **stop**. Do not open the PR. Do not open a partial PR.
Then search https://docs.newapi.ai/ , https://deepwiki.com/QuantumNous/new-api ,
the README, and the code. If this is a usage, configuration, or integration
@@ -37,7 +47,12 @@ question, answer the user and do not open a PR.
## User request
(verbatim or close paraphrase)
Quote the request the user made to the agent as faithfully as possible.
Keep the original language and line breaks. Do not rewrite, summarize,
translate, or turn it into a PR description.
- Verbatim:
- Later constraints or corrections from the user (quote, or none):
## Out of scope — refuse
@@ -49,13 +64,25 @@ accept it and **do not open a PR**.
- Third-party API wrappers
- Codex channel-type changes, or compatibility from exposing Codex as a general-purpose API
- Codex API-specific protocol or behavior treated as standard OpenAI API behavior
- Pass-through-only forwarding
- Pass-through-only forwarding (pass-through sends content as-is and does not go through new-api processing logic)
- Third-party hosting sites, relay services, or API services
- Usage, configuration, or integration (answer from docs and code instead)
- Matched: yes/no
- If yes, what was told to the user (stop here; do not open a PR):
## Open gate — do not open unless all are satisfied
If any item fails, tell the user and **stop**. Do not open the PR.
- Out of scope (including pass-through-only forwarding): no
- Usage / configuration / integration (answered instead of opening): no
- Required issue facts present without invention: yes
- Verification is actual commands or steps and observed results, not only `go build` or tests passed: yes
- Body is short and factual; no unfiltered AI-generated text: yes
- Open: yes/no
- If no, what was told to the user (stop here):
## Kind
- [ ] Bug fix
@@ -76,7 +103,8 @@ Take these from the linked issue. If a needed item is empty, ask the user that q
## Change
(what changed, why it works, grounded in the code actually touched)
(what changed, why it works, grounded in the code actually touched.
Short and factual; do not paste unfiltered AI-generated text.)
## Research
@@ -114,7 +142,8 @@ Open them. Do not write "already checked" without sources.
## Verification
Only what was actually run.
Only what was actually run. Do not state only that `go build` or tests passed.
Each item needs the command or steps and the observed result.
- Commands and results:
- Manual steps and observed result:
@@ -133,4 +162,4 @@ Only what was actually run.
- Single focused change: yes/no (if no, why):
- Secrets included: no
- Out of scope (Coding Plan / reverse-engineered channel / third-party wrapper / Codex): no
- Out of scope (Coding Plan / reverse-engineered channel / third-party wrapper / Codex / pass-through-only forwarding): no
+28
View File
@@ -0,0 +1,28 @@
# Billing Rules
Mandatory conventions for quota, pricing, pre-consume, settlement, refund, and usage-quantity code in the Go backend. `AGENTS.md` defines when a task touches billing (see **Billing rules (mandatory read gate)**); any task that matches MUST read this whole file before planning, coding, or reviewing. Tasks that match none of those triggers may skip it. Rules use the same MUST / Do NOT wording as `AGENTS.md` and carry the same weight.
**Billing expression system:** When working on tiered/dynamic billing (expression-based pricing), MUST read `pkg/billingexpr/expr.md` first. It documents the design philosophy, expression language, full architecture, token normalization rules, quota conversion, and expression versioning. All billing expression changes must follow that document.
**Built-in model pricing:** New built-in model prices MUST be defined as self-contained billing expressions in `setting/billing_setting/builtin_billing.go`, using real USD per million tokens. Do not add new built-in prices to the legacy model/completion/cache ratio tables. Preserve explicit administrator pricing overrides. Existing legacy prices are migrated only when explicitly requested. Verify published prices and cover applicable context-length thresholds and cache categories.
**Billing safety invariants:** Quota/billing code MUST never produce a negative charge (a credit) from arithmetic overflow or unvalidated input. Apply defense in depth:
- Every user-controlled quantity that becomes a billing multiplier (image `n`, video `seconds`/`duration`, resolution/quality ratios, batch counts) MUST be bounded before it reaches quota calculation. Reject out-of-range values at request validation with a 400. Existing bounds: `dto.MaxImageN` for image generation count, `relaycommon.MaxTaskDurationSeconds` for task video duration, `maxTokensLimit` (`relay/helper/valid_request.go`) for `max_tokens`-family fields on every relay format (OpenAI, Claude, Gemini, Responses). Reuse these constants instead of introducing new ad hoc limits for the same concepts. When adding a new relay format or request DTO, bound its max-tokens and count fields in its validator from day one.
- Watch for validation bypass paths: passthrough fields (e.g. `Extra["parameters"]`), task `metadata` maps, and multipart form fields can carry the same quantities around the standard DTO validation. Any adaptor that reads a multiplier from such a path must enforce the same bound (or clamp) locally.
- Durations parsed from media metadata are user/upstream-controlled too: audio file headers (transcription token counting, TTS response duration) and upstream deduction numbers (e.g. Kling `FinalUnitDeduction`) can claim absurd values. Convert them with saturation before they become token counts.
- Never convert a computed quota or token count to `int` with a bare cast like `int(float64(quota) * ratio)`, `int(math.Round(...))` on unbounded input, or `int(decimal.IntPart())`. All quota rounding/conversion is centralized in `common/quota_math.go`; use those helpers: `common.QuotaFromFloat` (truncating) for float products, `common.QuotaRound` (half-away-from-zero) where rounding is intended, and `common.QuotaFromDecimal` for decimal products. `billingexpr.QuotaRound` delegates to `common.QuotaRound`. Do not reintroduce local conversion helpers or bare casts. Single-request saturation stays at the int32 boundary so batch accumulation cannot approach 64-bit wraparound; wallet/top-up conversion uses `common.WalletQuotaFromDecimalStrict` with the JavaScript-safe `common.MaxWalletQuota` boundary. Every clamp/NaN fallback is logged via `common.SysError`.
- Saturation events are also audited: each helper has a `*Checked` variant (`common.QuotaFromFloatChecked` / `QuotaRoundChecked` / `QuotaFromDecimalChecked`) that additionally returns a `*common.QuotaClamp` when clamping occurred. Billing paths that compute a charge capture that clamp onto `relayInfo.QuotaClamp` (or thread it into task settlement) and, right before writing the consume/task log, call `attachQuotaSaturation` (in `service/log_info_generate.go`) which nests the marker under the log's `other.admin_info.quota_saturation` and emits a request-correlated `logger.LogWarn`. Nesting under `admin_info` makes it admin-only for free (non-admin log views strip `admin_info`). When adding a new billing path, use the `*Checked` variant and surface the clamp the same way so the anomaly stays auditable in both the admin log UI and backend logs.
- Multiplier maps go through `types.PriceData.AddOtherRatio`, which rejects non-positive, NaN, and +Inf ratios. Do not write to `PriceData.OtherRatios` directly, and do not weaken these guards.
- Pre-consume (预扣费) and settle (结算/差额) must both be safe: a saturated oversized quota must fail pre-consume with insufficient-quota, never silently wrap. When adding a new billing path (new relay format, new task platform, new adjustment hook), trace the full chain — validation → EstimateBilling/OtherRatios → quota conversion → pre-consume → settle/refund — and confirm each step preserves these invariants.
- Fields parsed into unsigned types (`*uint`) accept huge positive JSON numbers (e.g. `18446744073686646784`, a wrapped negative); a `>= 0` check is not sufficient, an upper bound is mandatory.
- Regression tests for these invariants belong with the boundary they protect (request validators, converter helpers). See `relay/helper/openai_image_request_test.go`, `relay/common/relay_utils_test.go`, and `common/quota_math_test.go` for the expected style.
**Upstream image quantity settlement:** The billable image count may be lowered or raised from the upstream response only through `RelayInfo.UpdateImageCount`; a zero or out-of-range count leaves the reserved request quantity in place. Derive the count from image payloads, never from JSON structure:
- Do NOT count `data` with gjson `data.#`, `len(data)`, or any element count. Providers behind OpenAI-compatible channels return non-standard shapes: `data` as a single object, one image split into a `url` entry and a `b64_json` entry, or entries carrying only `revised_prompt`.
- An object-shaped `data` is one image when it has a non-empty `url` or `b64_json`, otherwise zero. For arrays the count is `max(entries with non-empty url, entries with non-empty b64_json)`: a standard response uses one `response_format` so this equals the entry count, a split image bills once, and payload-less entries bill nothing. The reference implementation is `openaiImageResponseCount` in `relay/channel/openai/relay_image.go`; reuse it (or the same rule) for any channel that forwards or rebuilds OpenAI-format image responses. The xAI adaptor already inherits it through `OpenaiImageHandler`.
- When an OpenAI-format JSON body is re-emitted as SSE, forward one `image_generation.completed` event per image payload using the same shape handling (object → one event, payload-less entries dropped). The client must never receive only `[DONE]` while being charged for images.
- Real SSE streams count `image_generation.completed` / `image_edit.completed` events and only trust a lower count when upstream finished the stream (`done` / `eof`); a client abort must not reduce the charge below the requested quantity.
- Channels with their own usage field (Ali `usage.image_count`) use that value, bounded by `dto.MaxImageN`, and fall back to the converted response length. Channels that rebuild responses from typed structs count the typed slice; none of them may read `data.#`.
- Regression cases for object, split, and payload-less shapes belong in `relay/channel/openai/image_stream_test.go` (fixed-price, tiered-expression, and JSON-as-SSE forwarding tables). Extend those tables instead of adding new files.