- Add Alibaba Cloud to trusted partners section in README.md
- Add Alibaba Cloud to trusted partners section in README.en.md
- Use aliyun.svg logo and https://www.aliyun.com/ as website link
- Maintain consistent formatting with existing partners
- Move card titles (earnings, total revenue, invitations) to Card title prop for consistency
- Remove custom color classes in favor of Semi-UI's built-in type system
- Standardize Text component usage with strong and tertiary types
- Improve code maintainability and visual consistency across all cards
- Align structure with existing reward description card pattern
- Rename React components/pages/utilities that contain JSX to `.jsx` across `web/src`
- Update import paths and re-exports to match new `.jsx` extensions
- Fix Vite entry by switching `web/index.html` from `/src/index.js` to `/src/index.jsx`
- Verified remaining `.js` files are plain JS (hooks/helpers/constants) and do not require JSX
- No runtime behavior changes; extension and reference alignment only
Context: Resolves the Vite pre-transform error caused by the stale `/src/index.js` entry after migrating to `.jsx`.
Add smooth scale-up animation effect when hovering over the header logo to enhance user interaction experience.
Changes:
- Add `group` class to Link element to enable Tailwind group hover functionality
- Update transition from `transition-opacity` to `transition-all` for smooth scaling
- Increase hover scale from `scale-105` to `scale-110` (10% enlargement)
- Maintain 200ms transition duration for optimal user experience
The logo now smoothly scales to 110% size on hover and returns to original size when mouse leaves, providing better visual feedback for user interactions.
- Restructure avatar-name-tags layout to left-right alignment
- Avatar positioned on the left
- Name aligned to avatar top, tags aligned to avatar bottom
- Remove margin-top usage in favor of flexbox justify-between
- Simplify desktop statistics cards to single-line layout
- Format as "icon + label: value" without stacked layout
- Remove custom color classes for cleaner styling
- Update UI component styling
- Increase tag size from small to large
- Reduce cover height from responsive to fixed 32
- Add Badge import for future enhancements
- Clean up icon and text color classes
- Maintain responsive behavior and accessibility
Summary
• Extracted `LogoSection`, `NavLinks`, `UserArea`, and `ActionButtons` from `HeaderBar.js`, reducing complexity and improving readability.
• Removed unused state, handlers, and redundant imports from `HeaderBar.js`.
• Simplified mobile/desktop logic:
– Menu icon now shows only on mobile `/console` routes.
– Logo, system name, and mode tags appear on all desktop screens and on mobile non-console pages.
• Reworked skeleton loaders:
– Narrower width on mobile (`40 px`) and clearer spacing (`p-1`).
• Added global `.scrollbar-hide` utility in `index.css` to enable scrollable areas without visible scrollbars.
• Ensured nav bar is horizontally scrollable across all breakpoints.
• Cleaned up language-switch, New Year, and notice handlers; consolidated side effects.
• Updated imports and internal calls after component extraction.
• Passed required props to new sub-components and removed obsolete ones.
• Confirmed zero linter warnings after refactor.
Why
Breaking the monolithic header into focused components makes future updates simpler, facilitates isolation testing, and aligns with the existing component architecture under `components/`. The UI tweaks provide a better mobile experience and consistent styling across devices.
Notes
No backend changes required. All routes and contexts remain untouched.
• Introduced `useMinimumLoadingTime` to `MjLogsActions.jsx`, guaranteeing a minimum skeleton display duration for smoother UX
• Refactored loading state UI to use wrapped `Skeleton` with placeholder, matching the implementation in `UsageLogsActions.jsx`
• Kept existing banner, admin checks, and compact-mode toggle intact while streamlining the code
• Ensures consistent loading indicators across usage- and MJ-log tables
Summary of changes
1. SetupWizard.jsx
• Center card (`min-h-screen flex items-center justify-center`) and remove top margin.
• Merge step indicator/content into single card; added `Divider` separator.
• Added sweep-shine animation to current step title via existing `shine-text` class.
• Simplified imports (removed Avatar / Typography) and deleted unused modal state.
2. Step components
• Stripped outer `Card` and header sections from `DatabaseStep.jsx`, `AdminStep.jsx`, `UsageModeStep.jsx`, `CompleteStep.jsx` to fit single-card layout.
• Removed unused imports and props.
3. Components cleanup
• Deleted obsolete files:
- `components/setup/components/SetupSteps.jsx`
- `components/setup/components/modals/UsageModeInfoModal.jsx`
• Updated `setup/index.jsx` exports accordingly.
4. Styling
• Ensured global sweep-shine effect already present in `index.css` is reused for step titles.
5. i18n
• Pruned unused translation keys related to removed components from `i18n/locales/en.json`.
6. Miscellaneous
• Removed redundant Avatar/Icon imports from multiple files.
• All linter checks pass with no new warnings.
This commit consolidates the initialization flow into a cleaner, centered single-card wizard, adds visual polish, and reduces dead code for easier maintenance.
- AccountManagement.js
- Prevent action button from shifting when account IDs are long by adding gap, min-w-0, and flex-shrink-0; keep buttons in a fixed position.
- Add copyable Popover for account identifiers (email/GitHub/OIDC/Telegram/LinuxDO) using Typography.Paragraph with copyable; reveal full text on hover.
- Ensure ellipsis works by rendering the popover trigger as `block max-w-full truncate`.
- Import Popover and wire up `renderAccountInfo` across all binding rows.
- UserInfoHeader.js
- Apply unified `with-pastel-balls` background to match PricingVendorIntro.
- Remove legacy absolute-positioned circles and top gradient bar to avoid visual overlap.
- RechargeCard.jsx
- Colorize non-Alipay/WeChat/Stripe payment icons using backend `pay_methods[].color`; fallback to `var(--semi-color-text-2)`.
- Add `showClear` to the redemption code input for quicker clearing.
Notes:
- No linter errors introduced.
- i18n strings and behavior remain unchanged except for improved UX and visual consistency.
- Add RightStatsCard and place it in RechargeCard header
- Shows current balance, historical spend, and request count
- Mobile: stacks under title; three metrics split equally (flex-1); vertical dividers hidden on small screens
- Remove extra margins; small card styling
- RechargeCard
- Replace redeem code Input icon with Semi UI IconGift
- Style “Payable amount” number in red and bold; keep same style in confirm modal
- Always render payment methods as Cards (remove Button variant) with adaptive grid
- Use brand color icons: SiAlipay (#1677FF), SiWechat (#07C160), SiStripe (#635BFF)
- Replace Stripe icon with SiStripe
- Integrate RightStatsCard props; adjust header to flex-col on mobile and flex-row on desktop
- Hide Banner close button when online top-up is disabled (closeIcon={null})
- InvitationCard
- Simplify to match RechargeCard’s minimalist slate style
- Use Card title for “Rewards” and place content directly in body
- Improve link input and copy button; use Badge dots for bullet points
- TopUp index
- Remove separate right-column stats card; pass userState and renderQuota to RechargeCard
- Cleanup
- Lint passes; no functional changes to APIs or business logic
Home started loading `/assets/visactor-*.js` due to static imports of `@visactor/react-vchart` and the Semi theme in dashboard components/hooks. This change moves chart dependencies to lazy/dynamic imports so they load only on dashboard routes.
Changes
- StatsCards.jsx: replace static `VChart` import with `React.lazy` + `Suspense` (fallback: null)
- ChartsPanel.jsx: replace static `VChart` import with `React.lazy` + `Suspense` (fallback: null)
- useDashboardCharts.js: remove static `initVChartSemiTheme` import; dynamically import and initialize the theme inside `useEffect` with a cancel guard
Behavior
- Home page no longer downloads `visactor` chunks on first load
- Chart libraries are fetched only when visiting `/console` (dashboard)
- No functional changes to chart rendering
Files
- web/src/components/dashboard/StatsCards.jsx
- web/src/components/dashboard/ChartsPanel.jsx
- web/src/hooks/dashboard/useDashboardCharts.js
Verification
- Build the app (`npm run build`) and open `/`: no `/assets/visactor-*.js` requests
- Navigate to `/console`: `visactor` chunks load and charts render as expected
Breaking Changes
- None
Follow-ups
- If needed, further trim homepage bundle by reducing heavy icon sets on the hero section
- Split the 1554-line PersonalSetting.js file into smaller, maintainable components
- Created organized folder structure under personal/:
- components/: UserInfoHeader for shared user info display
- tabs/: ModelsList, AccountBinding, SecuritySettings, NotificationSettings
- modals/: EmailBindModal, WeChatBindModal, AccountDeleteModal, ChangePasswordModal
- Refactored main PersonalSetting component to use composition pattern
- Improved code maintainability and separation of concerns
- Added collapsible prop to ModelsList tabs for better UX
- Fixed import path for TwoFASetting component in SecuritySettings
- Preserved all existing functionality and user interactions
This refactoring reduces the main file from 1554 to 484 lines and makes
the codebase more modular, testable, and easier to maintain.
- Add comprehensive i18n support to TwoFASetting.js component
- Add all required English translations to en.json for 2FA settings
- Update component to accept t function as prop and use translation keys
- Fix prop passing in PersonalSetting.js to provide t function
- Maintain all existing UI improvements and functionality
- Support both Chinese and English interfaces for:
* Main 2FA settings card with status indicators
* Setup modal with guided steps (QR scan, backup codes, verification)
* Disable 2FA modal with impact warnings and confirmation
* Regenerate backup codes modal with success states
* All buttons, placeholders, messages, and notifications
- Follow project i18n conventions using t('key') pattern
- Ensure seamless language switching for enhanced user experience
This enables the 2FA settings to be fully localized while preserving
the modern UI design and improved user workflow from previous updates.
- Replace lucide-react icons with Semi UI icons for consistency
- Implement Steps component for guided 2FA setup modal flow
- Redesign disable and regenerate backup codes modals to match setup modal style
- Extract duplicate backup codes display logic into reusable BackupCodesDisplay component
- Move modal navigation buttons to proper footer parameter following Semi UI standards
- Replace custom styled dots with Badge components (warning/danger/success types)
- Use Banner and Divider components for better visual hierarchy
- Remove redundant modal step titles and download functionality
- Apply consistent rounded corners, spacing, and color scheme across all modals
- Improve responsive design with maxWidth constraints
This unifies the 2FA settings visual design with other settings pages and
enhances user experience through better component usage and layout structure.
The primary "Chat" button on the tokens table navigated to a 404 page
because it passed incorrect arguments to onOpenLink (using a raw
localStorage value instead of the parsed chat value).
Changes:
- Build chatsArray with an explicit `value` for each item.
- Use the first item's `name` and `value` for the main button, matching
the dropdown behavior.
- Preserve existing error handling when no chats are configured.
Impact:
- Main "Chat" button now opens the correct link, consistent with the
dropdown action.
- No API/schema changes, no UI changes.
File:
- web/src/components/table/tokens/TokensColumnDefs.js
Verification:
- Manually verified primary button and dropdown both navigate correctly.
- Linter passes with no issues.
Summary:
• Removed early return in `handleChange` that blocked controlled value updates while an Input Method Editor (IME) was composing text.
• Ensures that Chinese (and other IME-based) characters appear immediately in the “Fuzzy Search Model Name” field.
• No change to downstream filtering logic—`searchValue` continues to drive model list filtering as before.
Files affected:
web/src/hooks/model-pricing/useModelPricingData.js
* Add `searchValue` to every dependency array in `usePricingFilterCounts`
to ensure group/vendor/tag counts and disabled states update dynamically
while performing fuzzy search.
* Refactor `PricingTopSection` search box into a controlled component:
- Accept `searchValue` prop and bind it to `Input.value`
- Extend memo dependencies to include `searchValue`
This keeps the UI in sync with state changes triggered by `handleChange`.
* Guarantee that `resetPricingFilters` clears the search field by
leveraging the new controlled input.
As a result, sidebar counters/disabled states now react to search input,
and the “Reset” button fully restores default filters without leaving the
search term visible.
Summary
• Ensure “Group Ratio” shows correctly when “All” groups are selected.
• Eliminate redundant price calculations in both card and table views.
Details
1. PricingCardView.jsx
• Removed obsolete renderPriceInfo function.
• Calculate priceData once per model and reuse for header price string and footer ratio block.
• Display priceData.usedGroupRatio as the group ratio fallback.
2. PricingTableColumns.js
• Introduced WeakMap-based cache (getPriceData) to compute priceData only once per row.
• Updated ratioColumn & priceColumn to reuse cached priceData.
• Now displays priceData.usedGroupRatio, preventing empty cells for “All” group.
Benefits
• Correct visual output for group ratio across all views.
• Reduced duplicate calculations, improving render performance.
• Removed dead code, keeping components clean and maintainable.
- Add new Forbidden page at /forbidden (`web/src/pages/Forbidden/index.js`)
- Use Semi-UI Empty with IllustrationNoAccess (250x250)
- Update i18n description to: '您无权访问此页面,请联系管理员~'
- Align visual style with existing 404 page
- Introduce `AdminRoute` in `web/src/helpers/auth.js`
- Use `UserContext`/localStorage; redirect to `/forbidden` when `!user` or `user.role < 10`
- Protect console/admin routes with `AdminRoute` and register `/forbidden` in `web/src/App.js`
- Update `web/src/i18n/locales/en.json`
- Add English translation for the new forbidden message
- Remove legacy "没有权限" entry
- Lint passes; no runtime errors observed
- Switch Collapse from controlled (activeKey) to uncontrolled (defaultActiveKey) so user toggling works
- Add a stable key to reset Collapse state when tab/category changes
- Default all panels to collapsed via defaultActiveKey: []
- Preserve Panel itemKey for consistent behavior
- No linter errors introduced
Scope: web/src/components/table/channels/modals/ModelSelectModal.jsx
- 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
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
Replace legacy single-column unique indexes with composite unique indexes on
(name, deleted_at) and introduce a safe index drop utility to eliminate
duplicate-key errors and noisy MySQL 1091 warnings.
WHAT
• model/model_meta.go
- Model.ModelName → `uniqueIndex:uk_model_name,priority:1`
- Model.DeletedAt → `index; uniqueIndex:uk_model_name,priority:2`
• model/vendor_meta.go
- Vendor.Name → `uniqueIndex:uk_vendor_name,priority:1`
- Vendor.DeletedAt → `index; uniqueIndex:uk_vendor_name,priority:2`
• model/main.go
- Add `dropIndexIfExists(table, index)`:
• Checks `information_schema.statistics`
• Drops index only when present (avoids Error 1091)
- Invoke helper in `migrateDB` & `migrateDBFast`
- Remove direct `ALTER TABLE … DROP INDEX …` calls
WHY
• Users received `Error 1062 (23000)` when re-creating a soft-deleted
model/vendor because the old unique index enforced uniqueness on name alone.
• Directly dropping nonexistent indexes caused MySQL `Error 1091` noise.
HOW
• Composite unique indexes `(model_name, deleted_at)` / `(name, deleted_at)`
respect GORM soft deletes.
• Safe helper ensures idempotent migrations across environments.
RESULT
• Users can now delete and re-add the same model or vendor without manual SQL.
• Startup migration runs quietly across MySQL, PostgreSQL, and SQLite.
• No behavior changes for existing data beyond index updates.
TEST
1. Add model “deepseek-chat” → delete (soft) → re-add → success.
2. Add vendor “DeepSeek” → delete (soft) → re-add → success.
3. Restart service twice → no duplicate key or 1091 errors.
* 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.
Ensure models and vendors can be re-created after soft deletion by switching to composite unique indexes on (name, deleted_at) and cleaning up legacy single-column unique indexes on MySQL.
Why
- MySQL raised 1062 duplicate key errors when re-adding a soft-deleted model/vendor because the legacy unique index enforced uniqueness on the name column alone (uk_model_name / uk_vendor_name), despite soft deletes.
- Users encountered errors such as:
- Error 1062 (23000): Duplicate entry 'deepseek-chat' for key 'models.uk_model_name'
- Error 1062 (23000): Duplicate entry 'DeepSeek' for key 'vendors.uk_vendor_name'
How
- Model indices:
- model/model_meta.go:
- Model.ModelName → gorm: uniqueIndex:uk_model_name,priority:1
- Model.DeletedAt → gorm: index; uniqueIndex:uk_model_name,priority:2
- Vendor indices:
- model/vendor_meta.go:
- Vendor.Name → gorm: uniqueIndex:uk_vendor_name,priority:1
- Vendor.DeletedAt → gorm: index; uniqueIndex:uk_vendor_name,priority:2
- Migration (automatic, idempotent):
- model/main.go (migrateDB, migrateDBFast):
- On MySQL, drop legacy single-column unique indexes if present:
- ALTER TABLE models DROP INDEX uk_model_name;
- ALTER TABLE vendors DROP INDEX uk_vendor_name;
- Then run AutoMigrate to create composite unique indexes.
- Missing-index errors are ignored to keep the migration safe to run multiple times.
Result
- Users can delete and re-add the same model/vendor name without manual SQL.
- Migration runs automatically at startup; no user action required.
- PostgreSQL and SQLite remain unaffected.
Files changed
- model/model_meta.go
- model/vendor_meta.go
- model/main.go (migrateDB, migrateDBFast)
Testing
- Create model "deepseek-chat" → delete (soft) → re-create → succeeds.
- Create vendor "DeepSeek" → delete (soft) → re-create → succeeds.
Backward compatibility
- Data remains intact; only index definitions are updated.
- Behavior is unchanged except for fixing the uniqueness constraint with soft deletes.
* 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
- Why: Avoid adding `gorm.io/datatypes` for a single field; the rest of the codebase does not use it, and using the standard library keeps dependencies lean.
- What:
- Switched `PrefillGroup.Items` from `datatypes.JSON` to `json.RawMessage`.
- Updated imports in `model/prefill_group.go` to use `encoding/json` and removed the unused `gorm.io/datatypes`.
- Preserved `gorm:"type:json"` so DB column behavior remains the same.
- Impact:
- API response/request shape for `items` remains unchanged (still JSON).
- DB schema behavior is unchanged; GORM migration continues to handle the field as JSON.
- No other references to `datatypes` exist; no `go.mod` changes needed.
- Lints pass for the modified file.
Files changed:
- model/prefill_group.go
No breaking changes.
- 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.
Summary
-------
This commit unifies soft-delete behaviour across meta tables and
introduces an in-memory cache for model pricing look-ups to improve
throughput under high concurrency.
Details
-------
Soft-delete consistency
• PrefillGroup / Vendor / Model
– Added `gorm.DeletedAt` field with `json:"-" gorm:"index"`.
– Replaced plain `uniqueIndex` with partial unique indexes
`uniqueIndex:<name>,where:deleted_at IS NULL`
allowing duplicate keys after logical deletion while preserving
uniqueness for active rows.
• Imports updated to include `gorm.io/gorm`.
• JSON output now hides `deleted_at`, matching existing tables.
High-throughput pricing cache
• model/pricing.go
– Added thread-safe maps `modelEnableGroups` & `modelQuotaTypeMap`
plus RW-mutex for O(1) access.
– `updatePricing()` now refreshes these maps alongside `pricingMap`.
• model/model_extra.go
– Rewrote `GetModelEnableGroups` & `GetModelQuotaType` to read from
the new maps, falling back to automatic refresh via `GetPricing()`.
Misc
• Retained `RefreshPricing()` helper for immediate cache invalidation
after admin actions.
• All modified files pass linter; no breaking DB migrations required
(handled by AutoMigrate).
Result
------
– Soft-delete logic is transparent, safe, and allows record “revival”.
– Pricing-related queries are now constant-time, reducing CPU usage and
latency under load.
Why:
• The vendor list API is separate from the models API, causing the “Vendor” column in `ModelsTable` to flash (rendering `'-'` first, then updating) after the table finishes loading.
• This visual jump degrades the user experience.
What:
• Updated `web/src/hooks/models/useModelsData.js`
– In the initial `useEffect`, vendors are fetched first with `loadVendors()` and awaited.
– Only after vendors are ready do we call `loadModels()`, ensuring `vendorMap` is populated before the table renders.
Outcome:
• The table now renders with complete vendor data on first paint, removing the flicker and providing a smoother UI.
- Update PricingCardSkeleton grid classes from 'sm:grid-cols-2 lg:grid-cols-3'
to 'xl:grid-cols-2 2xl:grid-cols-3' to match PricingCardView layout
- Ensures consistent column count between skeleton and actual content
at same screen sizes
- Improves loading state visual consistency across different breakpoints
- **Backend Changes:**
- Refactor pricing API to return separate vendors array with ID-based model references
- Remove redundant vendor_name/vendor_icon fields from pricing records, use vendor_id only
- Add vendor_description to pricing response for frontend display
- Maintain 1-minute cache protection for pricing endpoint security
- **Frontend Data Flow:**
- Update useModelPricingData hook to build vendorsMap from API response
- Enhance model records with vendor info during data processing
- Pass vendorsMap through component hierarchy for consistent vendor data access
- **UI Component Replacements:**
- Replace PricingCategories with PricingVendors component for vendor-based filtering
- Replace PricingCategoryIntro with PricingVendorIntro in header section
- Remove all model category related components and logic
- **Header Improvements:**
- Implement vendor intro with real backend data (name, icon, description)
- Add text collapsible feature (2-line limit with expand/collapse functionality)
- Support carousel animation for "All Vendors" view with vendor icon rotation
- **Model Detail Modal Enhancements:**
- Update ModelHeader to use real vendor icons via getLobeHubIcon()
- Move tags from header to ModelBasicInfo content area to avoid SideSheet title width constraints
- Display only custom tags from backend with stringToColor() for consistent styling
- Use Space component with wrap property for proper tag layout
- **Table View Optimizations:**
- Integrate RenderUtils for description and tags columns
- Implement renderLimitedItems for tags (max 3 visible, +x popover for overflow)
- Use renderDescription for text truncation with tooltip support
- **Filter Logic Updates:**
- Vendor filter shows disabled options instead of hiding when no models match
- Include "Unknown Vendor" category for models without vendor information
- Remove all hardcoded vendor descriptions, use real backend data
- **Code Quality:**
- Fix import paths after component relocation
- Remove unused model category utilities and hardcoded mappings
- Ensure consistent vendor data usage across all pricing views
- Maintain backward compatibility with existing pricing calculation logic
This refactor provides a more scalable vendor-based architecture while eliminating
data redundancy and improving user experience with real-time backend data integration.
Add flexible model name matching system to support different matching patterns:
Backend changes:
- Add `name_rule` field to Model struct with 4 matching types:
* 0: Exact match (default)
* 1: Prefix match
* 2: Contains match
* 3: Suffix match
- Implement `FindModelByNameWithRule` function with priority order:
exact > prefix > suffix > contains
- Add database migration for new `name_rule` column
Frontend changes:
- Add "Match Type" column in models table with colored tags
- Add name rule selector in create/edit modal with validation
- Auto-set exact match and disable selection for preconfigured models
- Add explanatory text showing priority order
- Support i18n for all new UI elements
This enables users to define model patterns once and reuse configurations
across similar models, reducing repetitive setup while maintaining exact
match priority for specific overrides.
Closes: #[issue-number]
Summary
• Backend
1. model/model_meta.go
– Added `QuotaType` field to `Model` struct (JSON only, gorm `-`).
2. model/model_groups.go
– Implemented `GetModelQuotaType(modelName)` leveraging cached pricing map.
3. controller/model_meta.go
– Enhanced `fillModelExtra` to populate `QuotaType` using new helper.
• Frontend
1. web/src/components/table/models/ModelsColumnDefs.js
– Introduced `renderQuotaType` helper that visualises billing mode with coloured tags (`teal = per-call`, `violet = per-token`).
– Added “计费类型” column (`quota_type`) to models table.
Why
Providing the billing mode alongside existing pricing/group information gives administrators instant visibility into whether each model is priced per call or per token, aligning UI with new backend metadata.
Notes
No database migration required – `quota_type` is transient, delivered via API. Frontend labels/colours can be adjusted via i18n or theme tokens if necessary.
- Add new PrefillGroup model with CRUD operations
* Support for model, tag, and endpoint group types
* JSON storage for group items with GORM datatypes
* Automatic database migration support
- Implement backend API endpoints
* GET /api/prefill_group - List groups by type with admin auth
* POST /api/prefill_group - Create new groups
* PUT /api/prefill_group - Update existing groups
* DELETE /api/prefill_group/:id - Delete groups
- Add comprehensive frontend management interface
* PrefillGroupManagement component for group listing
* EditPrefillGroupModal for group creation/editing
* Integration with EditModelModal for auto-filling
* Responsive design with CardTable and SideSheet
- Enhance model editing workflow
* Tag group selection with auto-fill functionality
* Endpoint group selection with auto-fill functionality
* Seamless integration with existing model forms
- Create reusable UI components
* Extract common rendering utilities to models/ui/
* Shared renderLimitedItems and renderDescription functions
* Consistent styling across all model-related components
- Improve user experience
* Empty state illustrations matching existing patterns
* Fixed column positioning for operation buttons
* Item content display with +x indicators for overflow
* Tooltip support for long descriptions