Skip to content

fix some bugs#5

Merged
CSCITech merged 2 commits into
mainfrom
cscitech
Jun 30, 2026
Merged

fix some bugs#5
CSCITech merged 2 commits into
mainfrom
cscitech

Conversation

@CSCITech

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

本次修复了几处后端边界问题:

  • 为环境变量配置补齐包级默认值,避免部分常量在 InitEnv 执行前仍为零值,导致流式超时、文件大小限制、任务轮询等配置行为不一致。
  • 调整日志重试标记回填逻辑,将迁移阶段的同步回填改为异步调度,并在读取重试日志前兜底完成回填,避免启动迁移被历史日志阻塞,同时保证重试日志筛选仍能命中旧数据。
  • 修正日志统计筛选逻辑,使 SumUsedQuota 能正确应用时间范围、日志类型和重试过滤,避免 RPM/TPM 与额度统计口径不一致。
  • 修复流式响应处理在 STREAMING_TIMEOUT=0 时会创建非法 ticker 的问题,允许显式关闭流式超时。
  • 为异常结束且上游未返回 usage 的流式请求增加预扣额度兜底结算,避免超时、客户端断开、扫描错误等场景下产生零额度消费记录。

这些改动通过在写入日志时同步 is_retry、读取前检查回填完成状态、统一统计查询条件,以及基于 StreamStatus 判断异常流式结束来生效。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • Closes #(请补充对应 Issue 编号,如无则移除此行)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

本地验证结果:

go test ./constant
# ok github.com/MAX-API-Next/MAX-API/constant

go test ./model -run "Test(GetAllLogsRetryFilter|GetUserLogsRetryFilter|SumUsedQuota|RetryFilter|BackfillLogRetryMarker|MigrateLOGDBSchedulesRetryMarkerBackfill|ScheduleLogRetryMarkerBackfill|LogRetryMarker)"
# ok github.com/MAX-API-Next/MAX-API/model 26.047s

go test ./service -run "Test(StreamFallbackQuota|PostTextConsumeQuotaUpdatesUsageStatsForStreamFallback|CalculateTextQuotaSummary)"
# ok github.com/MAX-API-Next/MAX-API/service 23.125s

go test ./relay/helper -run "Test(NewStreamingTimeoutTicker|StreamScannerHandler_AllowsDisabledStreamingTimeout|StreamScannerHandler_StreamStatus_Timeout)$"
# ok github.com/MAX-API-Next/MAX-API/relay/helper 33.270s

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added safer, non-zero default configuration for streaming limits, token behaviors, notifications, and task timing.
    • Streaming timeout can now be cleanly disabled (no ticker/timeout behavior) without breaking request handling.
  • Bug Fixes

    • Improved retry log processing by ensuring retry markers are backfilled before reading, and by applying correct log-type filtering for quota calculations.
    • Quota usage stats are now updated more consistently on abnormal stream ends (including timeout), using fallback quota when needed.
  • Tests

    • Added/updated coverage for default initialization, retry backfill scheduling/backfill readiness, and stream quota fallback behavior.

Walkthrough

This PR updates configuration defaults, makes streaming timeout handling nullable, adds fallback billing for abnormal stream endings, converts log retry marker backfill to async execution, and adjusts retry-filter and quota aggregation behavior.

Changes

Configuration Default Constants

Layer / File(s) Summary
Default constants and initialized vars
constant/env.go, common/init.go, constant/env_test.go
Adds exported Default* constants for timeouts, limits, and feature toggles; initializes exported config vars from those defaults; updates initConstantEnv() to use the default constants; adds a test asserting the env-backed vars match their defaults.

Nullable Streaming Timeout

Layer / File(s) Summary
newStreamingTimeoutTicker helper and nil-safe handler wiring
relay/helper/stream_scanner.go
Introduces newStreamingTimeoutTicker returning (nil, nil) for non-positive timeouts and wires StreamScannerHandler to use it with nil-safe stop, reset, and timeout handling.
Stream scanner and relay test cleanup
relay/helper/stream_scanner_test.go, relay/channel/openai/empty_completion_test.go, relay/channel/openai/image_stream_test.go, relay/channel/gemini/relay_gemini_usage_test.go
Removes global constant.StreamingTimeout overrides from stream-related tests and adds coverage for disabled-timeout behavior and zero-timeout handling.

Stream Fallback Quota Billing

