- Truncate long labels via pure CSS and always show full text in a Tooltip
- Ensure the right-side Tag is never truncated and remains fully visible
- Simplify implementation: remove overflow detection and ResizeObserver
- Use minimal markup with sbg-button/sbg-inner/sbg-label to enable shrinking
- Add global rules to allow `.semi-button-content` to shrink and ellipsize
Files:
- web/src/components/common/ui/SelectableButtonGroup.jsx
- web/src/index.css
No API changes; visuals improved and code complexity reduced.
Frontend
- Models table (model management):
- Render billing types with the same segmented list component (renderLimitedItems) used by tags and endpoints
- Display quota_types as an array with capped items (maxDisplay: 3) and graceful fallback for unknown types
- Pricing view (unchanged by request):
- Revert to single-value quota_type rendering and sorter
- Keep ratio display logic based on quota_type only
Files
- web/src/components/table/models/ModelsColumnDefs.js
- web/src/components/table/model-pricing/view/table/PricingTableColumns.js
Notes
- This commit only adjusts the model management UI rendering; pricing views remain as-is
Backend
- Add GetBoundChannelsByModelsMap to batch-fetch bound channels via a single JOIN (Distinct), compatible with SQLite/MySQL/PostgreSQL
- Replace per-record enrichment with a single-pass enrichModels to avoid N+1 queries; compute unions for prefix/suffix/contains matches in memory
- Change Model.QuotaType to QuotaTypes []int and expose quota_types in responses
- Add GetModelQuotaTypes for cached O(1) lookups; exact models return a single-element array
- Sort quota_types for stable output order
- Remove unused code: GetModelByName, GetBoundChannels, GetBoundChannelsForModels, FindModelByNameWithRule, buildPrefixes, buildSuffixes
- Clean up redundant comments, keeping concise and readable code
Frontend
- Models table: switch to quota_types, render multiple billing modes ([0], [1], [0,1], future values supported)
- Pricing table: switch to quota_types; ratio display now checks quota_types.includes(0); array rendering for billing tags
Compatibility
- SQL uses standard JOIN/IN/Distinct; works across SQLite/MySQL/PostgreSQL
- Lint passes; no DB schema changes (quota_types is a JSON response field only)
Breaking Change
- API field renamed: quota_type -> quota_types (array). Update clients accordingly.
Why
Previous versions created single-column UNIQUE constraints (`models.model_name`, `vendors.name`).
After introducing composite indexes on `(model_name, deleted_at)` and `(name, deleted_at)` for soft-delete support, those legacy constraints could still exist in user databases.
When a record was soft-deleted and re-inserted with the same name, MySQL raised `Error 1062 … for key 'models.model_name'`.
What
• In `migrateDB` and `migrateDBFast` paths of `model/main.go`, proactively drop:
– `models.uk_model_name` and fallback `models.model_name`
– `vendors.uk_vendor_name` and fallback `vendors.name`
• Keeps existing helper `dropIndexIfExists` to ensure the operation is MySQL-only and error-free when indexes are already absent.
Result
Startup migration now removes every possible legacy UNIQUE index, ensuring composite index strategy works correctly.
Users can soft-delete and recreate models/vendors with identical names without hitting duplicate-entry errors.
Summary
-------
1. **Backend**
• `model/model_meta.go`
– Add `GetBoundChannelsForModels([]string)` to retrieve channels for multiple models in a single SQL (`IN (?)`) and deduplicate with `GROUP BY`.
• `controller/model_meta.go`
– In non-exact `fillModelExtra`:
– Remove per-model `GetBoundChannels` calls.
– Collect matched model names, then call `GetBoundChannelsForModels` once and merge results into `channelSet`.
– Minor cleanup on loop logic; channel aggregation now happens after quota/group/endpoint processing.
Impact
------
• Eliminates N+1 query pattern for prefix/suffix/contains rules.
• Reduces DB round-trips from *N + 1* to **1**, markedly speeding up the model-management list load.
• Keeps existing `GetBoundChannels` API intact for single-model scenarios; no breaking changes.
Summary
-------
1. **Backend**
• `model/model_meta.go`
– Add `MatchedModels []string` and `MatchedCount int` (ignored by GORM) to expose matching details in API responses.
• `controller/model_meta.go`
– When processing prefix/suffix/contains rules in `fillModelExtra`, collect every matched model name, fill `MatchedModels`, and calculate `MatchedCount`.
2. **Frontend**
• `web/src/components/table/models/ModelsColumnDefs.js`
– Import `Tooltip`.
– Enhance `renderNameRule` to:
– Display tag text like “前缀 5个模型” for non-exact rules.
– Show a tooltip listing all matched model names on hover.
Impact
------
Users now see the total number of concrete models aggregated under each prefix/suffix/contains rule and can inspect the exact list via tooltip, improving transparency in model management.
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