feat: 自动将渠道重定向中的模型名加入到模型列表 - #1707
Conversation
WalkthroughAdds lodash dependency in web/package.json. Updates EditChannel submit logic to parse and normalize JSON model_mapping, merge mapping keys into models, sort/deduplicate models, enforce at least one model at submit, and surface JSON parse errors. Removes prior verifyJSON usage and UI-required rule for models. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant EC as EditChannel Page
participant SH as Submit Handler
participant VM as Validator/Mapper
participant UI as UI Feedback
U->>EC: Click "Save"/Submit
EC->>SH: onSubmit(localInputs)
alt model_mapping provided
SH->>VM: JSON.parse(localInputs.model_mapping)
alt Parse fails
VM-->>SH: Error
SH->>UI: Show error "Invalid JSON"
SH-->>EC: Abort submission
else Parse succeeds
VM-->>SH: mapping object
SH->>SH: Normalize mapping (sort keys)
SH->>SH: Merge mapping keys into models
SH->>SH: Deduplicate + sort models
end
else no model_mapping
SH->>SH: Use existing models
end
alt models empty
SH->>UI: Show error "At least one model required"
SH-->>EC: Abort submission
else
SH-->>EC: Proceed with save request
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/pages/Channel/EditChannel.js (1)
267-299: Avoid mutating React state arrays when aggregating models.
modelsreferencesinputs.models;pushmutates state. Clone first to keep state immutable.- const models = inputs['models'] || []; + const models = [...(inputs['models'] || [])]; @@ - models.push(...res.data.data); + models.push(...(res.data.data || [])); @@ - if (res && res.data && res.data.success) { - models.push(...res.data.data); + if (res && res.data && res.data.success) { + models.push(...(res.data.data || []));
🧹 Nitpick comments (3)
web/package.json (1)
21-21: Consider avoiding full lodash; use native or lodash-es to keep bundle lean.For the current sorting/dedup use case, native APIs suffice. If a utility lib is preferred, import from lodash-es per-function to enable tree-shaking.
Optional removal if switching to native code:
- "lodash": "^4.17.21",web/src/pages/Channel/EditChannel.js (2)
28-28: Drop default lodash import (or switch to per-function from lodash-es).If adopting native code below, remove this import. If keeping lodash, prefer
import { toPairs, orderBy, fromPairs, keys } from 'lodash-es'.-import _ from 'lodash';
224-230: Defensive parse for model_mapping from backend to prevent UI crash.A bad value from server will throw. Wrap in try/catch and degrade gracefully.
- if (data.model_mapping !== '') { - data.model_mapping = JSON.stringify( - JSON.parse(data.model_mapping), - null, - 2, - ); - } + if (data.model_mapping !== '') { + try { + data.model_mapping = JSON.stringify( + JSON.parse(data.model_mapping), + null, + 2, + ); + } catch { + showError(t('后端返回的模型映射不是有效 JSON,已忽略')); + data.model_mapping = ''; + } + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
web/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (2)
web/package.json(1 hunks)web/src/pages/Channel/EditChannel.js(2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: AAEE86
PR: QuantumNous/new-api#1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the applyModelMapping function transforms the models list by replacing original model names (mapping values) with display names (mapping keys). The database stores this transformed list containing mapped keys. On channel load, data.models contains these mapped display names, making the initialization filter if (data.models.includes(key)) correct.
Learnt from: AAEE86
PR: QuantumNous/new-api#1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the database stores mapped keys (display names) in the models field after applying model mapping transformations. When loading a channel, data.models contains the mapped keys, not the original model names. The filtering logic if (data.models.includes(key)) in the initialization is correct.
📚 Learning: 2025-08-27T02:15:25.448Z
Learnt from: AAEE86
PR: QuantumNous/new-api#1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the applyModelMapping function transforms the models list by replacing original model names (mapping values) with display names (mapping keys). The database stores this transformed list containing mapped keys. On channel load, data.models contains these mapped display names, making the initialization filter if (data.models.includes(key)) correct.
Applied to files:
web/src/pages/Channel/EditChannel.js
📚 Learning: 2025-08-27T02:15:25.448Z
Learnt from: AAEE86
PR: QuantumNous/new-api#1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the database stores mapped keys (display names) in the models field after applying model mapping transformations. When loading a channel, data.models contains the mapped keys, not the original model names. The filtering logic if (data.models.includes(key)) in the initialization is correct.
Applied to files:
web/src/pages/Channel/EditChannel.js
🧬 Code graph analysis (1)
web/src/pages/Channel/EditChannel.js (1)
web/src/helpers/utils.js (1)
showInfo(133-135)
🔇 Additional comments (1)
web/src/pages/Channel/EditChannel.js (1)
1249-1293: UX parity check: models no longer required at UI level; runtime will auto-fill from mapping keys.Please verify flows where the user supplies only model_mapping: submit should succeed and models should persist the mapping keys (as stored/display names).
Suggested manual check:
- New channel: clear “模型” list, set model_mapping to {"foo":"bar"}. Submit.
- Confirm payload models contains "foo" only, and DB shows "foo" (mapped key) per previous behavior.
| let model_mapping = {}; | ||
| if (localInputs.model_mapping && localInputs.model_mapping !== '') { | ||
| try { | ||
| model_mapping = _(JSON.parse(localInputs.model_mapping)) | ||
| .toPairs() | ||
| .orderBy([0], ['asc']) | ||
| .fromPairs() | ||
| .value(); | ||
| } catch (error) { | ||
| showInfo(t('模型映射必须是合法的 JSON 格式!')); | ||
| return; | ||
| } | ||
| } | ||
| localInputs.model_mapping = _.isEmpty(model_mapping) ? '' : JSON.stringify(model_mapping); | ||
| localInputs.models = _(localInputs.models) | ||
| .concat(_.keys(model_mapping)) | ||
| .sort() | ||
| .sortedUniq() | ||
| .value(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Harden model_mapping handling: validate object shape, trim keys/values, sort deterministically, dedup models; also treat JSON errors as errors.
Current code accepts arrays and uses lodash where native APIs suffice.
- let model_mapping = {};
- if (localInputs.model_mapping && localInputs.model_mapping !== '') {
- try {
- model_mapping = _(JSON.parse(localInputs.model_mapping))
- .toPairs()
- .orderBy([0], ['asc'])
- .fromPairs()
- .value();
- } catch (error) {
- showInfo(t('模型映射必须是合法的 JSON 格式!'));
- return;
- }
- }
- localInputs.model_mapping = _.isEmpty(model_mapping) ? '' : JSON.stringify(model_mapping);
- localInputs.models = _(localInputs.models)
- .concat(_.keys(model_mapping))
- .sort()
- .sortedUniq()
- .value();
+ let model_mapping = {};
+ if (localInputs.model_mapping && localInputs.model_mapping.trim() !== '') {
+ try {
+ const raw = JSON.parse(localInputs.model_mapping);
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
+ showError(t('模型映射必须是一个对象(形如 {"from":"to"})!'));
+ return;
+ }
+ const entries = Object.entries(raw)
+ .map(([k, v]) => [String(k).trim(), typeof v === 'string' ? v.trim() : String(v)])
+ .filter(([k, v]) => k.length > 0 && v.length > 0);
+ entries.sort((a, b) => a[0].localeCompare(b[0]));
+ model_mapping = Object.fromEntries(entries);
+ } catch {
+ showError(t('模型映射必须是合法的 JSON 格式!'));
+ return;
+ }
+ }
+ localInputs.model_mapping = Object.keys(model_mapping).length === 0 ? '' : JSON.stringify(model_mapping);
+ const currentModels = Array.isArray(localInputs.models) ? localInputs.models : [];
+ localInputs.models = Array.from(
+ new Set([
+ ...currentModels.map((m) => (m || '').trim()),
+ ...Object.keys(model_mapping),
+ ])
+ ).sort((a, b) => a.localeCompare(b));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let model_mapping = {}; | |
| if (localInputs.model_mapping && localInputs.model_mapping !== '') { | |
| try { | |
| model_mapping = _(JSON.parse(localInputs.model_mapping)) | |
| .toPairs() | |
| .orderBy([0], ['asc']) | |
| .fromPairs() | |
| .value(); | |
| } catch (error) { | |
| showInfo(t('模型映射必须是合法的 JSON 格式!')); | |
| return; | |
| } | |
| } | |
| localInputs.model_mapping = _.isEmpty(model_mapping) ? '' : JSON.stringify(model_mapping); | |
| localInputs.models = _(localInputs.models) | |
| .concat(_.keys(model_mapping)) | |
| .sort() | |
| .sortedUniq() | |
| .value(); | |
| let model_mapping = {}; | |
| if (localInputs.model_mapping && localInputs.model_mapping.trim() !== '') { | |
| try { | |
| const raw = JSON.parse(localInputs.model_mapping); | |
| if (!raw || typeof raw !== 'object' || Array.isArray(raw)) { | |
| showError(t('模型映射必须是一个对象(形如 {"from":"to"})!')); | |
| return; | |
| } | |
| const entries = Object.entries(raw) | |
| .map(([k, v]) => [String(k).trim(), typeof v === 'string' ? v.trim() : String(v)]) | |
| .filter(([k, v]) => k.length > 0 && v.length > 0); | |
| entries.sort((a, b) => a[0].localeCompare(b[0])); | |
| model_mapping = Object.fromEntries(entries); | |
| } catch { | |
| showError(t('模型映射必须是合法的 JSON 格式!')); | |
| return; | |
| } | |
| } | |
| localInputs.model_mapping = Object.keys(model_mapping).length === 0 ? '' : JSON.stringify(model_mapping); | |
| const currentModels = Array.isArray(localInputs.models) ? localInputs.models : []; | |
| localInputs.models = Array.from( | |
| new Set([ | |
| ...currentModels.map((m) => (m || '').trim()), | |
| ...Object.keys(model_mapping), | |
| ]) | |
| ).sort((a, b) => a.localeCompare(b)); |
🤖 Prompt for AI Agents
In web/src/pages/Channel/EditChannel.js around lines 550-568, tighten
model_mapping handling: parse localInputs.model_mapping with JSON.parse inside
try/catch and on parse error call showInfo(...) and return; assert parsed value
is a plain object (not array/null), then build a new mapping by iterating
Object.entries(parsed), trimming key and value (String(value).trim()), skipping
entries with empty keys or values, and collecting keys into a Set; create a
deterministically ordered mapping by sorting keys lexicographically and
constructing a new object in that order; set localInputs.model_mapping to '' if
the resulting mapping is empty or to JSON.stringify(orderedMapping) otherwise;
ensure localInputs.models is an array (default to []), merge it with the
collected keys, deduplicate (using a Set), sort the final array, and assign back
to localInputs.models; avoid unnecessary lodash usage and rely on native
Array/Object/Set methods.
|
是否与 #1658 重复? |
|
😱做其他部分顺手做了忘了查 |
PR 类型
PR 是否包含破坏性更新?
PR 描述
如图所示,保存时将自动填入重定向里的模型
Summary by CodeRabbit
New Features
Chores