Layer / File(s) Summary
streamFallbackQuota helper and PostTextConsumeQuota integration
service/text_quota.go
Adds streamFallbackQuota for abnormal stream end reasons and gates text quota usage-stat updates with shouldUpdateUsageStats.
PostWssConsumeQuota and PostAudioConsumeQuota integration
service/quota.go
Applies the same fallback and usage-stat gating pattern to WebSocket and audio quota paths.
Stream fallback quota tests
service/text_quota_test.go
Tests fallback quota selection and verifies text quota updates and consume-log output during fallback cases.

Async Log Retry Marker Backfill and SumUsedQuota Fixes

Layer / File(s) Summary
Async backfill scheduling and mutex guard
model/main.go
Adds async scheduling for retry-marker backfill, checks completion state before scheduling, and serializes backfill execution with a mutex.
applyRetryLogFilter pre-read backfill check
model/log.go
Makes retry-filter reads verify backfill completion first and propagate readiness errors.
SumUsedQuota time window and log type filter fixes
model/log.go
Aligns the RPM/TPM query with the main quota query’s time window and log-type rules.
Retry backfill and quota filter tests
model/log_test.go
Updates retry-filter expectations and adds coverage for backfill readiness, async scheduling, completion-marker skipping, and explicit log-type quota aggregation.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PostTextConsumeQuota
  participant streamFallbackQuota
  participant Database

  Client->>PostTextConsumeQuota: stream ends with zero tokens
  PostTextConsumeQuota->>streamFallbackQuota: check end reason and relay info
  streamFallbackQuota-->>PostTextConsumeQuota: fallback quota and applied flag
  PostTextConsumeQuota->>Database: update user/channel usage stats
  PostTextConsumeQuota->>Database: insert consume log with fallback quota
Loading
sequenceDiagram
  participant migrateLOGDB
  participant scheduleLogRetryMarkerBackfill
  participant isLogRetryMarkerBackfillCompleted
  participant gopool
  participant backfillLogRetryMarker

  migrateLOGDB->>scheduleLogRetryMarkerBackfill: schedule after DB migration
  scheduleLogRetryMarkerBackfill->>isLogRetryMarkerBackfillCompleted: check completion marker
  isLogRetryMarkerBackfillCompleted-->>scheduleLogRetryMarkerBackfill: incomplete
  scheduleLogRetryMarkerBackfill->>gopool: Go(backfillLogRetryMarker)
  gopool->>backfillLogRetryMarker: run under logRetryMarkerBackfillMu
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • MAX-API-Next/MAX-API#2: Modifies model/log_test.go around retry filtering and the createRetryFilterLogs fixture, directly overlapping with this PR’s retry-filter test updates.

Poem

