Skip to content

Commit e79822f

Browse files
committed
add Clip-Vit-B 32 Multilingual
1 parent 09f6ef6 commit e79822f

File tree

9 files changed

+285
-33
lines changed

9 files changed

+285
-33
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ LoFS 聚焦“本地优先”的知识管理场景,将文件管理与语义检
5454
模型目录按需懒加载,首次使用对应能力时自动下载;也可以提前拉取以免首轮等待:
5555

5656
```bash
57-
python -c "from service.model_manager import get_model_manager; manager = get_model_manager(); [manager.get_model_path(key) for key in ('bge_m3', 'bge_reranker_v2_m3', 'clip_vit_b_32', 'pdf_extract_kit')]"
57+
python -c "from service.model_manager import get_model_manager; manager = get_model_manager(); [manager.get_model_path(key) for key in ('bge_m3', 'bge_reranker_v2_m3', 'clip_vit_b_32', 'clip_vit_b_32_multilingual', 'pdf_extract_kit')]"
5858
```
5959

6060
## 4. 部署使用
@@ -95,6 +95,6 @@ npm run dev
9595
python package.py # 一键打包
9696
```
9797

98-
- 应用启动时会在 `meta` 目录创建所需模型文件夹(`embedding/bge-m3``embedding/clip``reranker/bge-reranker-v3-m3``pdf-extract-kit`)。
98+
- 应用启动时会在 `meta` 目录创建所需模型文件夹(`embedding/bge-m3``embedding/clip``embedding/clip-Vit-32B-multilingual``reranker/bge-reranker-v3-m3``pdf-extract-kit`)。
9999
- 首次使用向量化、重排、图像检索或 PDF 解析时,会通过 `huggingface_hub` 自动下载对应模型。
100100
- 即便清空 `meta` 目录,LoFS 也会在下次启动时自动恢复目录结构。

README_EN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ LoFS fuses local file organization with semantic retrieval to deliver an “alwa
5454
Model assets download lazily the first time a capability is invoked. Prefetch them to warm the cache:
5555

5656
```bash
57-
python -c "from service.model_manager import get_model_manager; manager = get_model_manager(); [manager.get_model_path(key) for key in ('bge_m3', 'bge_reranker_v2_m3', 'clip_vit_b_32', 'pdf_extract_kit')]"
57+
python -c "from service.model_manager import get_model_manager; manager = get_model_manager(); [manager.get_model_path(key) for key in ('bge_m3', 'bge_reranker_v2_m3', 'clip_vit_b_32', 'clip_vit_b_32_multilingual', 'pdf_extract_kit')]"
5858
```
5959

6060
## 4. Deployment & Usage
@@ -95,7 +95,7 @@ npm run dev
9595
python package.py # cross-platform one-click packaging
9696
```
9797

98-
- On startup LoFS creates model directories under `meta` (`embedding/bge-m3`, `embedding/clip`, `reranker/bge-reranker-v3-m3`, `pdf-extract-kit`).
98+
- On startup LoFS creates model directories under `meta` (`embedding/bge-m3`, `embedding/clip`, `embedding/clip-Vit-32B-multilingual`, `reranker/bge-reranker-v3-m3`, `pdf-extract-kit`).
9999
- The first invocation of embeddings, reranking, CLIP, or PDF parsing auto-downloads weights via `huggingface_hub`.
100100
- Deleting `meta` is safe—the app will recreate the structure during the next boot.
101101

electron/src/modules/model.js

