[feat] Add ownership filter to model browser (#7201)

## Summary
Adds a dropdown filter to the model browser that allows users to filter
assets by ownership (All, My models, Public models), based on the
`is_immutable` property.

## Changes
- **Filter UI**: Added ownership dropdown in
[AssetFilterBar.vue](src/platform/assets/components/AssetFilterBar.vue#L30-L38)
that only appears when user has uploaded models
- **Filter Logic**: Implemented `filterByOwnership` function in
[useAssetBrowser.ts](src/platform/assets/composables/useAssetBrowser.ts#L38-L45)
to filter by `is_immutable` property
- **i18n**: Added translation strings for ownership filter options
- **Tests**: Added comprehensive tests for ownership filtering in both
composable and component test files

## Review Focus
- The ownership filter visibility logic correctly checks for mutable
assets (`!is_immutable`)
- Default filter value is 'all' to show all models initially
- Filter integrates cleanly with existing file format and base model
filters

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7201-feat-Add-ownership-filter-to-model-browser-2c16d73d365081f280f6d1e42e5400af)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Luke Mino-Altherr
2025-12-09 13:52:33 -08:00
committed by GitHub
parent 2b9f7ecedf
commit 6850c45d63
7 changed files with 328 additions and 74 deletions

View File

@@ -249,7 +249,8 @@ describe('useAssetBrowser', () => {
updateFilters({
sortBy: 'name-asc',
fileFormats: ['safetensors'],
baseModels: []
baseModels: [],
ownership: 'all'
})
await nextTick()
@@ -284,7 +285,8 @@ describe('useAssetBrowser', () => {
updateFilters({
sortBy: 'name-asc',
fileFormats: [],
baseModels: ['SDXL']
baseModels: ['SDXL'],
ownership: 'all'
})
await nextTick()
@@ -335,7 +337,12 @@ describe('useAssetBrowser', () => {
const { updateFilters, filteredAssets } = useAssetBrowser(ref(assets))
updateFilters({ sortBy: 'name', fileFormats: [], baseModels: [] })
updateFilters({
sortBy: 'name',
fileFormats: [],
baseModels: [],
ownership: 'all'
})
await nextTick()
const names = filteredAssets.value.map((asset) => asset.name)
@@ -355,7 +362,12 @@ describe('useAssetBrowser', () => {
const { updateFilters, filteredAssets } = useAssetBrowser(ref(assets))
updateFilters({ sortBy: 'recent', fileFormats: [], baseModels: [] })
updateFilters({
sortBy: 'recent',
fileFormats: [],
baseModels: [],
ownership: 'all'
})
await nextTick()
const dates = filteredAssets.value.map((asset) => asset.created_at)
@@ -367,6 +379,92 @@ describe('useAssetBrowser', () => {
})
})
describe('Ownership filtering', () => {
it('filters by ownership - all', async () => {
const assets = [
createApiAsset({ name: 'my-model.safetensors', is_immutable: false }),
createApiAsset({
name: 'public-model.safetensors',
is_immutable: true
}),
createApiAsset({
name: 'another-my-model.safetensors',
is_immutable: false
})
]
const { updateFilters, filteredAssets } = useAssetBrowser(ref(assets))
updateFilters({
sortBy: 'name-asc',
fileFormats: [],
baseModels: [],
ownership: 'all'
})
await nextTick()
expect(filteredAssets.value).toHaveLength(3)
})
it('filters by ownership - my models only', async () => {
const assets = [
createApiAsset({ name: 'my-model.safetensors', is_immutable: false }),
createApiAsset({
name: 'public-model.safetensors',
is_immutable: true
}),
createApiAsset({
name: 'another-my-model.safetensors',
is_immutable: false
})
]
const { updateFilters, filteredAssets } = useAssetBrowser(ref(assets))
updateFilters({
sortBy: 'name-asc',
fileFormats: [],
baseModels: [],
ownership: 'my-models'
})
await nextTick()
expect(filteredAssets.value).toHaveLength(2)
expect(filteredAssets.value.every((asset) => !asset.is_immutable)).toBe(
true
)
})
it('filters by ownership - public models only', async () => {
const assets = [
createApiAsset({ name: 'my-model.safetensors', is_immutable: false }),
createApiAsset({
name: 'public-model.safetensors',
is_immutable: true
}),
createApiAsset({
name: 'another-public-model.safetensors',
is_immutable: true
})
]
const { updateFilters, filteredAssets } = useAssetBrowser(ref(assets))
updateFilters({
sortBy: 'name-asc',
fileFormats: [],
baseModels: [],
ownership: 'public-models'
})
await nextTick()
expect(filteredAssets.value).toHaveLength(2)
expect(filteredAssets.value.every((asset) => asset.is_immutable)).toBe(
true
)
})
})
describe('Dynamic Category Extraction', () => {
it('extracts categories from asset tags', () => {
const assets = [