본인 dogfood 환경 = gemma4:e4b (텍스트). vision 변종은 현재 gemma3 (vision-capable) 또는 향후 gemma4 출시 시. 양 family 모두 hint 에 포함 — capability detection 이 future-proof. - VisionDetect.VISION_FAMILIES + VISION_NAME_HINTS 에 'gemma4' 추가 - isVisionCapable test 2건 추가 (gemma4 family / gemma4 name hint detection) - spec §1 + §2 의 'gemma3 family default' → 'gemma family — gemma3 / gemma4' 영향: 기존 detection 정확도 무영향 (set 추가만), 사용자가 gemma4 vision 변종을 설치하면 자동 인식.
48 lines
1.9 KiB
TypeScript
48 lines
1.9 KiB
TypeScript
import type { SettingsService } from './SettingsService.js';
|
|
|
|
// v0.3.1 Cut F final fix — gemma 시리즈 default 정정. 본인 dogfood 환경 = gemma4:e4b
|
|
// (텍스트). vision 변종은 gemma3 (현재 vision-capable) 또는 gemma4 (향후 출시 시).
|
|
// 양 family 모두 hint 에 포함 — capability detection 이 future-proof.
|
|
const VISION_FAMILIES = new Set(['gemma3', 'gemma4', 'llava', 'llama3.2-vision', 'minicpm-v', 'pixtral']);
|
|
const VISION_NAME_HINTS = ['vision', 'vl', 'multimodal', 'gemma3', 'gemma4'];
|
|
|
|
export interface OllamaModel {
|
|
name: string;
|
|
details?: { family?: string; families?: string[] };
|
|
}
|
|
|
|
export function isVisionCapable(model: OllamaModel): boolean {
|
|
if (model.details?.family && VISION_FAMILIES.has(model.details.family)) return true;
|
|
if (model.details?.families?.some((f) => VISION_FAMILIES.has(f))) return true;
|
|
const lower = model.name.toLowerCase();
|
|
return VISION_NAME_HINTS.some((h) => lower.includes(h));
|
|
}
|
|
|
|
export interface RefreshDeps {
|
|
settings: SettingsService;
|
|
endpoint: string;
|
|
now?: () => Date;
|
|
fetchImpl?: typeof fetch;
|
|
}
|
|
|
|
export async function refreshVisionCache(
|
|
deps: RefreshDeps
|
|
): Promise<{ ok: true; models: string[] } | { ok: false; reason: string }> {
|
|
if (!(await deps.settings.isAiEnabled())) {
|
|
return { ok: false, reason: 'ai_disabled' };
|
|
}
|
|
const fetchFn = deps.fetchImpl ?? fetch;
|
|
let body: { models?: OllamaModel[] };
|
|
try {
|
|
const r = await fetchFn(`${deps.endpoint}/api/tags`);
|
|
if (!r.ok) return { ok: false, reason: `tags http ${r.status}` };
|
|
body = (await r.json()) as { models?: OllamaModel[] };
|
|
} catch (e) {
|
|
return { ok: false, reason: `unreachable: ${(e as Error).message}` };
|
|
}
|
|
const capable = (body.models ?? []).filter(isVisionCapable).map((m) => m.name);
|
|
const now = deps.now ? deps.now() : new Date();
|
|
await deps.settings.setVisionCapableCache(capable, now);
|
|
return { ok: true, models: capable };
|
|
}
|