Lines changed: 120 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ class ModelModule {
5252
this.systemFetchPending = false;
5353
this.systemPollingHandle = null;
5454
this.systemPollingInterval = null;
55-
this.systemPollingDelayMs = 30000;
55+
this.systemPollingDelayMs = 5000;
5656
this.systemDownloadRequests = new Set();
57+
this.systemUninstallRequests = new Set();
5758
this.registryListeners = new Set();
5859

5960
this.dependencies = {
@@ -123,8 +124,8 @@ class ModelModule {
123124
},
124125
{
125126
key: 'clip_vit_b_32',
126-
name: 'CLIP ViT-B',
127-
description: '支持图文向量化的 CLIP 模型,用于图片检索与比对。',
127+
name: 'CLIP ViT-B 32',
128+
description: '英文优化的标准 CLIP 模型,适用于通用图文检索。',
128129
tags: ['图像嵌入', '多模态'],
129130
status: 'not_downloaded',
130131
progress: 0,
@@ -135,6 +136,20 @@ class ModelModule {
135136
endpoint: null,
136137
updatedAt: now
137138
},
139+
{
140+
key: 'clip_vit_b_32_multilingual',
141+
name: 'CLIP ViT-B Multilingual',
142+
description: '支持中文等多语言查询的 CLIP 模型,提升图文检索效果。',
143+
tags: ['图像嵌入', '多模态', '多语言'],
144+
status: 'not_downloaded',
145+
progress: 0,
146+
downloadedBytes: 0,
147+
totalBytes: null,
148+
message: '点击卡片开始下载模型。',
149+
error: null,
150+
endpoint: null,
151+
updatedAt: now
152+
},
138153
{
139154
key: 'pdf_extract_kit',
140155
name: 'PDF Extract Kit 套件',
@@ -384,6 +399,7 @@ class ModelModule {
384399
return;
385400
}
386401
if (model.status === 'downloaded') {
402+
this.confirmUninstallSystemModel(model);
387403
return;
388404
}
389405
if (this.systemDownloadRequests.has(model.key) || model.status === 'downloading') {
@@ -435,6 +451,72 @@ class ModelModule {
435451
}
436452
}
437453

454+
async triggerSystemModelUninstall(key) {
455+
if (!key || this.systemUninstallRequests.has(key)) {
456+
return;
457+
}
458+
this.systemUninstallRequests.add(key);
459+
this.updateSystemModelPartial(key, {
460+
status: 'pending',
461+
message: '正在卸载...',
462+
error: null
463+
});
464+
this.renderAllModels();
465+
this.updateSystemPollingState({ forceFast: true });
466+
467+
try {
468+
const response = await fetch(`${this.baseApiUrl}/api/models/${encodeURIComponent(key)}/uninstall`, {
469+
method: 'POST',
470+
headers: { 'Accept': 'application/json' }
471+
});
472+
if (!response.ok) {
473+
throw new Error(`HTTP ${response.status}`);
474+
}
475+
const data = await response.json();
476+
this.mergeSystemModelStatus(data);
477+
if (data && data.status === 'not_downloaded') {
478+
this.updateSystemModelPartial(key, { message: '模型已卸载' });
479+
}
480+
this.renderAllModels();
481+
this.updateSystemPollingState();
482+
} catch (error) {
483+
console.error('卸载模型失败:', error);
484+
this.updateSystemModelPartial(key, {
485+
status: 'failed',
486+
message: '卸载失败,请稍后重试。',
487+
error: error?.message || '卸载失败'
488+
});
489+
this.renderAllModels();
490+
this.updateSystemPollingState();
491+
} finally {
492+
this.systemUninstallRequests.delete(key);
493+
this.updateSystemPollingState();
494+
}
495+
}
496+
497+
confirmUninstallSystemModel(model) {
498+
const doUninstall = () => {
499+
this.triggerSystemModelUninstall(model.key);
500+
};
501+
502+
if (typeof window.showModal === 'function') {
503+
window.showModal({
504+
type: 'warning',
505+
title: '卸载系统模型',
506+
message: `确定要卸载 ${model.name} 吗?这将删除本地模型数据。`,
507+
confirmText: '卸载',
508+
cancelText: '取消',
509+
showCancel: true,
510+
onConfirm: doUninstall
511+
});
512+
return;
513+
}
514+
515+
if (window.confirm(`确定要卸载 ${model.name} 吗?这将删除本地模型数据。`)) {
516+
doUninstall();
517+
}
518+
}
519+
438520
buildSystemModelCard(model) {
439521
const card = document.createElement('article');
440522
card.className = 'model-card system-model-card';
@@ -458,7 +540,8 @@ class ModelModule {
458540
const header = document.createElement('div');
459541
header.className = 'system-model-card__header';
460542
header.appendChild(title);
461-
header.appendChild(providerLabel);
543+
// 将开发商标签从标题区域移除,移动到标签列表末尾
544+
// header.appendChild(providerLabel);
462545
header.appendChild(statusBadge);
463546

464547
const description = document.createElement('p');
@@ -475,19 +558,32 @@ class ModelModule {
475558
tagsWrapper.appendChild(tagEl);
476559
});
477560
}
561+
// 将开发商标签追加到描述标签之后
562+
tagsWrapper.appendChild(providerLabel);
478563

479564
card.appendChild(header);
565+
566+
const messageText = this.buildSystemModelMessage(model);
567+
let inlineMessageAttached = false;
568+
const shouldAttachToTags =
569+
messageText && model.key === 'clip_vit_b_32_multilingual' && tagsWrapper.children.length > 0;
570+
if (shouldAttachToTags) {
571+
const inlineMessage = this.createSystemModelInlineMessage(messageText);
572+
inlineMessage.classList.add('system-model-card__inline-message--after-tags');
573+
tagsWrapper.appendChild(inlineMessage);
574+
inlineMessageAttached = true;
575+
}
576+
480577
card.appendChild(description);
481-
if (tagsWrapper.children.length > 0) {
482-
card.appendChild(tagsWrapper);
578+
579+
if (messageText && !inlineMessageAttached) {
580+
const inlineMessage = this.createSystemModelInlineMessage(messageText);
581+
inlineMessage.classList.add('system-model-card__inline-message--after-description');
582+
description.appendChild(inlineMessage);
483583
}
484584

485-
const messageText = this.buildSystemModelMessage(model);
486-
if (messageText) {
487-
const message = document.createElement('div');
488-
message.className = 'system-model-card__message';
489-
message.textContent = messageText;
490-
card.appendChild(message);
585+
if (tagsWrapper.children.length > 0) {
586+
card.appendChild(tagsWrapper);
491587
}
492588

493589
card.addEventListener('click', (event) => {
@@ -499,6 +595,13 @@ class ModelModule {
499595
return card;
500596
}
501597

598+
createSystemModelInlineMessage(text) {
599+
const inlineMessage = document.createElement('span');
600+
inlineMessage.className = 'system-model-card__inline-message';
601+
inlineMessage.textContent = text;
602+
return inlineMessage;
603+
}
604+
502605
describeSystemModelStatus(model) {
503606
if (!model) {
504607
return '未知状态';
@@ -510,10 +613,13 @@ class ModelModule {
510613
const percent = Math.max(0, Math.min(100, Math.round((model.progress || 0) * 100)));
511614
return `下载中 ${percent}%`;
512615
}
616+
if (model.status === 'pending') {
617+
return '卸载中';
618+
}
513619
if (model.status === 'failed') {
514620
return '下载失败';
515621
}
516-
return '点击下载';
622+
return '未下载';
517623
}
518624

519625
buildSystemModelMessage(model) {
@@ -555,6 +661,7 @@ class ModelModule {
555661
case 'bge_reranker_v2_m3':
556662
return 'AAAI';
557663
case 'clip_vit_b_32':
664+
case 'clip_vit_b_32_multilingual':
558665
return 'SentenceTransformer';
559666
case 'pdf_extract_kit':
560667
return 'opendatalab';

electron/src/styles/model.css

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,25 @@
191191
background-color: rgba(248, 113, 113, 0.4);
192192
}
193193

194-
.system-model-card__message {
194+
.system-model-card__inline-message {
195+
display: inline-flex;
196+
align-items: center;
195197
font-size: 12px;
196198
color: var(--text-muted);
197-
z-index: 1;
199+
white-space: nowrap;
200+
flex: 0 0 auto;
201+
}
202+
203+
.system-model-card__inline-message--after-tags {
204+
margin-left: 8px;
205+
align-self: center;
206+
}
207+
208+
.system-model-card__inline-message--after-description {
209+
margin-left: 8px;
198210
}
199211

200-
.dark-mode .system-model-card__message {
212+
.dark-mode .system-model-card__inline-message {
201213
color: rgba(255, 255, 255, 0.65);
202214
}
203215

server/api/model_api.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,14 @@ def trigger_download(key: str) -> ModelStatusResponse:
6464
except KeyError as exc:
6565
raise HTTPException(status_code=404, detail=str(exc)) from exc
6666
return ModelStatusResponse.from_service(status)
67+
68+
69+
@router.post("/{key}/uninstall", response_model=ModelStatusResponse)
70+
def uninstall_model(key: str) -> ModelStatusResponse:
71+
"""Uninstall a system model and return its updated status."""
72+
service = _get_service()
73+
try:
74+
status = service.uninstall(key)
75+
except KeyError as exc:
76+
raise HTTPException(status_code=404, detail=str(exc)) from exc
77+
return ModelStatusResponse.from_service(status)

0 commit comments

Comments
 (0)