fix(settings): 모델 dropdown 에 보유 모델 전부 표시 (v2.2.209)
설정 패널 dropdown 이 LM Studio 에서 모델 1개만 보이고, 변경하면 원복되던 회귀 수정. 원인: settings 패널의 discoverModels 가 REST /v1/models 만 사용 → JIT 로딩 환경에서 '현재 로드된' 모델만 반환. (사이드바는 SDK 로 전체를 가져옴) - discoverModels: LM Studio SDK listDownloadedModels(전체 다운로드) 우선, 실패/0개면 REST 폴백. 사이드바 ModelDiscovery 와 동일 정책으로 통일 → 두 경로가 갈라져 다시 회귀하지 않도록 가이드라인 주석 명시. - SettingsPanelDeps/SettingsSetupDeps 에 lmStudioDownloaded 콜백 추가, extension.ts 에서 lmStudioClient.listDownloadedCached 연결. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "astra",
|
"name": "astra",
|
||||||
"version": "2.2.208",
|
"version": "2.2.209",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "astra",
|
"name": "astra",
|
||||||
"version": "2.2.208",
|
"version": "2.2.209",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lmstudio/sdk": "^1.5.0",
|
"@lmstudio/sdk": "^1.5.0",
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"name": "astra",
|
"name": "astra",
|
||||||
"displayName": "Astra",
|
"displayName": "Astra",
|
||||||
"description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
|
"description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
|
||||||
"version": "2.2.208",
|
"version": "2.2.209",
|
||||||
"publisher": "g1nation",
|
"publisher": "g1nation",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "assets/icon.png",
|
"icon": "assets/icon.png",
|
||||||
|
|||||||
@@ -281,6 +281,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
const { settingsPanel, disposables: settingsDisposables } = setupSettingsPanel(context, {
|
const { settingsPanel, disposables: settingsDisposables } = setupSettingsPanel(context, {
|
||||||
telegramClient,
|
telegramClient,
|
||||||
telegramBot,
|
telegramBot,
|
||||||
|
// 모델 dropdown 이 보유 모델 전부를 보이도록 SDK 다운로드 목록을 전달.
|
||||||
|
lmStudioDownloaded: () => lmStudioClient.listDownloadedCached(),
|
||||||
});
|
});
|
||||||
context.subscriptions.push(...settingsDisposables);
|
context.subscriptions.push(...settingsDisposables);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import type { TelegramHttpClient } from '../integrations/telegram/telegramClient
|
|||||||
export interface SettingsSetupDeps {
|
export interface SettingsSetupDeps {
|
||||||
telegramClient: TelegramHttpClient;
|
telegramClient: TelegramHttpClient;
|
||||||
telegramBot: TelegramBot;
|
telegramBot: TelegramBot;
|
||||||
|
/** LM Studio SDK 다운로드 모델 목록 콜백 — 모델 dropdown 이 보유 모델 전부를 보이도록 전달. */
|
||||||
|
lmStudioDownloaded?: () => Promise<string[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,6 +36,7 @@ export function setupSettingsPanel(
|
|||||||
secrets: context.secrets,
|
secrets: context.secrets,
|
||||||
telegramClient: deps.telegramClient,
|
telegramClient: deps.telegramClient,
|
||||||
telegramBot: deps.telegramBot,
|
telegramBot: deps.telegramBot,
|
||||||
|
lmStudioDownloaded: deps.lmStudioDownloaded,
|
||||||
});
|
});
|
||||||
|
|
||||||
const disposables: vscode.Disposable[] = [
|
const disposables: vscode.Disposable[] = [
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ export interface SettingsPanelDeps {
|
|||||||
telegramClient: ITelegramClient;
|
telegramClient: ITelegramClient;
|
||||||
/** Returns the live bot instance for enrollNextChat. */
|
/** Returns the live bot instance for enrollNextChat. */
|
||||||
telegramBot: TelegramBot;
|
telegramBot: TelegramBot;
|
||||||
|
/**
|
||||||
|
* LM Studio SDK 의 '다운로드된 모든 LLM' 목록 콜백 (보통 lmStudioClient.listDownloadedCached).
|
||||||
|
* 모델 dropdown 이 보유 모델 전부를 보여주도록 discoverModels 에 전달한다.
|
||||||
|
* 없으면 REST `/v1/models` 만 사용 → JIT 환경에서 로드된 1개만 나오는 회귀 발생.
|
||||||
|
*/
|
||||||
|
lmStudioDownloaded?: () => Promise<string[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SettingsState {
|
interface SettingsState {
|
||||||
@@ -434,7 +440,7 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
|||||||
this._modelsLoading = true;
|
this._modelsLoading = true;
|
||||||
await this._refreshState();
|
await this._refreshState();
|
||||||
try {
|
try {
|
||||||
const models = await discoverModels(url);
|
const models = await discoverModels(url, { lmStudioDownloaded: this._deps.lmStudioDownloaded });
|
||||||
this._modelsCache = {
|
this._modelsCache = {
|
||||||
url,
|
url,
|
||||||
models,
|
models,
|
||||||
|
|||||||
@@ -3,17 +3,44 @@ import { resolveEngine, buildApiUrl, logError, logInfo } from '../utils';
|
|||||||
/**
|
/**
|
||||||
* Discover the model list exposed by the local AI engine at `baseUrl`.
|
* Discover the model list exposed by the local AI engine at `baseUrl`.
|
||||||
*
|
*
|
||||||
* Same wire format as the sidebar's `_sendModels` (which still owns the
|
* [가이드라인] 보유한 모델이 *전부* 나와야 한다. LM Studio 의 REST `/v1/models`
|
||||||
* sidebar-specific caching/UI logic) — extracted here so the settings panel
|
* 는 JIT(Just-In-Time) 로딩 설정에서 *현재 로드된* 모델만 반환하므로, 그것만
|
||||||
* can fetch the same list without depending on the sidebar provider.
|
* 쓰면 dropdown 에 1개만 뜨는 회귀가 생긴다. 따라서 LM Studio 에서는 SDK
|
||||||
|
* `system.listDownloadedModels('llm')`(다운로드된 모든 LLM)을 **우선** 시도하고,
|
||||||
|
* 실패/0개일 때만 REST 로 폴백한다. 사이드바 `ModelDiscovery` 와 동일한 정책 —
|
||||||
|
* 두 경로가 갈라지면 또 회귀하므로 반드시 같은 우선순위를 유지할 것.
|
||||||
|
*
|
||||||
|
* `opts.lmStudioDownloaded` 는 LM Studio SDK 의 다운로드 모델 목록 콜백
|
||||||
|
* (보통 `lmStudioClient.listDownloadedCached`). 제공되지 않으면 REST 만 사용.
|
||||||
*
|
*
|
||||||
* Returns an empty array on any failure (offline engine, parse error, etc.).
|
* Returns an empty array on any failure (offline engine, parse error, etc.).
|
||||||
* Callers should treat the result as a hint, not a hard list.
|
* Callers should treat the result as a hint, not a hard list.
|
||||||
*/
|
*/
|
||||||
export async function discoverModels(baseUrl: string, timeoutMs: number = 5000): Promise<string[]> {
|
export async function discoverModels(
|
||||||
|
baseUrl: string,
|
||||||
|
opts: { timeoutMs?: number; lmStudioDownloaded?: () => Promise<string[]> } = {},
|
||||||
|
): Promise<string[]> {
|
||||||
|
const { timeoutMs = 5000, lmStudioDownloaded } = opts;
|
||||||
const url = (baseUrl || '').trim();
|
const url = (baseUrl || '').trim();
|
||||||
if (!url) return [];
|
if (!url) return [];
|
||||||
const engine = resolveEngine(url);
|
const engine = resolveEngine(url);
|
||||||
|
|
||||||
|
// 1) LM Studio + SDK 우선 — 다운로드된 모든 모델(로드 여부 무관).
|
||||||
|
if (engine === 'lmstudio' && lmStudioDownloaded) {
|
||||||
|
try {
|
||||||
|
const sdk = await lmStudioDownloaded();
|
||||||
|
const filtered = sdk.filter((m): m is string => typeof m === 'string' && m.length > 0);
|
||||||
|
if (filtered.length > 0) {
|
||||||
|
logInfo('discoverModels: SDK 다운로드 모델 사용', { count: filtered.length });
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
logInfo('discoverModels: SDK 0개 — REST 폴백', { engine });
|
||||||
|
} catch (e: any) {
|
||||||
|
logInfo('discoverModels: SDK 실패 — REST 폴백', { error: e?.message ?? String(e) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) REST 폴백 (`/v1/models` lmstudio · `/api/tags` ollama)
|
||||||
const modelsUrl = buildApiUrl(url, engine, 'models');
|
const modelsUrl = buildApiUrl(url, engine, 'models');
|
||||||
try {
|
try {
|
||||||
const res = await fetch(modelsUrl, { signal: AbortSignal.timeout(timeoutMs) });
|
const res = await fetch(modelsUrl, { signal: AbortSignal.timeout(timeoutMs) });
|
||||||
|
|||||||
Reference in New Issue
Block a user