Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR separates retry markers, adds Responses-to-chat conversion paths and relay handlers, updates Doubao video billing, and expands task protocol request-body configuration across backend and web layers. ChangesRetry Log Classification and Quota Data Dimensions
Estimated code review effort: 4 (Complex) | ~75 minutes OpenAI Responses to Chat Completions Conversion
Estimated code review effort: 4 (Complex) | ~90 minutes Sequence Diagram(s)sequenceDiagram
participant Adaptor
participant OpenAICompat
participant ResponsesBridge
participant Client
Adaptor->>OpenAICompat: ResponsesRequestToChatCompletionsRequest()
OpenAICompat-->>Adaptor: GeneralOpenAIRequest
Adaptor->>ResponsesBridge: OaiChatToResponsesHandler / GeminiResponsesHandler
ResponsesBridge->>OpenAICompat: ChatCompletionsResponseToResponsesResponse()
ResponsesBridge-->>Client: Responses format output
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
model/log.go (1)
504-548: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winUse the persisted log timestamp for quota-data export.
Line 535 captures a second timestamp after
LOG_DB.Create, so quota data can land in a different second/window than the log row. CapturecreatedAtonce before constructingLogand reuse it for both records.🧩 Proposed fix
+ createdAt := common.GetTimestamp() log := &Log{ UserId: userId, Username: username, - CreatedAt: common.GetTimestamp(), + CreatedAt: createdAt, Type: LogTypeConsume, @@ } err := LOG_DB.Create(log).Error @@ if common.DataExportEnabled { - createdAt := common.GetTimestamp() gopool.Go(func() {🤖 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.go` around lines 504 - 548, The quota-data export in the log persistence flow is using a second timestamp instead of the timestamp stored on the Log row. In the log-recording function around the Log struct creation and LogQuotaData call, capture createdAt once before building the Log, set Log.CreatedAt from that shared value, and reuse the same value when populating QuotaDataLogParams.CreatedAt so both records stay aligned.relay/channel/task/doubao/adaptor.go (1)
207-230: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winDo not treat malformed
video_urlvalues as usable video input.
hasUsableVideoInputreturnstrueforbool(true), arbitrary non-empty maps, and all default types. Because Line 207 and Line 226 use it forvideo_url, inputs like"video_url": trueor{"video_url": {"foo": ""}}can incorrectly switch billing to the video-input price bucket. Use a stricter URL-specific validator forvideo_urlfields.Proposed fix
- if videoURL, has := metadata["video_url"]; has && hasUsableVideoInput(videoURL) { + if videoURL, has := metadata["video_url"]; has && hasUsableVideoURL(videoURL) { return true } @@ - if itemMap["type"] == "video_url" && hasUsableVideoInput(itemMap["video_url"]) { + if itemMap["type"] == "video_url" && hasUsableVideoURL(itemMap["video_url"]) { return true } - if videoURL, has := itemMap["video_url"]; has && hasUsableVideoInput(videoURL) { + if videoURL, has := itemMap["video_url"]; has && hasUsableVideoURL(videoURL) { return true } } return false } +func hasUsableVideoURL(value interface{}) bool { + switch v := value.(type) { + case string: + return strings.TrimSpace(v) != "" + case map[string]interface{}: + url, has := v["url"] + return has && hasUsableVideoURL(url) + case map[string]string: + url, has := v["url"] + return has && strings.TrimSpace(url) != "" + default: + return false + } +} + func hasUsableVideoInput(value interface{}) bool { switch v := value.(type) { @@ case map[string]interface{}: if url, has := v["url"]; has { - return hasUsableVideoInput(url) + return hasUsableVideoURL(url) } return len(v) > 0 @@ default: - return true + return false } }Also applies to: 236-257
🤖 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 `@relay/channel/task/doubao/adaptor.go` around lines 207 - 230, The video-input detection logic in the adaptor is too permissive for video_url fields and can misclassify malformed values as usable video input. Update the checks in the video detection helper in adaptor.go so that video_url only passes a URL-specific validation path, while keeping hasUsableVideoInput for the broader video field if appropriate. Make sure the content array handling and the top-level metadata["video_url"] path both use the stricter validator, including the repeated checks in the later block that applies the same logic.
🤖 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 `@model/log_test.go`:
- Around line 85-95: The retry-marker expectations in log_test.go need to
account for the fixture that contains both markers, so it should be included in
both subtype filters. Update the assertions around GetAllLogs for
LogFilterErrorRetry and LogFilterEmptyRetry to reflect the combined-marker
behavior, and make the corresponding stats/expected IDs match the corrected
logOtherRetryMarkers handling in the test setup.
In `@model/log.go`:
- Around line 241-255: The retry marker parsing in logOtherRetryMarkers
currently returns as soon as it sees empty_retry, which prevents retry_log from
being recorded when both markers exist. Update logOtherRetryMarkers to inspect
both flags in the parsed otherMap and set errorRetry and emptyRetry
independently before returning, so an Other payload can be counted as both
IsRetry and IsEmptyRetry when appropriate.
In `@relay/channel/gemini/adaptor_responses.go`:
- Around line 32-53: The Gemini tool filtering in filterGeminiResponsesTools is
silently removing any non-"function" tool types, which drops managed tools
without notice. Update the function to detect unsupported tool entries while
iterating the unmarshaled tools and return an explicit error instead of
appending only matching items; keep the existing handling for valid function
tools, but fail fast when search, code execution, file search, computer use, or
any other non-function type is present so callers can see the unsupported
capability.
In `@relay/channel/task/doubao/adaptor.go`:
- Around line 58-59: The optional request fields in the Doubao adapter struct
are using plain strings, so omitted and empty values cannot be distinguished
when the payload is re-marshaled upstream. Update the request struct in
adaptor.go so Resolution and Ratio use *string with omitempty, and adjust any
reads/assignments in the Doubao request path to safely handle nil pointers while
preserving omitted fields for upstream providers.
In `@service/convert_test.go`:
- Around line 39-51: Add a Gemini coverage test for malformed tool arguments in
ResponseOpenAI2Gemini, mirroring the existing Claude fallback test behavior.
Create a test alongside TestResponseOpenAI2GeminiPreservesTextWithToolCalls that
uses openAIResponseWithToolCallArguments with invalid JSON, then assert
ResponseOpenAI2Gemini returns one candidate whose single FunctionCall wraps the
raw value under "arguments". Use the existing ResponseOpenAI2Gemini and
openAIResponseWithToolCallArguments helpers to locate the logic and keep the
assertion shape consistent with the Claude fallback test.
In `@service/convert.go`:
- Around line 618-634: The tool-argument parsing logic is duplicated between
ResponseOpenAI2Claude and ResponseOpenAI2Gemini and should be centralized.
Extract a shared helper for parsing tool call arguments that uses
common.Unmarshal and falls back to a map containing the raw arguments string
when parsing fails. Update both ResponseOpenAI2Claude and ResponseOpenAI2Gemini
to call that helper so the Claude and Gemini conversion paths stay consistent.
In `@service/openaicompat/chat_to_responses_response.go`:
- Around line 535-557: The helper chatToolCallToResponsesOutput currently
advertises an error return but never produces one, which makes the API
misleading and leaves dead error-handling code in the caller. Remove the unused
error from chatToolCallToResponsesOutput and update its callers in the
chat-to-responses conversion path to treat it as a pure transform, using the
function and related dto.ResponsesOutput construction branches to locate the
change.
In `@service/openaicompat/responses_request_to_chat.go`:
- Around line 194-212: The responsesInputContentToChatContent function has a
dead type switch branch for []map[string]any because JSON-unmarshaled nested
arrays arrive as []any and already hit the existing case above. Remove the
unreachable []map[string]any handling, or adjust the upstream decoding/typing so
this function only handles the actual shapes it can receive; keep the logic
centered on responsesInputContentToChatContent and
responsesContentPartsToChatContent.
- Around line 149-178: `responsesInputItemToChatMessages` and
`responsesFunctionCallItemToChatToolCall` allow tool-call messages to be built
with empty identifiers, which can break upstream correlation. Add explicit
validation that function_call items have a non-empty `call_id`/`id` before
creating the assistant tool call, and reject the input with an error if missing.
Do the same for the `responsesInputTypeFunctionCallOutput` branch by requiring a
non-empty `call_id` before appending the tool message. Apply the same validation
in the other conversion path noted by the review so both code paths consistently
prevent empty tool_call_id values.
In `@service/openaicompat/responses_to_chat.go`:
- Around line 120-147: UsageFromResponsesUsage is dropping several nested
usage-detail fields when converting Responses usage into dto.Usage. Update this
function to mirror UsageFromChatUsage by copying all available input detail
fields (CachedTokens, ImageTokens, AudioTokens, CachedCreationTokens,
TextTokens) and output detail fields (ReasoningTokens, TextTokens, AudioTokens,
ImageTokens) from src into the corresponding PromptTokensDetails and
CompletionTokenDetails. Keep the existing token totals behavior unchanged.
---
Outside diff comments:
In `@model/log.go`:
- Around line 504-548: The quota-data export in the log persistence flow is
using a second timestamp instead of the timestamp stored on the Log row. In the
log-recording function around the Log struct creation and LogQuotaData call,
capture createdAt once before building the Log, set Log.CreatedAt from that
shared value, and reuse the same value when populating
QuotaDataLogParams.CreatedAt so both records stay aligned.
In `@relay/channel/task/doubao/adaptor.go`:
- Around line 207-230: The video-input detection logic in the adaptor is too
permissive for video_url fields and can misclassify malformed values as usable
video input. Update the checks in the video detection helper in adaptor.go so
that video_url only passes a URL-specific validation path, while keeping
hasUsableVideoInput for the broader video field if appropriate. Make sure the
content array handling and the top-level metadata["video_url"] path both use the
stricter validator, including the repeated checks in the later block that
applies the same logic.
🪄 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: 3172d872-4c7c-410e-b6e1-4055da6e9678
📒 Files selected for processing (45)
controller/relay.godto/channel_settings.godto/openai_response.gomodel/log.gomodel/log_test.gomodel/main.gomodel/task.gomodel/usedata.gorelay/channel/advancedcustom/adaptor.gorelay/channel/advancedcustom/adaptor_test.gorelay/channel/claude/relay-claude.gorelay/channel/claude/relay_claude_test.gorelay/channel/gemini/adaptor.gorelay/channel/gemini/adaptor_responses.gorelay/channel/gemini/adaptor_responses_test.gorelay/channel/gemini/relay_gemini_usage_test.gorelay/channel/gemini/relay_responses.gorelay/channel/openai/responses_via_chat.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/doubao/adaptor_media_protocol_test.gorelay/channel/task/doubao/constants.goservice/convert.goservice/convert_test.goservice/openai_chat_responses_compat.goservice/openaicompat/chat_to_responses_response.goservice/openaicompat/responses_compat_test.goservice/openaicompat/responses_request_to_chat.goservice/openaicompat/responses_to_chat.goservice/task_billing.goservice/task_billing_test.goweb/default/src/features/channels/lib/advanced-custom.tsweb/default/src/features/channels/types.tsweb/default/src/features/usage-logs/components/common-logs-filter-bar.tsxweb/default/src/features/usage-logs/constants.tsweb/default/src/features/usage-logs/lib/utils.test.tsweb/default/src/features/usage-logs/lib/utils.tsweb/default/src/features/usage-logs/types.tsweb/default/src/i18n/locales/en.jsonweb/default/src/i18n/locales/fr.jsonweb/default/src/i18n/locales/ja.jsonweb/default/src/i18n/locales/ru.jsonweb/default/src/i18n/locales/vi.jsonweb/default/src/i18n/locales/zh.jsonweb/default/src/i18n/static-keys.tsweb/default/src/routes/_authenticated/usage-logs/$section.tsx
📜 Review details
⚠️ CI failures not shown inline (4)
GitHub Actions: CI / 1_Frontend checks.txt: v1.0.4-preview.1
Conclusion: failure
##[group]Run bun run typecheck
�[36;1mbun run typecheck�[0m
shell: /usr/bin/bash -e {0}
##[endgroup]
$ tsc -b
src/features/usage-logs/lib/utils.ts(254,18): error TS2345: Argument of type 'string' is not assignable to parameter of type '"retry" | "error_retry" | "empty_retry"'.
##[error]Process completed with exit code 2.
GitHub Actions: CI / Backend checks: v1.0.4-preview.1
Conclusion: failure
##[group]Run go run ./tools/jsonwrapcheck
�[36;1mgo run ./tools/jsonwrapcheck�[0m
shell: /usr/bin/bash -e {0}
##[endgroup]
stale json wrapper allowlist entries:
relay/channel/claude/relay-claude.go|RequestOpenAI2ClaudeMessage|Unmarshal|***REDACTED***
Regenerate the baseline with: go run ./tools/jsonwrapcheck -list > tools/jsonwrapcheck/allowlist.txt
exit status 1
##[error]Process completed with exit code 1.
GitHub Actions: CI / Frontend checks: v1.0.4-preview.1
Conclusion: failure
##[group]Run bun run typecheck
�[36;1mbun run typecheck�[0m
shell: /usr/bin/bash -e {0}
##[endgroup]
$ tsc -b
src/features/usage-logs/lib/utils.ts(254,18): error TS2345: Argument of type 'string' is not assignable to parameter of type '"retry" | "error_retry" | "empty_retry"'.
##[error]Process completed with exit code 2.
GitHub Actions: CI / 0_Backend checks.txt: v1.0.4-preview.1
Conclusion: failure
##[group]Run go run ./tools/jsonwrapcheck
�[36;1mgo run ./tools/jsonwrapcheck�[0m
shell: /usr/bin/bash -e {0}
##[endgroup]
stale json wrapper allowlist entries:
relay/channel/claude/relay-claude.go|RequestOpenAI2ClaudeMessage|Unmarshal|***REDACTED***
Regenerate the baseline with: go run ./tools/jsonwrapcheck -list > tools/jsonwrapcheck/allowlist.txt
exit status 1
##[error]Process completed with exit code 1.
🧰 Additional context used
📓 Path-based instructions (9)
web/default/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/default/AGENTS.md)
web/default/src/**/*.{ts,tsx}: 前端页面文本与组件内文案必须支持 i18n:React 组件中应使用useTranslation()取得t,并通过t()渲染用户可见文本;子组件也应自行使用useTranslation()保持独立性。
禁止使用两层及以上嵌套三元表达式;复杂逻辑应改用if-else、提前返回或抽取函数。
控制函数圈复杂度,复杂逻辑应拆成更小的函数;变量与函数命名应有意义并遵循驼峰等常规命名约定。
TypeScript 代码应避免使用any,优先使用具体类型或unknown;参数与返回值应显式标注类型;仅类型用途的导入应使用import type。
修改 TypeScript 或 TSX 代码后必须执行类型检查,并修复所有类型错误,不得遗留。
对象非必要不要解构,尤其是组件 props;优先直接使用props.xxx以保持代码清晰。
组件应使用函数式组件与 Hooks,遵循单一职责;组件 props 必须有明确类型(接口或类型别名)。
单文件超过约 200 行时应考虑拆分子组件或抽取自定义 Hooks;类型定义可与组件同文件或放在同模块的types中。
在 React 中应合理使用useMemo、useCallback、React.memo,避免在渲染路径中创建新对象或数组;必要时进行代码分割与动态import。
React Query 的数据获取应使用useQuery、变更应使用useMutation;每个查询需配置唯一queryKey,并在成功后对相关查询执行invalidateQueries;服务端错误应统一交给handleServerError。
Axios 请求应使用项目统一的api实例;GET 请求默认去重,特殊请求可显式关闭;认证与通用错误应在拦截器中统一处理。
服务端错误应统一使用handleServerError,展示层应使用toast.error等统一方式;文案需走 i18n;路由级错误应由errorComponent承接;表单错误应通过form.setError等方式映射到字段。
样式应以 Tailwind 工具类为主,动态类名使用cn()合并;非动态场景避免内联样式;响应式采用移动优先与 Tailwind 断点,主题与暗色模式通过 CSS 变量与dark:处理。
应使用语义化 HTML、正确关联label与输入、保证键盘可操作与合理焦点顺序;必要时添加 ARIA 属性,装饰性图标应使用aria-hidden="true"。
认证与权限应在路由与接口层校验;前后端都应做数据校验(如 Zod);敏感信息不得落前端存储;避免使用dangerouslySetInnerHTML;跨域与 Cookie 需配合withCredentials并按后端要求处理 CSRF。
组件测试应使用 React Testing Library,关注交互与行为,避免测试实现细节;关键流程可补充集成与 E2E 测试。
环境变量应通过.env读取,并使用VITE_前缀;代码中不得硬编码密钥。
Files:
web/default/src/routes/_authenticated/usage-logs/$section.tsxweb/default/src/i18n/static-keys.tsweb/default/src/features/channels/types.tsweb/default/src/features/usage-logs/components/common-logs-filter-bar.tsxweb/default/src/features/usage-logs/types.tsweb/default/src/features/usage-logs/lib/utils.test.tsweb/default/src/features/usage-logs/constants.tsweb/default/src/features/channels/lib/advanced-custom.tsweb/default/src/features/usage-logs/lib/utils.ts
web/default/src/routes/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/default/AGENTS.md)
路由应使用 TanStack Router,并通过
createFileRoute定义;搜索参数应使用 Zod schema +validateSearch校验;认证与重定向应放在beforeLoad中;导航应优先使用useNavigate或Link,避免直接操作window.location。
Files:
web/default/src/routes/_authenticated/usage-logs/$section.tsx
web/default/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Use
bunas the preferred package manager and script runner for the frontend inweb/default/(bun install,bun run dev,bun run build, andbun run i18n:*).
Files:
web/default/src/routes/_authenticated/usage-logs/$section.tsxweb/default/src/i18n/static-keys.tsweb/default/src/features/channels/types.tsweb/default/src/features/usage-logs/components/common-logs-filter-bar.tsxweb/default/src/features/usage-logs/types.tsweb/default/src/features/usage-logs/lib/utils.test.tsweb/default/src/i18n/locales/ru.jsonweb/default/src/i18n/locales/zh.jsonweb/default/src/i18n/locales/en.jsonweb/default/src/i18n/locales/vi.jsonweb/default/src/features/usage-logs/constants.tsweb/default/src/i18n/locales/ja.jsonweb/default/src/features/channels/lib/advanced-custom.tsweb/default/src/i18n/locales/fr.jsonweb/default/src/features/usage-logs/lib/utils.ts
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrappers incommon/json.go(common.Marshal,common.Unmarshal,common.UnmarshalJsonStr,common.DecodeJson,common.GetJsonType) instead of directly callingencoding/json;encoding/jsontypes likejson.RawMessageandjson.Numbermay 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 directAUTO_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 withomitemptyso explicit zero/false values are preserved and omitted-only fields remainnil.
**/*.go: Usecommon.Marshal,common.Unmarshal,common.UnmarshalJsonStr,common.DecodeJson, andcommon.GetJsonTypefromcommon/json.gofor all JSON marshal/unmarshal operations; do not directly callencoding/jsonin 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 withomitemptyfor optional scalar fields so explicit zero/false values are preserved.
Files:
controller/relay.gorelay/channel/gemini/adaptor_responses.godto/openai_response.gomodel/task.goservice/task_billing.gorelay/channel/openai/responses_via_chat.goservice/convert_test.gorelay/channel/advancedcustom/adaptor.gorelay/channel/claude/relay-claude.gorelay/channel/gemini/adaptor_responses_test.gorelay/channel/gemini/relay_responses.gorelay/channel/gemini/relay_gemini_usage_test.gomodel/main.gorelay/channel/advancedcustom/adaptor_test.godto/channel_settings.gorelay/channel/gemini/adaptor.gorelay/channel/task/doubao/constants.gorelay/channel/task/doubao/adaptor_media_protocol_test.goservice/convert.goservice/task_billing_test.gorelay/channel/claude/relay_claude_test.goservice/openaicompat/responses_to_chat.gorelay/channel/task/doubao/adaptor.goservice/openaicompat/responses_request_to_chat.goservice/openai_chat_responses_compat.gomodel/log_test.gomodel/usedata.goservice/openaicompat/chat_to_responses_response.gomodel/log.goservice/openaicompat/responses_compat_test.go
relay/channel/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
When implementing a new relay channel, confirm whether the provider supports
StreamOptions; if it does, add that channel tostreamSupportedChannels.When implementing a new channel, confirm whether the provider supports
StreamOptions; if it does, add the channel tostreamSupportedChannels.
Files:
relay/channel/gemini/adaptor_responses.gorelay/channel/openai/responses_via_chat.gorelay/channel/advancedcustom/adaptor.gorelay/channel/claude/relay-claude.gorelay/channel/gemini/adaptor_responses_test.gorelay/channel/gemini/relay_responses.gorelay/channel/gemini/relay_gemini_usage_test.gorelay/channel/advancedcustom/adaptor_test.gorelay/channel/gemini/adaptor.gorelay/channel/task/doubao/constants.gorelay/channel/task/doubao/adaptor_media_protocol_test.gorelay/channel/claude/relay_claude_test.gorelay/channel/task/doubao/adaptor.go
web/default/src/features/**
📄 CodeRabbit inference engine (web/default/AGENTS.md)
功能模块应放在
src/features/<feature>/,并按需包含components/、lib/、hooks/、api.ts、types.ts、constants.ts等;通用组件应放在src/components/,通用工具与类型应放在src/lib/。
Files:
web/default/src/features/channels/types.tsweb/default/src/features/usage-logs/components/common-logs-filter-bar.tsxweb/default/src/features/usage-logs/types.tsweb/default/src/features/usage-logs/lib/utils.test.tsweb/default/src/features/usage-logs/constants.tsweb/default/src/features/channels/lib/advanced-custom.tsweb/default/src/features/usage-logs/lib/utils.ts
web/default/src/features/**/lib/**/*.ts
📄 CodeRabbit inference engine (web/default/AGENTS.md)
表单应使用 React Hook Form + Zod:在功能模块的
lib/下定义 schema,并用z.infer导出表单类型;useForm应配合@hookform/resolvers/zod进行校验。
Files:
web/default/src/features/usage-logs/lib/utils.test.tsweb/default/src/features/channels/lib/advanced-custom.tsweb/default/src/features/usage-logs/lib/utils.ts
web/default/src/**/*.test.ts
📄 CodeRabbit inference engine (web/default/AGENTS.md)
工具函数与纯逻辑应优先编写单元测试;测试文件应命名为
*.test.ts。
Files:
web/default/src/features/usage-logs/lib/utils.test.ts
web/default/src/features/**/constants.ts
📄 CodeRabbit inference engine (web/default/AGENTS.md)
各 feature 的
constants.ts中,成功/错误/提示类消息常量应仅保存 i18n 键,展示时必须通过t()渲染;状态/选项的 label 应统一使用labelKey或label(二选一且同一 feature 内保持一致);新增此类常量时应同步登记翻译键。
Files:
web/default/src/features/usage-logs/constants.ts
🪛 GitHub Actions: CI / 1_Frontend checks.txt
web/default/src/features/usage-logs/lib/utils.ts
[error] 254-254: TypeScript (tsc -b) error TS2345: Argument of type 'string' is not assignable to parameter of type '"retry" | "error_retry" | "empty_retry"'.
🪛 GitHub Actions: CI / Frontend checks
web/default/src/features/usage-logs/lib/utils.ts
[error] 254-254: TypeScript (tsc -b) failed with TS2345: Argument of type 'string' is not assignable to parameter of type '"retry" | "error_retry" | "empty_retry"'.
🔇 Additional comments (48)
model/log.go (1)
57-58: LGTM!Also applies to: 72-80, 94-129, 144-165, 553-614, 784-854
model/main.go (2)
30-49: LGTM!Also applies to: 460-474
435-450: 🩺 Stability & AvailabilityCheck startup ordering for the new retry columns.
backfillLogRetryMarkerreadsis_error_retryandis_empty_retry; if it can run before those columns exist, upgraded databases will fail on the select.model/log_test.go (1)
37-39: LGTM!Also applies to: 52-55, 72-75, 182-232, 292-332
relay/channel/task/doubao/constants.go (1)
3-59: LGTM!relay/channel/task/doubao/adaptor_media_protocol_test.go (2)
9-208: LGTM!Also applies to: 244-271
210-223: 🎯 Functional CorrectnessInitialize
TaskAdaptorwith the Seedance Media protocol hereThis test should exercise the
a.useSeedanceMediaProtocol()path inEstimateBilling; if the zero-value adaptor does not enable that mode, it won’t cover the Seedance-specific media handling.controller/relay.go (1)
585-605: LGTM!model/task.go (1)
100-110: LGTM!service/task_billing.go (1)
180-191: LGTM!Also applies to: 244-255
service/task_billing_test.go (1)
187-193: LGTM!Also applies to: 336-381
web/default/src/i18n/locales/zh.json (1)
701-703: LGTM!Also applies to: 1541-1541, 1693-1693, 2219-2220
model/usedata.go (2)
141-161: 🗄️ Data Integrity & IntegrationCheck SUM() scan targets for cross-DB compatibility
model/usedata.go:141-161
sum(count),sum(quota), andsum(token_used)can return NULL or driver-specific numeric types; ensure these aggregate results map safely intointfields across SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+.
100-139: 🗄️ Data Integrity & IntegrationPotential duplicate rows in quota persistence. The lookup-then-create/update flow can race unless
quota_datahas a composite unique constraint over the full 8-field key or this is changed to an atomic upsert. The leftover commented-out block can be removed.web/default/src/features/usage-logs/constants.ts (1)
69-71: LGTM!Also applies to: 110-138
web/default/src/features/usage-logs/components/common-logs-filter-bar.tsx (1)
63-65: LGTM!web/default/src/features/usage-logs/types.ts (1)
42-43: LGTM!web/default/src/routes/_authenticated/usage-logs/$section.tsx (1)
22-22: LGTM!Also applies to: 34-34
web/default/src/i18n/locales/ru.json (1)
701-703: LGTM!Also applies to: 1541-1541, 1693-1693, 2219-2220
web/default/src/i18n/locales/vi.json (1)
701-703: LGTM!Also applies to: 1541-1541, 1693-1693, 2219-2220
web/default/src/features/usage-logs/lib/utils.ts (1)
36-37: LGTM!Also applies to: 101-107, 118-134, 249-257
web/default/src/features/usage-logs/lib/utils.test.ts (1)
36-55: LGTM!Also applies to: 138-202
web/default/src/i18n/static-keys.ts (1)
54-55: LGTM!web/default/src/i18n/locales/en.json (1)
701-703: LGTM!Also applies to: 1541-1541, 1693-1693, 2219-2220
web/default/src/i18n/locales/fr.json (1)
701-703: LGTM!Also applies to: 1541-1541, 1693-1693, 2219-2220
web/default/src/i18n/locales/ja.json (2)
1541-1541: LGTM!Also applies to: 1693-1693
701-703: LGTM!Also applies to: 2219-2220
dto/channel_settings.go (1)
80-80: LGTM!Also applies to: 165-167, 259-262
web/default/src/features/channels/types.ts (1)
144-151: LGTM!service/openaicompat/responses_request_to_chat.go (2)
1-481: LGTM on the remaining conversion logic (messages, tools, tool_choice, response_format, media parts) — verified against the accompanying test suite.
89-107: 🎯 Functional CorrectnessCheck unsupported-field coverage for Responses requests. If
dto.OpenAIResponsesRequestexposes output-shaping fields likeinclude, reject them here or handle them explicitly instead of silently dropping them.service/openai_chat_responses_compat.go (1)
12-34: LGTM!relay/channel/advancedcustom/adaptor.go (1)
106-117: LGTM!Also applies to: 231-235
relay/channel/advancedcustom/adaptor_test.go (1)
9-9: LGTM!Also applies to: 372-408, 433-439
relay/channel/gemini/adaptor.go (1)
15-15: LGTM!Also applies to: 242-251, 257-263
relay/channel/gemini/adaptor_responses_test.go (1)
1-100: LGTM! Test assumptions are consistent with the adaptor.go wiring reviewed in this cohort; the filtering behavior itself lives inadaptor_responses.go, which isn't part of this review batch.web/default/src/features/channels/lib/advanced-custom.ts (1)
46-49: LGTM!Also applies to: 585-587
dto/openai_response.go (1)
336-338: Correct fix — matches real OpenAI schema.OpenAI's Responses API actually names this field
reason(e.g.incomplete_details.reason === "max_output_tokens"), so renaming fromReasoning/reasoningtoReason/reasonfixes a schema mismatch rather than introducing one.Source
If the generated tokens reach the context window limit or the max_output_tokens value you've set, you'll receive a response with a status of incomplete and incomplete_details with reason set to max_output_tokens.
service/openaicompat/chat_to_responses_response.go (1)
61-120: LGTM!Manually traced the streaming state machine against the aggregation test in
responses_compat_test.go(created → text delta → tool added → tool arg delta → done events → completed) and the event ordering/count/usage all line up correctly.Also applies to: 181-226, 228-243
service/openaicompat/responses_compat_test.go (1)
1-324: LGTM!relay/channel/openai/responses_via_chat.go (1)
1-135: LGTM!service/openaicompat/responses_to_chat.go (1)
59-82: 🎯 Functional CorrectnessNeed to confirm the
custom_tool_calloutput shape before treating it asfunctionand readingArgumentsString()here.relay/channel/gemini/adaptor_responses.go (1)
1-30: LGTM!Also applies to: 55-97
relay/channel/gemini/relay_responses.go (1)
1-175: LGTM!relay/channel/gemini/relay_gemini_usage_test.go (1)
8-8: LGTM!Also applies to: 273-329, 379-394
relay/channel/claude/relay-claude.go (1)
464-479: LGTM!relay/channel/claude/relay_claude_test.go (1)
8-8: LGTM!Also applies to: 308-392
service/convert.go (1)
865-870: LGTM!
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/default/src/features/usage-logs/lib/utils.ts (1)
130-146: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPreserve OR semantics for multiple retry subtype selections.
filterValues/typeValuescan be arrays, but the retry branch returns fromerror_retrybefore checkingempty_retry, andbuildApiParamskeeps only the first retry value. Selecting both retry subtypes can therefore omit empty-retry rows/stats. Treat subtype filters as additive, or collapseerror_retry + empty_retryto the existingretryfilter.Proposed direction
- if (filterValues.includes(LOG_TYPE_ERROR_RETRY_VALUE)) { + const retryFilterValues = filterValues.filter(isRetryLogFilterValue) + const other = parseLogOther(log.other) + const isEmptyRetry = + log.is_empty_retry === true || other?.empty_retry === true + const isErrorRetry = + log.is_error_retry === true || + (!isEmptyRetry && (log.is_retry === true || other?.retry_log === true)) + + if (retryFilterValues.includes(LOG_TYPE_ERROR_RETRY_VALUE) && isErrorRetry) { + return true + } + if (retryFilterValues.includes(LOG_TYPE_EMPTY_RETRY_VALUE) && isEmptyRetry) { + return true + } + if (retryFilterValues.includes(LOG_TYPE_RETRY_VALUE)) { + return log.is_retry === true || isErrorRetry || isEmptyRetry + } + return false - if (log.is_error_retry === true) return true - if (log.is_empty_retry === true) return false - const other = parseLogOther(log.other) - if (other?.empty_retry === true) return false - return other?.retry_log === true - }- const retryFilter = typeValues.find(isRetryLogFilterValue) + const retryFilters = typeValues.filter(isRetryLogFilterValue) + const retryFilter = + retryFilters.includes(LOG_TYPE_RETRY_VALUE) || retryFilters.length > 1 + ? LOG_TYPE_RETRY_VALUE + : retryFilters[0]Also applies to: 261-263
🤖 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 `@web/default/src/features/usage-logs/lib/utils.ts` around lines 130 - 146, Preserve OR semantics in the retry filter handling: the usage-log filtering logic in the retry branch of utils.ts currently short-circuits on error_retry and buildApiParams only forwards the first retry subtype, so selecting both subtypes can hide empty-retry results. Update the filter handling around the retry subtype checks in the utility function and the parameter builder so multiple retry selections are treated additively, or normalize error_retry plus empty_retry into the existing retry filter before applying the API params.
🤖 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 `@web/default/src/features/usage-logs/lib/utils.ts`:
- Around line 130-146: Preserve OR semantics in the retry filter handling: the
usage-log filtering logic in the retry branch of utils.ts currently
short-circuits on error_retry and buildApiParams only forwards the first retry
subtype, so selecting both subtypes can hide empty-retry results. Update the
filter handling around the retry subtype checks in the utility function and the
parameter builder so multiple retry selections are treated additively, or
normalize error_retry plus empty_retry into the existing retry filter before
applying the API params.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ed120bbf-7003-4fc2-aff1-aadebe378007
📒 Files selected for processing (2)
tools/jsonwrapcheck/allowlist.txtweb/default/src/features/usage-logs/lib/utils.ts
💤 Files with no reviewable changes (1)
- tools/jsonwrapcheck/allowlist.txt
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: Backend checks
🧰 Additional context used
📓 Path-based instructions (4)
web/default/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/default/AGENTS.md)
web/default/src/**/*.{ts,tsx}: 前端页面文本与组件内文案必须支持 i18n:React 组件中应使用useTranslation()取得t,并通过t()渲染用户可见文本;子组件也应自行使用useTranslation()保持独立性。
禁止使用两层及以上嵌套三元表达式;复杂逻辑应改用if-else、提前返回或抽取函数。
控制函数圈复杂度,复杂逻辑应拆成更小的函数;变量与函数命名应有意义并遵循驼峰等常规命名约定。
TypeScript 代码应避免使用any,优先使用具体类型或unknown;参数与返回值应显式标注类型;仅类型用途的导入应使用import type。
修改 TypeScript 或 TSX 代码后必须执行类型检查,并修复所有类型错误,不得遗留。
对象非必要不要解构,尤其是组件 props;优先直接使用props.xxx以保持代码清晰。
组件应使用函数式组件与 Hooks,遵循单一职责;组件 props 必须有明确类型(接口或类型别名)。
单文件超过约 200 行时应考虑拆分子组件或抽取自定义 Hooks;类型定义可与组件同文件或放在同模块的types中。
在 React 中应合理使用useMemo、useCallback、React.memo,避免在渲染路径中创建新对象或数组;必要时进行代码分割与动态import。
React Query 的数据获取应使用useQuery、变更应使用useMutation;每个查询需配置唯一queryKey,并在成功后对相关查询执行invalidateQueries;服务端错误应统一交给handleServerError。
Axios 请求应使用项目统一的api实例;GET 请求默认去重,特殊请求可显式关闭;认证与通用错误应在拦截器中统一处理。
服务端错误应统一使用handleServerError,展示层应使用toast.error等统一方式;文案需走 i18n;路由级错误应由errorComponent承接;表单错误应通过form.setError等方式映射到字段。
样式应以 Tailwind 工具类为主,动态类名使用cn()合并;非动态场景避免内联样式;响应式采用移动优先与 Tailwind 断点,主题与暗色模式通过 CSS 变量与dark:处理。
应使用语义化 HTML、正确关联label与输入、保证键盘可操作与合理焦点顺序;必要时添加 ARIA 属性,装饰性图标应使用aria-hidden="true"。
认证与权限应在路由与接口层校验;前后端都应做数据校验(如 Zod);敏感信息不得落前端存储;避免使用dangerouslySetInnerHTML;跨域与 Cookie 需配合withCredentials并按后端要求处理 CSRF。
组件测试应使用 React Testing Library,关注交互与行为,避免测试实现细节;关键流程可补充集成与 E2E 测试。
环境变量应通过.env读取,并使用VITE_前缀;代码中不得硬编码密钥。
Files:
web/default/src/features/usage-logs/lib/utils.ts
web/default/src/features/**/lib/**/*.ts
📄 CodeRabbit inference engine (web/default/AGENTS.md)
表单应使用 React Hook Form + Zod:在功能模块的
lib/下定义 schema,并用z.infer导出表单类型;useForm应配合@hookform/resolvers/zod进行校验。
Files:
web/default/src/features/usage-logs/lib/utils.ts
web/default/src/features/**
📄 CodeRabbit inference engine (web/default/AGENTS.md)
功能模块应放在
src/features/<feature>/,并按需包含components/、lib/、hooks/、api.ts、types.ts、constants.ts等;通用组件应放在src/components/,通用工具与类型应放在src/lib/。
Files:
web/default/src/features/usage-logs/lib/utils.ts
web/default/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Use
bunas the preferred package manager and script runner for the frontend inweb/default/(bun install,bun run dev,bun run build, andbun run i18n:*).
Files:
web/default/src/features/usage-logs/lib/utils.ts
🔇 Additional comments (1)
web/default/src/features/usage-logs/lib/utils.ts (1)
48-58: LGTM!
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
relay/channel/task/doubao/adaptor.go (1)
167-190: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winSilent error swallowing may skew Seedance billing.
Both conversion errors here (
convertToRequestPayloadat Line 174-175 andconvertToGenericMediaRequestat Line 183) are ignored — on failure, the function silently falls back to metadata-onlyresolution/hasVideowith no logging. Since this feeds thevideo_inputbilling multiplier inEstimateBilling, a masked conversion failure could silently mis-price Seedance video tasks (e.g., missing the video-input surcharge) without any operator visibility.Consider at least logging the error so billing anomalies caused by conversion failures are diagnosable.
🤖 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 `@relay/channel/task/doubao/adaptor.go` around lines 167 - 190, The billing input resolver in TaskAdaptor.resolveSeedanceBillingInputs is swallowing failures from convertToRequestPayload and convertToGenericMediaRequest, which can hide Seedance billing mismatches. Update the two conversion call sites to log the returned error with enough context, while preserving the existing fallback behavior for resolution and hasVideo. Keep the fix localized to resolveSeedanceBillingInputs and use the existing helper names so conversion failures are visible when EstimateBilling derives the video_input multiplier.
🤖 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 `@relay/channel/task/doubao/adaptor.go`:
- Around line 167-190: The billing input resolver in
TaskAdaptor.resolveSeedanceBillingInputs is swallowing failures from
convertToRequestPayload and convertToGenericMediaRequest, which can hide
Seedance billing mismatches. Update the two conversion call sites to log the
returned error with enough context, while preserving the existing fallback
behavior for resolution and hasVideo. Keep the fix localized to
resolveSeedanceBillingInputs and use the existing helper names so conversion
failures are visible when EstimateBilling derives the video_input multiplier.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 324c9cc9-b637-46da-ad89-1289881d88a5
📒 Files selected for processing (12)
model/log.gomodel/log_test.gorelay/channel/gemini/adaptor_responses.gorelay/channel/gemini/adaptor_responses_test.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/doubao/adaptor_media_protocol_test.goservice/convert.goservice/convert_test.goservice/openaicompat/chat_to_responses_response.goservice/openaicompat/responses_compat_test.goservice/openaicompat/responses_request_to_chat.goservice/openaicompat/responses_to_chat.go
📜 Review details
⚠️ CI failures not shown inline (2)
GitHub Actions: CI / Backend checks: v1.0.4-preview.1
Conclusion: failure
##[group]Run go run ./tools/jsonwrapcheck
�[36;1mgo run ./tools/jsonwrapcheck�[0m
shell: /usr/bin/bash -e {0}
##[endgroup]
stale json wrapper allowlist entries:
service/convert.go|ResponseOpenAI2Gemini|Unmarshal|***REDACTED***
service/convert.go|StreamResponseOpenAI2Gemini|Unmarshal|***REDACTED***
Regenerate the baseline with: go run ./tools/jsonwrapcheck -list > tools/jsonwrapcheck/allowlist.txt
exit status 1
##[error]Process completed with exit code 1.
GitHub Actions: CI / 1_Backend checks.txt: v1.0.4-preview.1
Conclusion: failure
##[group]Run go run ./tools/jsonwrapcheck
�[36;1mgo run ./tools/jsonwrapcheck�[0m
shell: /usr/bin/bash -e {0}
##[endgroup]
stale json wrapper allowlist entries:
service/convert.go|ResponseOpenAI2Gemini|Unmarshal|***REDACTED***
service/convert.go|StreamResponseOpenAI2Gemini|Unmarshal|***REDACTED***
Regenerate the baseline with: go run ./tools/jsonwrapcheck -list > tools/jsonwrapcheck/allowlist.txt
exit status 1
##[error]Process completed with exit code 1.
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrappers incommon/json.go(common.Marshal,common.Unmarshal,common.UnmarshalJsonStr,common.DecodeJson,common.GetJsonType) instead of directly callingencoding/json;encoding/jsontypes likejson.RawMessageandjson.Numbermay 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 directAUTO_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 withomitemptyso explicit zero/false values are preserved and omitted-only fields remainnil.
**/*.go: Usecommon.Marshal,common.Unmarshal,common.UnmarshalJsonStr,common.DecodeJson, andcommon.GetJsonTypefromcommon/json.gofor all JSON marshal/unmarshal operations; do not directly callencoding/jsonin 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 withomitemptyfor optional scalar fields so explicit zero/false values are preserved.
Files:
relay/channel/gemini/adaptor_responses_test.goservice/convert_test.gorelay/channel/gemini/adaptor_responses.gorelay/channel/task/doubao/adaptor.goservice/convert.goservice/openaicompat/responses_compat_test.gomodel/log_test.gorelay/channel/task/doubao/adaptor_media_protocol_test.gomodel/log.goservice/openaicompat/responses_to_chat.goservice/openaicompat/responses_request_to_chat.goservice/openaicompat/chat_to_responses_response.go
relay/channel/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
When implementing a new relay channel, confirm whether the provider supports
StreamOptions; if it does, add that channel tostreamSupportedChannels.When implementing a new channel, confirm whether the provider supports
StreamOptions; if it does, add the channel tostreamSupportedChannels.
Files:
relay/channel/gemini/adaptor_responses_test.gorelay/channel/gemini/adaptor_responses.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/doubao/adaptor_media_protocol_test.go
🪛 GitHub Actions: CI / 1_Backend checks.txt
service/convert.go
[error] 1-1: json wrapper allowlist contains stale entry: service/convert.go|ResponseOpenAI2Gemini|Unmarshal|REDACTED
[error] 1-1: json wrapper allowlist contains stale entry: service/convert.go|StreamResponseOpenAI2Gemini|Unmarshal|REDACTED
🔇 Additional comments (12)
relay/channel/gemini/adaptor_responses_test.go (1)
1-130: LGTM!service/convert_test.go (1)
53-64: LGTM! Closes the previously flagged Gemini malformed-arguments coverage gap.relay/channel/gemini/adaptor_responses.go (1)
33-55: LGTM! Properly surfaces an error for unsupported tool types instead of silently dropping them, addressing the earlier major finding.relay/channel/task/doubao/adaptor.go (1)
56-64: LGTM!Resolution/Rationow use*stringwithomitempty, correctly preserving explicit empty values while omitting truly-absent fields, addressing the prior major finding.service/convert.go (1)
607-650: LGTM! The sharedparseToolCallArgumentshelper cleanly removes the duplicated Claude/Gemini argument-parsing logic and standardizes oncommon.Unmarshal, resolving the earlier duplication nitpick.Also applies to: 871-897, 900-991
relay/channel/task/doubao/adaptor_media_protocol_test.go (1)
244-298: LGTM! Good regression coverage confirming explicit empty optional strings are preserved as distinct from omitted fields.service/openaicompat/responses_compat_test.go (1)
191-230: LGTM!Also applies to: 279-329
model/log_test.go (1)
85-95: LGTM!Also applies to: 106-116
model/log.go (1)
241-256: LGTM!service/openaicompat/responses_to_chat.go (1)
120-146: LGTM!service/openaicompat/responses_request_to_chat.go (1)
160-166: LGTM!Also applies to: 252-268
service/openaicompat/chat_to_responses_response.go (1)
111-113: LGTM!Also applies to: 531-552
There was a problem hiding this comment.
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 `@relay/channel/task/taskcommon/request_body.go`:
- Around line 58-82: In BuildConfiguredTaskRequestBody, the media-generation
path is re-parsing the request body in both configuredTaskRequest and
buildMediaGenerationTaskBody, which creates redundant work and depends on
reusable-body behavior. Update the TaskRequestBodyModeMediaGeneration flow to
parse the body once into a raw map/object, then pass that parsed value through
to both the configuredTaskRequest and buildMediaGenerationTaskBody steps. Adjust
the related helpers in configuredTaskRequest and buildMediaGenerationTaskBody so
they share the same parsed input instead of calling common.UnmarshalBodyReusable
twice.
- Around line 289-313: Silent overwrite of existing non-map values in nested
mappings: setMappedOutputValue currently replaces any intermediate non-map at a
dotted path with a new map, which can discard RequestBodyDefaults data without
warning. Update setMappedOutputValue to detect when current[part] exists but is
not a map[string]any, and stop processing or return an error/log instead of
overwriting it; keep the behavior only when the intermediate key is missing or
already a map. Refer to setMappedOutputValue and the RequestBodyDefaults mapping
flow when implementing the conflict check.
- Around line 370-404: applyMediaRawFields is only copying a fixed set of media
request keys and currently drops any unknown top-level fields from the raw body.
Update applyMediaRawFields to preserve unrecognized entries by adding them to
payload.Extra, similar to the default handling in applyMediaMetadata, while
keeping the existing typed parsing for known fields like model, prompt,
duration_seconds, with_audio, and reference_images. Use payload.Extra and
setPayloadExtra as the main insertion points so provider-specific top-level
parameters are not silently lost.
🪄 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: 4e0451a8-d523-4b42-8c57-de0c0483bb95
📒 Files selected for processing (26)
dto/channel_settings.gomodel/log.gomodel/log_test.gorelay/channel/api_request.gorelay/channel/api_request_test.gorelay/channel/openai/chat_via_responses.gorelay/channel/openai/empty_completion_test.gorelay/channel/openai/responses_via_chat.gorelay/channel/task/doubao/adaptor.gorelay/channel/task/doubao/adaptor_billing_test.gorelay/channel/task/doubao/media_protocol.gorelay/channel/task/taskcommon/path_config.gorelay/channel/task/taskcommon/path_config_test.gorelay/channel/task/taskcommon/protocol_config.gorelay/channel/task/taskcommon/request_body.gorelay/relay_task.goweb/default/src/features/channels/components/drawers/channel-mutate-drawer.tsxweb/default/src/features/channels/lib/channel-form.tsweb/default/src/features/channels/types.tsweb/default/src/i18n/locales/en.jsonweb/default/src/i18n/locales/fr.jsonweb/default/src/i18n/locales/ja.jsonweb/default/src/i18n/locales/ru.jsonweb/default/src/i18n/locales/vi.jsonweb/default/src/i18n/locales/zh.jsonweb/default/tests/channel-form.test.ts
💤 Files with no reviewable changes (2)
- relay/channel/task/doubao/media_protocol.go
- model/log.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrappers incommon/json.go(common.Marshal,common.Unmarshal,common.UnmarshalJsonStr,common.DecodeJson,common.GetJsonType) instead of directly callingencoding/json;encoding/jsontypes likejson.RawMessageandjson.Numbermay 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 directAUTO_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 withomitemptyso explicit zero/false values are preserved and omitted-only fields remainnil.
**/*.go: Usecommon.Marshal,common.Unmarshal,common.UnmarshalJsonStr,common.DecodeJson, andcommon.GetJsonTypefromcommon/json.gofor all JSON marshal/unmarshal operations; do not directly callencoding/jsonin 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 withomitemptyfor optional scalar fields so explicit zero/false values are preserved.
Files:
relay/channel/openai/empty_completion_test.gorelay/relay_task.gorelay/channel/openai/chat_via_responses.gorelay/channel/api_request.gorelay/channel/openai/responses_via_chat.gorelay/channel/api_request_test.gorelay/channel/task/taskcommon/path_config.gorelay/channel/task/doubao/adaptor_billing_test.godto/channel_settings.gorelay/channel/task/taskcommon/protocol_config.gomodel/log_test.gorelay/channel/task/taskcommon/request_body.gorelay/channel/task/taskcommon/path_config_test.gorelay/channel/task/doubao/adaptor.go
relay/channel/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
When implementing a new relay channel, confirm whether the provider supports
StreamOptions; if it does, add that channel tostreamSupportedChannels.When implementing a new channel, confirm whether the provider supports
StreamOptions; if it does, add the channel tostreamSupportedChannels.
Files:
relay/channel/openai/empty_completion_test.gorelay/channel/openai/chat_via_responses.gorelay/channel/api_request.gorelay/channel/openai/responses_via_chat.gorelay/channel/api_request_test.gorelay/channel/task/taskcommon/path_config.gorelay/channel/task/doubao/adaptor_billing_test.gorelay/channel/task/taskcommon/protocol_config.gorelay/channel/task/taskcommon/request_body.gorelay/channel/task/taskcommon/path_config_test.gorelay/channel/task/doubao/adaptor.go
web/default/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Use
bunas the preferred package manager and script runner for the frontend inweb/default/(bun install,bun run dev,bun run build, andbun run i18n:*).
Files:
web/default/tests/channel-form.test.tsweb/default/src/features/channels/types.tsweb/default/src/features/channels/components/drawers/channel-mutate-drawer.tsxweb/default/src/i18n/locales/ja.jsonweb/default/src/i18n/locales/vi.jsonweb/default/src/i18n/locales/en.jsonweb/default/src/i18n/locales/fr.jsonweb/default/src/i18n/locales/zh.jsonweb/default/src/i18n/locales/ru.jsonweb/default/src/features/channels/lib/channel-form.ts
web/default/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/default/AGENTS.md)
web/default/src/**/*.{ts,tsx}: 前端页面文本与组件内文案必须支持 i18n:React 组件中应使用useTranslation()取得t,并通过t()渲染用户可见文本;子组件也应自行使用useTranslation()保持独立性。
禁止使用两层及以上嵌套三元表达式;复杂逻辑应改用if-else、提前返回或抽取函数。
控制函数圈复杂度,复杂逻辑应拆成更小的函数;变量与函数命名应有意义并遵循驼峰等常规命名约定。
TypeScript 代码应避免使用any,优先使用具体类型或unknown;参数与返回值应显式标注类型;仅类型用途的导入应使用import type。
修改 TypeScript 或 TSX 代码后必须执行类型检查,并修复所有类型错误,不得遗留。
对象非必要不要解构,尤其是组件 props;优先直接使用props.xxx以保持代码清晰。
组件应使用函数式组件与 Hooks,遵循单一职责;组件 props 必须有明确类型(接口或类型别名)。
单文件超过约 200 行时应考虑拆分子组件或抽取自定义 Hooks;类型定义可与组件同文件或放在同模块的types中。
在 React 中应合理使用useMemo、useCallback、React.memo,避免在渲染路径中创建新对象或数组;必要时进行代码分割与动态import。
React Query 的数据获取应使用useQuery、变更应使用useMutation;每个查询需配置唯一queryKey,并在成功后对相关查询执行invalidateQueries;服务端错误应统一交给handleServerError。
Axios 请求应使用项目统一的api实例;GET 请求默认去重,特殊请求可显式关闭;认证与通用错误应在拦截器中统一处理。
服务端错误应统一使用handleServerError,展示层应使用toast.error等统一方式;文案需走 i18n;路由级错误应由errorComponent承接;表单错误应通过form.setError等方式映射到字段。
样式应以 Tailwind 工具类为主,动态类名使用cn()合并;非动态场景避免内联样式;响应式采用移动优先与 Tailwind 断点,主题与暗色模式通过 CSS 变量与dark:处理。
应使用语义化 HTML、正确关联label与输入、保证键盘可操作与合理焦点顺序;必要时添加 ARIA 属性,装饰性图标应使用aria-hidden="true"。
认证与权限应在路由与接口层校验;前后端都应做数据校验(如 Zod);敏感信息不得落前端存储;避免使用dangerouslySetInnerHTML;跨域与 Cookie 需配合withCredentials并按后端要求处理 CSRF。
组件测试应使用 React Testing Library,关注交互与行为,避免测试实现细节;关键流程可补充集成与 E2E 测试。
环境变量应通过.env读取,并使用VITE_前缀;代码中不得硬编码密钥。
Files:
web/default/src/features/channels/types.tsweb/default/src/features/channels/components/drawers/channel-mutate-drawer.tsxweb/default/src/features/channels/lib/channel-form.ts
web/default/src/features/**
📄 CodeRabbit inference engine (web/default/AGENTS.md)
功能模块应放在
src/features/<feature>/,并按需包含components/、lib/、hooks/、api.ts、types.ts、constants.ts等;通用组件应放在src/components/,通用工具与类型应放在src/lib/。
Files:
web/default/src/features/channels/types.tsweb/default/src/features/channels/components/drawers/channel-mutate-drawer.tsxweb/default/src/features/channels/lib/channel-form.ts
web/default/src/features/**/lib/**/*.ts
📄 CodeRabbit inference engine (web/default/AGENTS.md)
表单应使用 React Hook Form + Zod:在功能模块的
lib/下定义 schema,并用z.infer导出表单类型;useForm应配合@hookform/resolvers/zod进行校验。
Files:
web/default/src/features/channels/lib/channel-form.ts
🔇 Additional comments (39)
relay/channel/task/doubao/adaptor.go (2)
57-65: Pointer-type fix from prior review confirmed.
payload.Resolution/SafetyIdentifier/Priorityare now used as pointer types throughout (resolveBillingInputs,TestConvertToRequestPayloadPreservesDoubaoVideoFields), addressing the previously flagged guideline violation.
107-199: LGTM!Also applies to: 201-266, 268-289, 297-329, 332-355, 365-399, 401-447, 449-505, 506-513
relay/channel/openai/empty_completion_test.go (1)
357-400: LGTM!relay/channel/openai/chat_via_responses.go (1)
16-16: LGTM!Also applies to: 485-485
relay/channel/api_request.go (1)
535-545: LGTM!Also applies to: 547-576, 578-586
relay/channel/openai/responses_via_chat.go (1)
61-138: LGTM!relay/channel/api_request_test.go (1)
4-14: LGTM!Also applies to: 15-35, 36-53, 55-73
web/default/src/i18n/locales/vi.json (1)
154-154: LGTM!Also applies to: 702-704, 830-830, 1543-1543, 1695-1695, 1911-1911, 2222-2223, 2364-2365, 2590-2590, 3198-3198, 3722-3728
relay/relay_task.go (1)
281-295: 🩺 Stability & AvailabilityNo panic risk here.
buildFieldMappingTaskBodyandconfiguredTaskRequestboth handle a nilreqby rereading the reusable body, andbuildMediaGenerationTaskBodyreturns a regular error if no request can be obtained. The helper no longer depends ontask_requestbeing stored ahead of time.> Likely an incorrect or invalid review comment.web/default/tests/channel-form.test.ts (1)
1-231: LGTM!web/default/src/features/channels/types.ts (1)
114-131: LGTM!Also applies to: 156-156
web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx (1)
212-238: LGTM!Also applies to: 398-398, 3086-3188
web/default/src/i18n/locales/ja.json (1)
154-154: LGTM! All new/changed keys correspond tot()calls added inchannel-mutate-drawer.tsxand are translated consistently.Also applies to: 830-830, 1543-1543, 1695-1695, 1911-1911, 2222-2223, 2364-2365, 2590-2590, 3198-3198, 3722-3728
web/default/src/i18n/locales/ru.json (1)
154-154: LGTM! Consistent with the correspondingja.jsonupdates and code usage.Also applies to: 830-830, 1543-1543, 1695-1695, 1911-1911, 2222-2223, 2364-2365, 2590-2590, 3198-3198, 3722-3728
relay/channel/task/taskcommon/path_config.go (3)
27-29: 🎯 Functional Correctness | ⚡ Quick winError message omits a valid
task_protocolvalue.
UseConfiguredTaskProtocolaccepts bothTaskProtocolGenericVideoandTaskProtocolLegacySeedanceMedia, but this error only mentions the former, which can mislead admins fixing an invalidtask_protocol.🐛 Proposed fix
if strings.TrimSpace(settings.TaskProtocol) != "" && !UseConfiguredTaskProtocol(settings) { - return fmt.Errorf("task_protocol must be %s", TaskProtocolGenericVideo) + return fmt.Errorf("task_protocol must be %s or %s", TaskProtocolGenericVideo, TaskProtocolLegacySeedanceMedia) }
121-158: LGTM!
65-75: LGTM!Also applies to: 91-107
relay/channel/task/taskcommon/path_config_test.go (1)
4-9: LGTM! Comprehensive new test coverage for the configured task protocol request-body modes and URL deduplication behavior.Also applies to: 57-72, 81-425, 493-526, 528-544
web/default/src/features/channels/lib/channel-form.ts (12)
543-567: 📐 Maintainability & Code QualityCallers must guard against
JSON.parsethrowing.
parseOptionalJsonObjectInputandparseOptionalStringRecordInputcallparseOptionalJson, which runs a bareJSON.parseand throws aSyntaxErroron invalid input. They are correctly wrapped in a try/catch at thesuperRefinecall site (Line 415-424), but are called unguarded later inbuildSettingsJSON(Line 1013-1018). See the follow-up comment there.
1051-1064: 🎯 Functional CorrectnessSee the related comment on Lines 742-766 regarding legacy protocol identity loss.
This is where
task_protocolis unconditionally set toVIDEO_TASK_PROTOCOL, which is the site of the concern raised above about overwriting the legacy protocol value on save.
42-67: LGTM!
264-277: LGTM!
408-433: 📐 Maintainability & Code Quality | 💤 Low valueConsider surfacing the issue on both fields.
The required-issue is only attached to
video_task_request_body_mapping, even thoughvideo_task_request_body_defaultscan also satisfy the requirement. If a user only touches the defaults field, the error will still point to the mapping field, which can be confusing.
494-496: LGTM!
618-636: 📐 Maintainability & Code Quality | ⚡ Quick winMissing explicit return type annotation.
normalizeVideoTaskRequestBodyModehas no declared return type (unlike the other new helpers in this file, e.g.stringifyOptionalJsonObject,parseOptionalJsonObjectInput). Per coding guidelines, function return values should be explicitly annotated.As per coding guidelines, "参数与返回值应显式标注类型".
🔧 Proposed fix
function normalizeVideoTaskRequestBodyMode( value: unknown, taskProtocol = '', hasImplicitFieldMappingConfig = false -) { +): VideoTaskRequestBodyMode {Source: Coding guidelines
691-694: LGTM!
865-867: LGTM!
1013-1023: 🩺 Stability & Availability | ⚡ Quick winUnguarded JSON parsing can throw during form submission.
parseOptionalStringRecordInputandparseOptionalJsonObjectInputinternally callJSON.parseviaparseOptionalJsonand will throw aSyntaxErroron malformed input. Elsewhere in this file (e.g. Lines 658-671, 710-816)JSON.parsecalls are wrapped in try/catch, and thesuperRefinevalidator (Lines 414-425) also wraps these same helper calls in a try/catch. Here inbuildSettingsJSONthey're called directly with no guard. SincechannelFormSchema's refinements should reject malformed JSON before submission, actual risk is low in the normal React Hook Form flow, but any direct/programmatic call tobuildSettingsJSON/transformFormDataToCreatePayloadbypassing validation would throw an uncaught exception instead of failing gracefully.🛡️ Proposed fix
if (enableVideoTaskProtocol) { - const requestBodyMapping = parseOptionalStringRecordInput( - formData.video_task_request_body_mapping - ) - const requestBodyDefaults = parseOptionalJsonObjectInput( - formData.video_task_request_body_defaults - ) + let requestBodyMapping: Record<string, string> | undefined + let requestBodyDefaults: Record<string, unknown> | undefined + try { + requestBodyMapping = parseOptionalStringRecordInput( + formData.video_task_request_body_mapping + ) + requestBodyDefaults = parseOptionalJsonObjectInput( + formData.video_task_request_body_defaults + ) + } catch (error) { + // eslint-disable-next-line no-console + console.error('Failed to parse video task request body config:', error) + }
1084-1102: 📐 Maintainability & Code Quality | 💤 Low valueRedundant delete of
task_protocol.Line 1084 deletes
settingsObj.task_protocolinside theenableVideoTaskPathOverridebranch, but Line 1100-1102 already performs the same delete unconditionally whenever!enableVideoTaskProtocol, which covers this branch too. The Line 1084 delete is now dead code.🧹 Proposed cleanup
} else if (enableVideoTaskPathOverride) { - if ('task_protocol' in settingsObj) { - delete settingsObj.task_protocol - } settingsObj.task_protocol_config = {
742-766: 🎯 Functional CorrectnessLegacy protocol is normalized on save. The backend treats
seedance_official_mediaandgeneric_video_taskas the same configured protocol, andmedia_generationcarries the legacy behavior here.> Likely an incorrect or invalid review comment.web/default/src/i18n/locales/en.json (1)
154-154: LGTM!Also applies to: 702-704, 830-830, 1543-1543, 1695-1695, 1911-1911, 2222-2223, 2364-2365, 2590-2590, 3198-3198, 3722-3728
relay/channel/task/doubao/adaptor_billing_test.go (1)
143-157: LGTM!Also applies to: 159-180, 199-227, 255-284, 286-304, 305-321
web/default/src/i18n/locales/fr.json (2)
2364-2365: 📐 Maintainability & Code Quality | ⚡ Quick winFix missing accents/typos in newly added French strings.
Lines 2364, 2365, 3722, and 3725 drop diacritics ("requete"/"defaut" instead of "requête"/"défaut"), inconsistent with sibling strings in the same batch (e.g. line 3723-3724, 3726-3728) that are correctly accented. Line 2365 also uses "fusionne"/"mappes" instead of the correct participle forms "fusionné"/"mappés".
🇫🇷 Proposed fix
- "JSON object mapping upstream request fields to source request paths": "Objet JSON associant les champs de requete amont aux chemins de la requete source", - "JSON object merged into the upstream request body before mapped fields": "Objet JSON fusionne dans le corps de requete amont avant les champs mappes", + "JSON object mapping upstream request fields to source request paths": "Objet JSON associant les champs de requête amont aux chemins de la requête source", + "JSON object merged into the upstream request body before mapped fields": "Objet JSON fusionné dans le corps de requête amont avant les champs mappés",- "Request Body Defaults": "Valeurs par defaut du corps de requete", + "Request Body Defaults": "Valeurs par défaut du corps de requête", "Request Body Disk Cache": "Cache disque du corps de requête", "Request Body Field": "Champ du corps de requête", - "Request Body Field Mapping": "Mappage des champs du corps de requete", + "Request Body Field Mapping": "Mappage des champs du corps de requête",Also applies to: 3722-3728
154-154: LGTM!Also applies to: 702-704, 830-830, 1543-1543, 1695-1695, 1911-1911, 2222-2223, 2590-2590, 3198-3198
dto/channel_settings.go (1)
55-69: LGTM!relay/channel/task/taskcommon/protocol_config.go (2)
22-26: LGTM!Also applies to: 52-69, 76-151, 153-159, 183-187, 224-228, 284-297
161-181: 🩺 Stability & AvailabilityNo aliasing issue here
GetOtherSettings()returns a fresh settings struct,buildFieldMappingTaskBody()deep-clonesRequestBodyDefaultsbefore any writes, andRequestBodyMappingonly contains strings. The shallow top-level copy here is harmless.> Likely an incorrect or invalid review comment.model/log_test.go (1)
134-167: LGTM!web/default/src/i18n/locales/zh.json (1)
154-154: LGTM!Also applies to: 830-830, 1543-1543, 1695-1695, 1911-1911, 2364-2365, 2590-2590, 3198-3198, 3722-3728
| func BuildConfiguredTaskRequestBody(c *gin.Context, info *relaycommon.RelayInfo, req *relaycommon.TaskSubmitReq) (io.Reader, bool, error) { | ||
| if info == nil || info.ChannelMeta == nil || !UseConfiguredTaskProtocol(info.ChannelOtherSettings) { | ||
| return nil, false, nil | ||
| } | ||
| cfg := EffectiveTaskProtocolConfig(info.ChannelOtherSettings) | ||
| switch cfg.RequestBodyMode { | ||
| case TaskRequestBodyModeAdapter: | ||
| return nil, false, nil | ||
| case TaskRequestBodyModePassThrough: | ||
| body, err := buildPassThroughTaskBody(c, info) | ||
| return body, true, err | ||
| case TaskRequestBodyModeFieldMapping: | ||
| body, err := buildFieldMappingTaskBody(c, info, req, cfg.RequestBodyMapping, cfg.RequestBodyDefaults) | ||
| return body, true, err | ||
| case TaskRequestBodyModeMediaGeneration: | ||
| taskReq, err := configuredTaskRequest(c, req) | ||
| if err != nil { | ||
| return nil, true, err | ||
| } | ||
| body, err := buildMediaGenerationTaskBody(c, info, taskReq) | ||
| return body, true, err | ||
| default: | ||
| return nil, false, fmt.Errorf("unsupported task request body mode: %s", cfg.RequestBodyMode) | ||
| } | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
Body re-parsed multiple times per request for media-generation mode.
For TaskRequestBodyModeMediaGeneration, the body is unmarshaled once in configuredTaskRequest (only if req was nil) and again in buildMediaGenerationTaskBody via common.UnmarshalBodyReusable(c, &raw). This relies on the reusable-body buffer working correctly across repeated reads; consider consolidating into a single raw-map parse passed to both steps to reduce redundant work and coupling to buffer-reuse correctness.
Also applies to: 134-150
🤖 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 `@relay/channel/task/taskcommon/request_body.go` around lines 58 - 82, In
BuildConfiguredTaskRequestBody, the media-generation path is re-parsing the
request body in both configuredTaskRequest and buildMediaGenerationTaskBody,
which creates redundant work and depends on reusable-body behavior. Update the
TaskRequestBodyModeMediaGeneration flow to parse the body once into a raw
map/object, then pass that parsed value through to both the
configuredTaskRequest and buildMediaGenerationTaskBody steps. Adjust the related
helpers in configuredTaskRequest and buildMediaGenerationTaskBody so they share
the same parsed input instead of calling common.UnmarshalBodyReusable twice.
| func setMappedOutputValue(out map[string]any, path string, value any) { | ||
| path = strings.TrimSpace(path) | ||
| if out == nil || path == "" { | ||
| return | ||
| } | ||
| parts := strings.Split(path, ".") | ||
| current := out | ||
| for _, part := range parts[:len(parts)-1] { | ||
| part = strings.TrimSpace(part) | ||
| if part == "" { | ||
| return | ||
| } | ||
| next, ok := current[part].(map[string]any) | ||
| if !ok { | ||
| next = map[string]any{} | ||
| current[part] = next | ||
| } | ||
| current = next | ||
| } | ||
| leaf := strings.TrimSpace(parts[len(parts)-1]) | ||
| if leaf == "" { | ||
| return | ||
| } | ||
| current[leaf] = value | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Silent overwrite of non-map defaults during nested field mapping.
If out (built from RequestBodyDefaults) already has a non-map value at an intermediate path segment (e.g. defaults set "a": "x" and a mapping targets "a.b"), setMappedOutputValue silently replaces it with a new empty map, discarding the default value without any error or log. Since defaults and mappings are both admin-configured JSON, a conflicting path is plausible and would fail silently.
🤖 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 `@relay/channel/task/taskcommon/request_body.go` around lines 289 - 313, Silent
overwrite of existing non-map values in nested mappings: setMappedOutputValue
currently replaces any intermediate non-map at a dotted path with a new map,
which can discard RequestBodyDefaults data without warning. Update
setMappedOutputValue to detect when current[part] exists but is not a
map[string]any, and stop processing or return an error/log instead of
overwriting it; keep the behavior only when the intermediate key is missing or
already a map. Refer to setMappedOutputValue and the RequestBodyDefaults mapping
flow when implementing the conflict check.
📝 变更描述 / Description
本次变更围绕 v1.0.4-preview.1 的兼容与日志能力展开:
safety_identifier、priority等请求字段。这些改动生效的关键在于:请求进入上游前先转换为目标协议结构,上游响应返回后再按客户端协议还原;日志写入时同步 retry 子类型字段,查询与统计直接使用后端过滤条件;Seedance 计费通过价格表计算相对基础价倍率;异步任务计费日志显式传递任务私有数据中的提交节点。
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix;如需按 bug fix 提交,请先关联对应 Issue。📸 运行证明 / Proof of Work