Summary
-------
1. **Backend**
• `controller/model_meta.go`
– For prefix/suffix/contains rules, aggregate endpoints, bound channels, enable groups, and quota types across all matched models.
– When mixed billing types are detected, return `quota_type = -1` (unknown) instead of defaulting to volume-based.
2. **Frontend**
• `web/src/helpers/utils.js`
– `calculateModelPrice` now handles `quota_type = -1`, returning placeholder `'-'`.
• `web/src/components/table/model-pricing/view/card/PricingCardView.jsx`
– Billing tag logic updated: displays “按次计费” (times), “按量计费” (volume), or `'-'` for unknown.
• `web/src/components/table/model-pricing/view/table/PricingTableColumns.js`
– `renderQuotaType` shows “未知” for unknown billing type.
• `web/src/components/table/models/ModelsColumnDefs.js`
– Unified `renderQuotaType` to return `'-'` when type is unknown.
• `web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx`
– Group price table honors unknown billing type; pricing columns show `'-'` and neutral tag color.
3. **Utilities**
• Added safe fallback colours/tags for unknown billing type across affected components.
Impact
------
• Ensures correct data aggregation for non-exact model matches.
• Prevents UI from implying volume billing when actual type is ambiguous.
• Provides consistent placeholder display (`'-'` or “未知”) across cards, tables and modals.
No breaking API changes; frontend gracefully handles legacy values.
Summary:
• Updated `helpers/utils.js` to display the “group” label without a colon, ensuring consistent typography with other price elements.
Details:
1. `formatPriceInfo`
– Changed `{t('分组')}:` to `{t('分组')}` for a cleaner look.
– Keeps spacing intact between label and selected group name.
2. No functional impact; purely visual polish.
Summary:
• Updated `PricingTopSection.jsx` to conditionally render `PricingVendorIntroWithSkeleton` only when `isMobile` is false.
Details:
1. Wrapped vendor-intro block in `!isMobile` check, preventing unnecessary content on small screens.
2. Kept desktop experience unchanged; no impact on other features.
3. Lint check passed with no new issues.
Result:
Cleaner mobile UI with improved performance and visual focus.
Overview:
• Introduced a new “Model Tag” filter across pricing screens
• Refactored `usePricingFilterCounts` to eliminate duplicated logic
• Improved tag handling to be case-insensitive and deduplicated
• Extended utilities to reset & persist the new filter
Details:
1. Added `filterTag` state to `useModelPricingData` and integrated it into all filtering paths.
2. Created reusable `PricingTags` component using `SelectableButtonGroup`.
3. Incorporated tag filter into `PricingSidebar` and mobile `PricingFilterModal`, including reset support.
4. Enhanced `resetPricingFilters` (helpers/utils) to restore tag filter defaults.
5. Refactored `usePricingFilterCounts.js`:
• Centralized predicate `matchesFilters` to remove redundancy
• Normalized tag parsing via `normalizeTags` helper
• Memoized model subsets with concise filter calls
6. Updated lints – zero errors after refactor.
Result:
Users can now filter models by custom tags with consistent UX, and internal logic is cleaner, faster, and easier to extend.
Previously, the card view displayed a “-” whenever a model had no custom tags,
because `renderLimitedItems` returned a dash for an empty array.
Now the function is only invoked when `customTags.length > 0`, removing the
unwanted placeholder and keeping the UI clean.
File affected:
- web/src/components/table/model-pricing/view/card/PricingCardView.jsx
- Added unique keys for JSONEditor components to ensure proper re-rendering based on channelId.
- Implemented input reset to clear previous JSON field values when the modal is opened.
Backend:
- Model: Add `icon` field to `model.Model` (gorm: varchar(128)); auto-migrated via GORM.
- Pricing API: Extend `model.Pricing` with `icon` and populate from model meta in `GetPricing()`.
Frontend:
- EditModelModal: Add `icon` input (with @lobehub/icons helper link); wire into init/load/submit flows.
- ModelHeader / PricingCardView: Prefer rendering `model.icon`; fallback to `vendor_icon`; final fallback to initials avatar.
- Models table: Add leading “Icon” column, rendering `model.icon` or `vendor` icon via `getLobeHubIcon`.
Notes:
- Backward-compatible. Existing data without `icon` remain unaffected.
- No manual SQL needed; column is added by AutoMigrate.
Affected files:
- model/model_meta.go
- model/pricing.go
- web/src/components/table/models/modals/EditModelModal.jsx
- web/src/components/table/model-pricing/modal/components/ModelHeader.jsx
- web/src/components/table/model-pricing/view/card/PricingCardView.jsx
- web/src/components/table/models/ModelsColumnDefs.js
- Replace custom dots with Semi Badge types (success/danger/warning); add compact Progress bars
- Remove pie chart and related deps/config; move total key count and mode tags into the modal title
- Rework header using Row/Col; three equal stat cards (enabled/manual-disabled/auto-disabled)
- Integrate toolbar into Table title; wrap content with Card; use Table’s native empty state
- Make “Enable All” conditional (hidden when all keys are enabled), mirroring “Disable All”
- Unify numeric typography (current/total same size) for better readability
- Default page size set to 10; fallback to 10 when backend page_size is absent; page-size options: 10/20/50/100
- Cleanup imports and dead code (remove VChart and pie-spec logic)
- Minor spacing polish (extra bottom margin before table), no footer buttons
- Tokens/Users tables:
- Replaced status Switch with explicit Enable/Disable buttons in the operation column
- Unified button styles with Channels/Models (Disable: danger + small; Enable: default + small)
- Status column now shows a small Tag only; standardized labels (Enabled/Disabled/etc.); removed usage info
- New "Remaining/Total Quota" column:
- Wrapped in a white Tag; shows Remaining/Total with a progress bar
- Replaced Tooltip with Popover; contents use Typography.Paragraph with copyable values
- Copyable content excludes percentages (only numeric quota values are copied)
- Added padding to Popover content for better readability
- Tokens specifics:
- For unlimited quota, show a white Tag "Unlimited quota" with a Popover that displays copyable "Used quota"
- Cleanup:
- Removed Switch imports/handlers and unused code paths
- Eliminated console logs and redundant flags; simplified chats parsing
- Removed quota calculations from status renderers
Files:
- web/src/components/table/tokens/TokensColumnDefs.js
- web/src/components/table/users/UsersColumnDefs.js
* Added full GNU Affero General Public License v3 header at the top of `JSONEditor.js`.
* Removed unused `IconCode` and `IconRefresh` imports to eliminate dead code.
* Set `closeIcon={null}` and applied `!rounded-md` class for `Banner`, improving visual consistency and preventing unintended dismissal.
* Normalized whitespace and line-breaks for better readability and lint compliance.
* Integrate `useIsMobile` hook to detect mobile devices.
* Pagination now automatically:
* sets `size="small"` on mobile screens
* enables `showQuickJumper` for quicker navigation on small screens
* Desktop behaviour remains unchanged.
This commit updates the quick-fill endpoint templates used in the model and pre-fill group editors:
• `EditModelModal.jsx`
• `EditPrefillGroupModal.jsx`
Key changes
1. Added missing default endpoints defined in `common/endpoint_defaults.go`.
- `openai-response`
- `gemini`
- `jina-rerank`
2. Ensured each template entry includes both `path` and `method` for clarity.
Benefits
• Provides one-click access to every built-in upstream endpoint, reducing manual input.
• Keeps the UI definitions in sync with backend defaults, preventing mismatch errors.
Problem
Choosing a different token-group in the pricing sidebar only updated the filter but did **not** refresh the displayed group ratio in both the Table (`@table/`) and Card (`@card/`) views. The callback used by the sidebar changed `filterGroup` yet left `selectedGroup` untouched, so ratio columns/cards kept showing the previous value.
Solution
• `PricingSidebar.jsx`
– Accept new prop `handleGroupClick` (from `useModelPricingData`).
– Forward this callback to `PricingGroups` (`setFilterGroup={handleGroupClick}`) while retaining `setFilterGroup` for reset logic.
– Keeps both `filterGroup` filtering and `selectedGroup` state in sync via the single unified handler.
Result
Switching groups in the sidebar now simultaneously updates:
1. the model list filtering, and
2. the ratio information shown in both pricing Table and Card views.
No UI/UX regression; linter passes.
The sidebar’s admin section now displays “Channel Management” before “Model Management” to better reflect common user workflows and improve navigation clarity.
Details:
• Updated `web/src/components/layout/SiderBar.js`
– Re-ordered items in `adminItems` array so `channel` precedes `models`.
• No logic or route changes; this is purely a UI ordering adjustment.
This change enhances usability for administrators by presenting frequently accessed channel settings first.
Changes
1. ModelPricingTable.jsx
• Compute `autoChain` as the intersection of `autoGroups` and the model’s `enable_groups` (order preserved).
• Display the chain banner only when `autoChain.length > 0`; banner shows the reduced path (e.g. `a → c → e`).
• Dropped obsolete `selectedGroup` prop; all callers updated.
2. ModelDetailSideSheet.jsx / PricingPage.jsx
• Removed forwarding of deleted `selectedGroup` prop.
Outcome
– “Auto group routing” appears only for models that actually participate in the chain, avoiding empty or irrelevant banners.
– Codebase simplified by eliminating an unused prop.
Detailed changes
Backend
• `controller/pricing.go` now includes `auto_groups` in `/api/pricing` response, sourced from `setting.AutoGroups`.
Frontend
• `useModelPricingData.js`
– Parses `auto_groups` and exposes `autoGroups` state.
• `PricingPage.jsx` → `ModelDetailSideSheet.jsx` → `ModelPricingTable.jsx`
– Thread `autoGroups` through component tree.
• `ModelPricingTable.jsx`
– Removes deprecated `getGroupDescription` / `Tooltip`.
– Filters out `auto` when building price table rows.
– Renders a descriptive banner: “auto 分组调用链路 → auto → group1 → …”, clarifying fallback order without showing prices.
• Minor i18n tweak: adds `auto分组调用链路` key for the banner text.
Why
Users were confused by the “auto” tag appearing alongside regular groups with no price.
This change:
1. Makes the routing chain explicit.
2. Keeps the pricing table focused on billable groups.
No breaking API changes; existing clients can ignore the new `auto_groups` field.
- Added support to fetch and render “model prefill groups” in `EditChannelModal.jsx`
- Users can now click a group button to instantly merge that group’s models into the models Select
- Mirrors the prefill-group UX used for tags/endpoints in `EditModelModal.jsx`
Details
- UI/UX:
- Renders one button per model group inside the models field’s extra actions
- Clicking a button merges its items into the selected models (trimmed, deduplicated), updating immediately
- Non-destructive and works alongside existing actions (fill related/all models, fetch upstream, clear, copy)
- API:
- GET `/api/prefill_group?type=model`
- Handles `items` as either an array or a JSON string array for robustness
- If request fails or returns no groups, buttons are simply not shown
- i18n:
- Reuses existing i18n; group names come from backend and are displayed as-is
- Performance:
- Simple set merge; negligible overhead
- Backward compatibility:
- No changes required on the backend or elsewhere; feature is additive
- Testing (manual):
1) Open channel modal (new or edit) and navigate to the Models section
2) Confirm model group buttons render when groups are configured
3) Click a group button → models Select updates with merged models (no duplicates)
4) Verify other actions (fill related/all, fetch upstream, clear, copy) still work
5) Close/reopen modal → state resets as expected
Implementation
- `web/src/components/table/channels/modals/EditChannelModal.jsx`
- Added `modelGroups` state and `fetchModelGroups()` (GET `/api/prefill_group?type=model`)
- Invoked `fetchModelGroups()` when the modal opens
- Rendered group buttons in the models Select `extraText`, merging group items into current selection
Chore
- Verified no new linter errors were introduced.
- Move model price column to fixed right position
- Convert endpoint types column from fixed to regular column
- Reorder columns: endpoint types now appears before ratio column
- Improve table layout and user experience for pricing data viewing
Changes made to web/src/components/table/model-pricing/view/table/PricingTableColumns.js:
* Removed `fixed: 'right'` from endpointColumn
* Added `fixed: 'right'` to priceColumn
* Updated column order in the columns array
- Why:
- Eliminate `gorm.io/datatypes` for a single field and fix scan errors when drivers return JSON as string.
- Prevent JSONEditor manual mode from locking on invalid JSON and from appending stray characters after “Fill Template”.
- What:
- Backend (`model/prefill_group.go`):
- Replaced `datatypes.JSON` with `JSONValue` (based on `json.RawMessage`) for `PrefillGroup.Items`.
- Implemented `sql.Scanner` and `driver.Valuer` to accept both `[]byte` and `string`.
- Implemented `MarshalJSON`/`UnmarshalJSON` to preserve raw JSON in API without base64.
- Converted comments to Chinese.
- Frontend (`web/src/components/common/ui/JSONEditor.js`):
- Added `manualText` buffer for manual mode to avoid input being overridden by external value.
- Only propagate `onChange` when manual text is valid JSON; otherwise show error but do not block typing.
- Safe manual-mode rendering: derive rows from `manualText` and avoid calling `split` on non-strings.
- Improved mode toggle: populate `manualText` from visual data; validate before switching back to visual.
- Fixed “Fill Template” to sync `manualText`, `jsonData`, and `onChange` to avoid stray trailing characters.
- Impact:
- Resolves: “unsupported Scan, storing driver.Value type string into type *json.RawMessage”.
- Resolves: `value.split is not a function` in manual mode.
- Resolves: extra `s` appended after inserting template.
- API shape and DB column type remain the same (`gorm:"type:json"`); no `go.mod` changes.
- Lints pass for modified files.
Files changed:
- model/prefill_group.go
- web/src/components/common/ui/JSONEditor.js
- Why:
- Avoid introducing `gorm.io/datatypes` for a single field.
- Align with existing pattern (`ChannelInfo`, `Properties`) using `Scanner`/`Valuer`.
- Fix runtime error when drivers return JSON as string.
- What:
- Introduced `JSONValue` (based on `json.RawMessage`) implementing `sql.Scanner` and `driver.Valuer`, with `MarshalJSON`/`UnmarshalJSON` to preserve raw JSON in API.
- Updated `PrefillGroup.Items` to use `JSONValue` with `gorm:"type:json"`.
- Localized comments in `model/prefill_group.go` to Chinese.
- Impact:
- Resolves “unsupported Scan, storing driver.Value type string into type *json.RawMessage”.
- Works with MySQL/Postgres/SQLite whether JSON is returned as `[]byte` or `string`.
- API and DB schema remain unchanged; no `go.mod` changes; lints pass.
Files changed:
- model/prefill_group.go
- Move `items` field (`JSONEditor` for endpoint type, `Form.TagInput` otherwise) into the first “Basic Information” card
- Remove the second “Content Configuration” card and its header; consolidate to a single-card layout
- Preserve form initialization, validation, and submit logic; API payload structure remains unchanged
- Improves clarity and reduces visual clutter without altering behavior
- Lint passes
Affected file:
- `web/src/components/table/models/modals/EditPrefillGroupModal.jsx`
No breaking changes.
- Why
- Needed to separate help text from action buttons in JSONEditor for better layout and UX.
- Models table should robustly render both new object-based endpoint mappings and legacy arrays.
- Columns should re-render when vendor map changes.
- Minor import cleanups for consistency.
- What
- JSONEditor.js
- Added optional prop extraFooter to render content below the extraText divider.
- Kept extraText rendered via Divider; extraFooter appears on the next line for clear separation.
- EditModelModal.jsx
- Moved endpoint group buttons from extraText into extraFooter to display under the helper text.
- Kept merge-logic: group items are merged into current endpoints JSON with key override semantics.
- Consolidated lucide-react imports into a single line.
- ModelsColumnDefs.js
- Made endpoint renderer resilient:
- Supports object-based JSON (keys as endpoint types) and legacy array format.
- Displays keys/items as tags and limits the number shown; uses stringToColor for visual consistency.
- Consolidated Semi UI imports into a single line.
- ModelsTable.jsx
- Fixed columns memoization dependency to include vendorMap, ensuring re-render when vendor data changes.
- Notes
- Backward-compatible: extraFooter is additive; existing JSONEditor usage remains unchanged.
- No API changes to backend.
- No linter errors introduced.
- Files touched
- web/src/components/common/ui/JSONEditor.js
- web/src/components/table/models/modals/EditModelModal.jsx
- web/src/components/table/models/ModelsColumnDefs.js
- web/src/components/table/models/ModelsTable.jsx
- Impact
- Clearer UI for endpoint editing (buttons now below helper text).
- Correct endpoints display for object-based mappings in models list.
- More reliable reactivity when vendor data updates.
Backend (Go)
- Include custom endpoints in each model’s SupportedEndpointTypes by parsing Model.Endpoints (JSON) and appending keys alongside native endpoint types.
- Build a global supportedEndpointMap map[string]EndpointInfo{path, method} by:
- Seeding with native defaults.
- Overriding/adding from models.endpoints (accepts string path → default POST, or {path, method}).
- Expose supported_endpoint at the top level of /api/pricing (vendors-like), removing per-model duplication.
- Fix default path for EndpointTypeOpenAIResponse to /v1/responses.
- Keep concurrency/caching for pricing retrieval intact.
Frontend (React)
- Fetch supported_endpoint in useModelPricingData and propagate to PricingPage → ModelDetailSideSheet → ModelEndpoints.
- ModelEndpoints
- Resolve path+method via endpointMap; replace {model} with actual model name.
- Fix mobile visibility; always show path and HTTP method.
- JSONEditor
- Wrap with Form.Slot to inherit form layout; simplify visual styles.
- Use Tabs for “Visual” / “Manual” modes.
- Unify editors: key-value editor now supports nested JSON:
- “+” to convert a primitive into an object and add nested fields.
- Add “Convert to value” for two‑way toggle back from object.
- Stable key rename without reordering rows; new rows append at bottom.
- Use Row/Col grid for clean alignment; region editor uses Form.Slot + grid.
- Editing flows
- EditModelModal / EditPrefillGroupModal use JSONEditor (editorType='object') for endpoint mappings.
- PrefillGroupManagement renders endpoint group items by JSON keys.
Data expectations / compatibility
- models.endpoints should be a JSON object mapping endpoint type → string path or {path, method}. Strings default to POST.
- No schema changes; existing TEXT field continues to store JSON.
QA
- /api/pricing now returns custom endpoint types and global supported_endpoint.
- UI shows both native and custom endpoints; paths/methods render on mobile; nested editing works and preserves order.
Add visual distinction for enabled/disabled models by applying different
background colors to table rows based on model status. This implementation
follows the same pattern used in ChannelsTable for consistent user experience.
Changes:
- Modified handleRow function in useModelsData.js to include row styling
- Disabled models (status !== 1) now display with gray background using
--semi-color-disabled-border CSS variable
- Enabled models (status === 1) maintain normal background color
- Preserved existing row click selection functionality
This enhancement improves the visual feedback for users to quickly identify
which models are active vs inactive in the models management interface.
This commit significantly refactors the `EditModelModal` component to streamline the user interface and enhance usability, aligning it with the interaction patterns found elsewhere in the application.
- **Consolidated Layout:** Merged the "Vendor Information" and "Feature Configuration" sections into a single "Basic Information" card. This simplifies the form, reduces clutter, and makes all settings accessible in one view.
- **Improved Prefill Groups:** Replaced the separate `Select` dropdowns for tag and endpoint groups with a more intuitive button-based system within the `extraText` of the `TagInput` components.
- **Additive Button Logic:** The prefill group buttons now operate in an additive mode. Users can click multiple group buttons to incrementally add tags or endpoints, with duplicates being automatically handled.
- **Clear Functionality:** Added "Clear" buttons for both tags and endpoints, allowing users to easily reset the fields.
- **Code Cleanup:** Removed the unused `endpointOptions` constant and unnecessary icon imports (`Building`, `Settings`) to keep the codebase clean.
Summary
• Backend
– Moved duplicate-name validation and total vendor-count aggregation from controllers (`controller/model_meta.go`, `controller/vendor_meta.go`, `controller/prefill_group.go`) to model layer (`model/model_meta.go`, `model/vendor_meta.go`, `model/prefill_group.go`).
– Added `GetVendorModelCounts()` and `Is*NameDuplicated()` helpers; controllers now call these instead of duplicating queries.
– API response for `/api/models` now returns `vendor_counts` with per-vendor totals across all pages, plus `all` summary.
– Removed redundant checks and unused imports, eliminating `go vet` warnings.
• Frontend
– `useModelsData.js` updated to consume backend-supplied `vendor_counts`, calculate the `all` total once, and drop legacy client-side counting logic.
– Simplified initial data flow: first render now triggers only one models request.
– Deleted obsolete `updateVendorCounts` helper and related comments.
– Ensured search flow also sets `vendorCounts`, keeping tab badges accurate.
Why
This refactor enforces single-responsibility (aggregation in model layer), delivers consistent totals irrespective of pagination, and removes redundant client queries, leading to cleaner code and better performance.
Summary
-------
1. Pricing generation
• `model/pricing.go`: skip any model whose `status != 1` when building
`pricingMap`, ensuring disabled models are never returned to the
front-end.
2. Cache refresh placement
• `controller/model_meta.go`
– Removed `model.RefreshPricing()` from pure read handlers
(`GetAllModelsMeta`, `SearchModelsMeta`).
– Kept refresh only in mutating handlers
(`Create`, `Update`, `Delete`), guaranteeing data is updated
immediately after an admin change while avoiding redundant work
on every read.
Result
------
Front-end no longer receives information about disabled models, and
pricing cache refreshes occur exactly when model data is modified,
improving efficiency and consistency.