🐇 I hop through defaults, bright and new,
Timeout sleeps when set to zero, too.
Backfills run async, logs stay neat,
Fallback quota makes stream endings complete.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague and does not convey the specific backend fixes in this PR. Use a concise title naming the main change, such as environment defaults, log retry backfill, and streaming quota fixes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description directly summarizes the bug fixes and matches the changes in the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cscitech

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@constant/env.go`:
- Around line 16-33: Promote the boolean environment defaults in the env
constants block to dedicated Default* constants so there is a single source of
truth for these values. Update the env-backed variables in constant/env.go to
use those new Default* symbols, then change common/init.go to reference the same
Default* constants instead of raw true/false literals. Keep the affected symbols
consistent across both files, including DifyDebug, ForceStreamOption,
CountToken, GetMediaToken, GetMediaTokenNotStream, UpdateTask,
GenerateDefaultToken, and ErrorLogEnabled.

In `@model/log.go`:
- Around line 103-114: The retry-marker readiness helper currently only logs
failures, so read paths can still run with an incomplete retry-marker state.
Update ensureLogRetryMarkerBackfillCompletedForRead to return an error instead
of swallowing failures from isLogRetryMarkerBackfillCompleted and
backfillLogRetryMarker, then thread that error through applyRetryLogFilter and
the retry-filtered read flow so the query is aborted before using logs.is_retry
= true when readiness cannot be confirmed.

In `@service/text_quota.go`:
- Around line 393-397: The fallback path in text quota handling is missing the
nil-upstream-usage case, so `streamFallbackQuota` in `service/text_quota.go`
should also trigger when `usage == nil` and the stream ended abnormally even if
`summary.Quota` was populated from estimated prompt tokens. Update the logic
around `streamFallbackQuota`, `summary.Quota`, and the abnormal-stream handling
in `RelayInfo`/`StreamStatus` so the no-upstream-usage path uses
`FinalPreConsumedQuota` instead of settling the estimated quota, while keeping
the existing logging and `shouldUpdateUsageStats` behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6c3b94d6-81de-47ba-b6ff-a733ffd090d4

📥 Commits

Reviewing files that changed from the base of the PR and between b60bbcc and e518ab3.

📒 Files selected for processing (14)
  • common/init.go
  • constant/env.go
  • constant/env_test.go
  • model/log.go
  • model/log_test.go
  • model/main.go
  • relay/channel/gemini/relay_gemini_usage_test.go
  • relay/channel/openai/empty_completion_test.go
  • relay/channel/openai/image_stream_test.go
  • relay/helper/stream_scanner.go
  • relay/helper/stream_scanner_test.go
  • service/quota.go
  • service/text_quota.go
  • service/text_quota_test.go
💤 Files with no reviewable changes (3)
  • relay/channel/openai/image_stream_test.go
  • relay/channel/gemini/relay_gemini_usage_test.go
  • relay/channel/openai/empty_completion_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrappers in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly calling encoding/json; encoding/json types like json.RawMessage and json.Number may still be used as types.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+ simultaneously: prefer GORM methods over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use cross-DB quoting/boolean helpers when raw SQL is unavoidable, avoid DB-specific functions/operators without fallbacks, and ensure migrations work on all three databases.
For request structs parsed from client JSON and later re-marshaled to upstream providers, especially in relay/convert paths, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved and omitted-only fields remain nil.

**/*.go: Use common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, and common.GetJsonType from common/json.go for all JSON marshal/unmarshal operations; do not directly call encoding/json in business code.
Keep all database code fully compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless cross-DB-safe, and use the provided DB-specific helpers/flags for quoting, booleans, and branching.
For request structs parsed from client JSON and re-marshaled to upstream providers, especially in relay/convert paths, use pointer types with omitempty for optional scalar fields so explicit zero/false values are preserved.

Files:

  • constant/env_test.go
  • common/init.go
  • relay/helper/stream_scanner.go
  • service/text_quota.go
  • service/text_quota_test.go
  • service/quota.go
  • model/log.go
  • constant/env.go
  • model/main.go
  • relay/helper/stream_scanner_test.go
  • model/log_test.go
🔇 Additional comments (9)
relay/helper/stream_scanner.go (1)

43-50: LGTM!

Also applies to: 69-71, 103-105, 247-249, 293-293

relay/helper/stream_scanner_test.go (1)

95-100: LGTM!

Also applies to: 626-654

service/text_quota.go (1)

322-344: LGTM!

service/quota.go (1)

217-232: LGTM!

Also applies to: 346-361

service/text_quota_test.go (1)

10-10: LGTM!

Also applies to: 72-178

constant/env_test.go (1)

5-31: LGTM!

model/main.go (1)

15-15: LGTM!

Also applies to: 32-37, 325-325, 407-434

model/log.go (1)

721-725: LGTM!

Also applies to: 742-748

model/log_test.go (1)

36-38: LGTM!

Also applies to: 51-54, 72-89, 139-220, 349-430, 569-582

Comment thread constant/env.go
Comment thread model/log.go Outdated
Comment thread service/text_quota.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
model/log_test.go (1)

388-423: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Seed the legacy retry row before migrateLOGDB().

This test is meant to prove migration schedules backfill without running it inline, but the retry log is inserted after Line 402. If migrateLOGDB() accidentally ran the backfill synchronously, the test would still pass because there was nothing to update yet.

Proposed test tightening
-	logDB := newRetryBackfillTestDB(t)
+	logDB := newRetryBackfillTestDB(t, &Log{})
@@
-	require.NoError(t, migrateLOGDB())
-	require.Len(t, scheduled, 1)
-
 	log := Log{
 		UserId: 1,
 		Type:   LogTypeConsume,
@@
 	require.NoError(t, LOG_DB.Create(&log).Error)
 	require.NoError(t, LOG_DB.Model(&Log{}).Where("1 = 1").UpdateColumn("is_retry", false).Error)
+
+	require.NoError(t, migrateLOGDB())
+	require.Len(t, scheduled, 1)
 
 	var reloaded Log
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/log_test.go` around lines 388 - 423, The async backfill test is too
weak because the retry row is created after migrateLOGDB(), so it would still
pass even if the backfill ran synchronously. Move the legacy retry Log seeding
and the reset of is_retry to before migrateLOGDB() in
TestRetryMarkerBackfillAsync (the block using LOG_DB.Create, UpdateColumn, and
migrateLOGDB), then keep the assertions that scheduled contains exactly one
callback and that the row remains false until scheduled[0]() runs. This ensures
the test verifies that migrateLOGDB only schedules the backfill and does not
execute it inline.
service/text_quota.go (1)

388-403: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Gate usage-stat updates on billable quota, not just token count.

