feat: complete dsh-plugin-model-enhancer with models.dev prefill, paperclip upload, codex slider, and i18n
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
.DS_Store
|
||||
*.log
|
||||
@@ -0,0 +1,47 @@
|
||||
# dsh-plugin-model-enhancer
|
||||
|
||||
DeepSeek Harness (DSH) 自定义模型增强与交互插件。
|
||||
|
||||
## ✨ 特性一览
|
||||
|
||||
1. **设置页面模型目录增强**:
|
||||
- 支持图像/视觉(Vision)输入模态开关。
|
||||
- 支持多档位推理思考深度(Reasoning Efforts,包含 `minimal` / `low` / `medium` / `high` / `xhigh` / `max`)。
|
||||
- 官方模型目录与模型条目右侧状态胶囊徽标展示(图像、思考档位)。
|
||||
- **models.dev 智能预填充**:
|
||||
- 单模型「智能补全」与对比确认弹窗(支持按项选用推荐值)。
|
||||
- 顶部一键「一键补全」批量安全清洗与参数匹配。
|
||||
|
||||
2. **输入栏左侧连体药丸操作小岛(Pill Island)**:
|
||||
- 整合 `[+]`(命令菜单)与 `[📎]`(多模态图片/文件上传)为一体化胶囊岛。
|
||||
- 正圆形 Hover 微光微交互。
|
||||
|
||||
3. **输入栏右下角 Codex 风格推理滑块与模型切换**:
|
||||
- 无边框极简常态,悬浮/激活呈现通透线框轮廓。
|
||||
- 水平胶囊滑轨与海蓝渐变激活填充(Soft Sky-Blue Gradient)。
|
||||
- 饱满悬浮大圆球(Thumb),突破滑槽高度,悬停/拖拽轻微弹性放大。
|
||||
- 点击顶部模型名平滑展开带供应商分类徽标与细分割线的模型选择树。
|
||||
|
||||
4. **全面双语支持(i18n)**:
|
||||
- 自动跟随 DSH 系统语言(中文 / English)无缝切换所有文案与档位标签。
|
||||
|
||||
## 📦 安装与配置
|
||||
|
||||
在 DSH Web Profile 的 `package.json` 中添加依赖并注册 bundle:
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"dsh-plugin-model-enhancer": "^1.0.0"
|
||||
},
|
||||
"dsh": {
|
||||
"profile": {
|
||||
"bundles": [
|
||||
"dsh-plugin-model-enhancer"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📄 License
|
||||
MIT
|
||||
+1954
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
# dsh bundle patch: replace default ui-settings-models with enhanced model settings
|
||||
- id: ui-settings-models
|
||||
disabled: true
|
||||
|
||||
- insert:
|
||||
- id: dsh-plugin-model-enhancer
|
||||
name: 'dsh-plugin-model-enhancer'
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
// Complete i18n Dictionary for Model Enhancer Plugin
|
||||
const ENHANCER_I18N = {
|
||||
zh: {
|
||||
// ModelListEditor & Badges
|
||||
batchBtn: "一键补全",
|
||||
batchBtnBusy: "补全中...",
|
||||
batchBtnTitle: "根据模型 ID 从 models.dev 自动匹配并预填充缺失参数",
|
||||
singleBtn: "智能补全",
|
||||
singleBtnBusy: "补全中",
|
||||
singleBtnTitle: "从 models.dev 获取该模型推荐参数",
|
||||
badgeVision: "图像",
|
||||
badgeVisionTitle: "支持图像输入 (Vision)",
|
||||
badgeThinking: "思考",
|
||||
badgeThinkingTitle: "思考档位: {levels}",
|
||||
defaultEffort: "默认",
|
||||
modalitiesTitle: "输入模态支持 (Input Modalities)",
|
||||
modalityText: "文本输入 (Text)",
|
||||
modalityVision: "图像输入 (Vision)",
|
||||
reasoningTitle: "推理思考档位 (Reasoning Efforts)",
|
||||
enableReasoning: "启用深度思考",
|
||||
saveBlockedHint: "保存受阻提示:模型 {index} {reason}",
|
||||
fillNoIdError: "列表中没有填写模型 ID 的有效模型",
|
||||
fillSuccessNotice: "一键补全完成:已匹配并更新 {count}/{total} 个模型",
|
||||
fillFailedNotice: "一键补全失败: {message}",
|
||||
singleFillRequireId: "请先填写模型 ID",
|
||||
singleFillNotFound: '未在 models.dev 中找到与 "{id}" 匹配的模型信息',
|
||||
singleFillFetchFailed: "获取 models.dev 失败: {message}",
|
||||
singleFillApplied: '已预填充 "{id}" 推荐参数',
|
||||
|
||||
// PrefillModal
|
||||
modalTitle: "models.dev 预填充确认与选择",
|
||||
matchedModelLabel: "检索到匹配模型:",
|
||||
thSelect: "选用",
|
||||
thField: "配置项",
|
||||
thCurrent: "当前设置",
|
||||
thRecommended: "models.dev 推荐值",
|
||||
fieldName: "显示名称",
|
||||
fieldContext: "上下文长度",
|
||||
fieldMaxTokens: "最大输出",
|
||||
fieldInput: "图像输入",
|
||||
fieldReasoning: "推理思考等级",
|
||||
fieldUnset: "(未设置)",
|
||||
fieldDefault: "(默认)",
|
||||
visionSupported: "支持图像",
|
||||
visionOnlyText: "仅文本",
|
||||
thinkingOff: "关闭思考",
|
||||
thinkingOn: "开启",
|
||||
cancel: "取消",
|
||||
applySelected: "应用已选推荐项",
|
||||
|
||||
// Paperclip
|
||||
attachTitle: "上传图片或文件",
|
||||
|
||||
// Codex Slider
|
||||
effortOff: "关",
|
||||
effortMinimal: "极低",
|
||||
effortLow: "低",
|
||||
effortMedium: "中",
|
||||
effortHigh: "高",
|
||||
effortXHigh: "超高",
|
||||
effortMax: "最大",
|
||||
effortReasoningTitle: "推理强度",
|
||||
effortSelectModelTip: "点击切换模型",
|
||||
effortResetTip: "恢复默认档位",
|
||||
effortChooseModel: "选择模型",
|
||||
commandsTitle: "命令菜单 (/)"
|
||||
},
|
||||
en: {
|
||||
// ModelListEditor & Badges
|
||||
batchBtn: "Auto-Fill All",
|
||||
batchBtnBusy: "Filling…",
|
||||
batchBtnTitle: "Auto-match and prefill missing parameters from models.dev",
|
||||
singleBtn: "Auto-Fill",
|
||||
singleBtnBusy: "Filling…",
|
||||
singleBtnTitle: "Fetch recommended parameters for this model from models.dev",
|
||||
badgeVision: "Vision",
|
||||
badgeVisionTitle: "Supports Image/Vision input",
|
||||
badgeThinking: "Reasoning",
|
||||
badgeThinkingTitle: "Reasoning efforts: {levels}",
|
||||
defaultEffort: "Default",
|
||||
modalitiesTitle: "Input Modalities",
|
||||
modalityText: "Text Input",
|
||||
modalityVision: "Vision Input",
|
||||
reasoningTitle: "Reasoning Efforts",
|
||||
enableReasoning: "Enable Deep Reasoning",
|
||||
saveBlockedHint: "Save blocked: Model {index} {reason}",
|
||||
fillNoIdError: "No valid models with a Model ID found in the list",
|
||||
fillSuccessNotice: "Auto-fill complete: matched & updated {count}/{total} models",
|
||||
fillFailedNotice: "Auto-fill failed: {message}",
|
||||
singleFillRequireId: "Please enter a Model ID first",
|
||||
singleFillNotFound: 'No matching model found on models.dev for "{id}"',
|
||||
singleFillFetchFailed: "Failed to fetch models.dev: {message}",
|
||||
singleFillApplied: 'Prefilled recommended parameters for "{id}"',
|
||||
|
||||
// PrefillModal
|
||||
modalTitle: "models.dev Prefill Confirmation",
|
||||
matchedModelLabel: "Matched Model: ",
|
||||
thSelect: "Apply",
|
||||
thField: "Property",
|
||||
thCurrent: "Current Setting",
|
||||
thRecommended: "models.dev Value",
|
||||
fieldName: "Display Name",
|
||||
fieldContext: "Context Window",
|
||||
fieldMaxTokens: "Max Output Tokens",
|
||||
fieldInput: "Vision Input",
|
||||
fieldReasoning: "Reasoning Efforts",
|
||||
fieldUnset: "(Unset)",
|
||||
fieldDefault: "(Default)",
|
||||
visionSupported: "Vision Supported",
|
||||
visionOnlyText: "Text Only",
|
||||
thinkingOff: "Disabled",
|
||||
thinkingOn: "Enabled",
|
||||
cancel: "Cancel",
|
||||
applySelected: "Apply Selected",
|
||||
|
||||
// Paperclip
|
||||
attachTitle: "Attach files or images",
|
||||
|
||||
// Codex Slider
|
||||
effortOff: "Off",
|
||||
effortMinimal: "Minimal",
|
||||
effortLow: "Low",
|
||||
effortMedium: "Medium",
|
||||
effortHigh: "High",
|
||||
effortXHigh: "X-High",
|
||||
effortMax: "Max",
|
||||
effortReasoningTitle: "Reasoning Effort",
|
||||
effortSelectModelTip: "Click to switch model",
|
||||
effortResetTip: "Reset to default effort",
|
||||
effortChooseModel: "Select model",
|
||||
commandsTitle: "Commands (/)"
|
||||
}
|
||||
};
|
||||
+4236
File diff suppressed because one or more lines are too long
@@ -0,0 +1,73 @@
|
||||
export const name = 'dsh-plugin-model-enhancer';
|
||||
export const inject = ['webServer'];
|
||||
|
||||
let cachedData = null;
|
||||
let lastFetchedAt = 0;
|
||||
const CACHE_TTL_MS = 3600 * 1000; // 1 hour
|
||||
|
||||
async function getModelsDevData() {
|
||||
const now = Date.now();
|
||||
if (cachedData && now - lastFetchedAt < CACHE_TTL_MS) {
|
||||
return cachedData;
|
||||
}
|
||||
try {
|
||||
const res = await fetch('https://models.dev/api.json', {
|
||||
headers: {
|
||||
'User-Agent': 'DSH-Model-Enhancer/1.0.0'
|
||||
}
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to fetch models.dev: HTTP ${res.status}`);
|
||||
}
|
||||
const json = await res.json();
|
||||
cachedData = json;
|
||||
lastFetchedAt = now;
|
||||
return json;
|
||||
} catch (err) {
|
||||
if (cachedData) {
|
||||
// Fallback to stale cache if network fails
|
||||
return cachedData;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
ctx.effect(() => {
|
||||
return ctx.webServer.register({
|
||||
kind: 'exact',
|
||||
path: '/api/models-dev/models',
|
||||
handler: async (req, res) => {
|
||||
// Add CORS headers so web client can always reach it seamlessly
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== 'GET') {
|
||||
res.writeHead(405, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await getModelsDevData();
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'public, max-age=3600'
|
||||
});
|
||||
res.end(JSON.stringify(data));
|
||||
} catch (err) {
|
||||
ctx.logger?.error?.('[model-enhancer] Error fetching models.dev:', err);
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: err.message || 'Internal Server Error' }));
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 'dsh-plugin-model-enhancer: webServer proxy route');
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "dsh-plugin-model-enhancer",
|
||||
"version": "1.0.0",
|
||||
"description": "Enhance DSH model settings with image input, reasoning effort levels, and models.dev prefilling",
|
||||
"type": "module",
|
||||
"main": "./lib/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./client": "./lib/client.js",
|
||||
"./cordis.patch.yml": "./cordis.patch.yml",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dsh": {
|
||||
"bundle": {
|
||||
"patch": "./cordis.patch.yml"
|
||||
},
|
||||
"client": {
|
||||
"platform": "web",
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-connection",
|
||||
"@deepseek-ai/dsh-client-runtime",
|
||||
"@deepseek-ai/dsh-client-locale",
|
||||
"@deepseek-ai/dsh-client-ui-settings",
|
||||
"@deepseek-ai/dsh-client-ui-slots",
|
||||
"@deepseek-ai/dsh-api-remotes"
|
||||
]
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "^4.0.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user