shouldUpdateUsageStats := summary.TotalTokens != 0 drops user/channel quota updates for zero-token but still-billable requests in this path, such as fixed-price web/file search or image-generation charges already reflected in summary.Quota. SettleBilling and RecordConsumeLog still run, so persisted usage stats diverge from the actual billed quota.

Suggested fix
-	shouldUpdateUsageStats := summary.TotalTokens != 0
-	if summary.TotalTokens == 0 {
+	shouldUpdateUsageStats := summary.TotalTokens != 0 || summary.Quota != 0
+	if summary.TotalTokens == 0 && summary.Quota == 0 {
 		extraContent = append(extraContent, "上游没有返回计费信息,无法扣费(可能是上游超时)")
 		logger.LogError(ctx, fmt.Sprintf("total tokens is 0, cannot consume quota, userId %d, channelId %d, tokenId %d, model %s, pre-consumed quota %d", relayInfo.UserId, relayInfo.ChannelId, relayInfo.TokenId, summary.ModelName, relayInfo.FinalPreConsumedQuota))
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/text_quota.go` around lines 388 - 403, The usage-stats update gate in
the billing flow is tied only to summary.TotalTokens, which skips quota/stat
updates for zero-token but still billable requests. Update the logic around
shouldUpdateUsageStats in the settlement path so it also considers billed quota
from summary.Quota (including cases like streamFallbackQuota, fixed-price
search, or image charges), and ensure SettleBilling/RecordConsumeLog still align
with the quota that was actually consumed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@model/log_test.go`:
- Around line 388-423: The async backfill test is too weak because the retry row
is created after migrateLOGDB(), so it would still pass even if the backfill ran
synchronously. Move the legacy retry Log seeding and the reset of is_retry to
before migrateLOGDB() in TestRetryMarkerBackfillAsync (the block using
LOG_DB.Create, UpdateColumn, and migrateLOGDB), then keep the assertions that
scheduled contains exactly one callback and that the row remains false until
scheduled[0]() runs. This ensures the test verifies that migrateLOGDB only
schedules the backfill and does not execute it inline.

In `@service/text_quota.go`:
- Around line 388-403: The usage-stats update gate in the billing flow is tied
only to summary.TotalTokens, which skips quota/stat updates for zero-token but
still billable requests. Update the logic around shouldUpdateUsageStats in the
settlement path so it also considers billed quota from summary.Quota (including
cases like streamFallbackQuota, fixed-price search, or image charges), and
ensure SettleBilling/RecordConsumeLog still align with the quota that was
actually consumed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a8a53db7-8ac6-40b7-9e5f-46fe77e238ca

📥 Commits

Reviewing files that changed from the base of the PR and between e518ab3 and 9aaa5cd.

📒 Files selected for processing (7)
  • common/init.go
  • constant/env.go
  • constant/env_test.go
  • model/log.go
  • model/log_test.go
  • service/text_quota.go
  • service/text_quota_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Backend checks
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrappers in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly calling encoding/json; encoding/json types like json.RawMessage and json.Number may still be used as types.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+ simultaneously: prefer GORM methods over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use cross-DB quoting/boolean helpers when raw SQL is unavoidable, avoid DB-specific functions/operators without fallbacks, and ensure migrations work on all three databases.
For request structs parsed from client JSON and later re-marshaled to upstream providers, especially in relay/convert paths, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved and omitted-only fields remain nil.

**/*.go: Use common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, and common.GetJsonType from common/json.go for all JSON marshal/unmarshal operations; do not directly call encoding/json in business code.
Keep all database code fully compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless cross-DB-safe, and use the provided DB-specific helpers/flags for quoting, booleans, and branching.
For request structs parsed from client JSON and re-marshaled to upstream providers, especially in relay/convert paths, use pointer types with omitempty for optional scalar fields so explicit zero/false values are preserved.

Files:

  • common/init.go
  • constant/env_test.go
  • constant/env.go
  • service/text_quota_test.go
  • service/text_quota.go
  • model/log.go
  • model/log_test.go
🔇 Additional comments (5)
constant/env.go (1)

14-21: LGTM!

Also applies to: 24-39

common/init.go (1)

150-168: LGTM!

constant/env_test.go (1)

8-22: LGTM!

model/log.go (1)

98-126: LGTM!

Also applies to: 544-547, 632-635, 716-723, 737-741, 758-764

model/log_test.go (1)

4-4: LGTM!

Also applies to: 140-247, 426-457, 504-520

@CSCITech
CSCITech merged commit 804fb19 into main Jun 30, 2026
3 checks passed
This was referenced Jul 2, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jul 12, 2026
11 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant