feat: refactor AI engine logic, remove cross-engine fallback, add retry with backoff, and bump version to 2.80.18
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "Final report with inconsistencies. This should be long enough to pass validation.",
|
"result": "Final report with inconsistencies. This should be long enough to pass validation.",
|
||||||
"createdAt": 1778137049532,
|
"createdAt": 1778170647495,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||||
"createdAt": 1778137049529,
|
"createdAt": 1778170647483,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
"result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||||
"createdAt": 1778137049527,
|
"createdAt": 1778170647479,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "---\nid: stress_conflict_1778137049510\ndate: 2026-05-07T06:57:29.533Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (16ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (2ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (3ms)\n",
|
"result": "---\nid: stress_conflict_1778170647465\ndate: 2026-05-07T16:17:27.498Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (9ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (5ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (12ms)\n",
|
||||||
"createdAt": 1778137049533,
|
"createdAt": 1778170647499,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+11
-11
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"missionId": "stress_conflict_1778137049510",
|
"missionId": "stress_conflict_1778170647465",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"startTime": "2026-05-07T06:57:29.510Z",
|
"startTime": "2026-05-07T16:17:27.465Z",
|
||||||
"totalElapsedMs": 24,
|
"totalElapsedMs": 36,
|
||||||
"results": {
|
"results": {
|
||||||
"planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
"planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||||
"researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
"researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||||
@@ -16,30 +16,30 @@
|
|||||||
{
|
{
|
||||||
"from": "idle",
|
"from": "idle",
|
||||||
"to": "planner",
|
"to": "planner",
|
||||||
"durationMs": 16,
|
"durationMs": 9,
|
||||||
"message": "전략 수립 중...",
|
"message": "전략 수립 중...",
|
||||||
"ts": "2026-05-07T06:57:29.526Z"
|
"ts": "2026-05-07T16:17:27.474Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "planner",
|
"from": "planner",
|
||||||
"to": "researcher",
|
"to": "researcher",
|
||||||
"durationMs": 2,
|
"durationMs": 5,
|
||||||
"message": "핵심 정보 수집 및 분석 중...",
|
"message": "핵심 정보 수집 및 분석 중...",
|
||||||
"ts": "2026-05-07T06:57:29.528Z"
|
"ts": "2026-05-07T16:17:27.479Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "researcher",
|
"from": "researcher",
|
||||||
"to": "writer",
|
"to": "writer",
|
||||||
"durationMs": 3,
|
"durationMs": 12,
|
||||||
"message": "최종 리포트 작성 및 편집 중...",
|
"message": "최종 리포트 작성 및 편집 중...",
|
||||||
"ts": "2026-05-07T06:57:29.531Z"
|
"ts": "2026-05-07T16:17:27.491Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "writer",
|
"from": "writer",
|
||||||
"to": "completed",
|
"to": "completed",
|
||||||
"durationMs": 3,
|
"durationMs": 10,
|
||||||
"message": "미션 완료",
|
"message": "미션 완료",
|
||||||
"ts": "2026-05-07T06:57:29.534Z"
|
"ts": "2026-05-07T16:17:27.501Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resilienceMetrics": {
|
"resilienceMetrics": {
|
||||||
Generated
+2
-6
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "astra",
|
"name": "astra",
|
||||||
"version": "2.80.5",
|
"version": "2.80.18",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "astra",
|
"name": "astra",
|
||||||
"version": "2.80.5",
|
"version": "2.80.18",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"marked": "^18.0.2",
|
"marked": "^18.0.2",
|
||||||
@@ -58,7 +58,6 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@@ -1943,7 +1942,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.10.12",
|
"baseline-browser-mapping": "^2.10.12",
|
||||||
"caniuse-lite": "^1.0.30001782",
|
"caniuse-lite": "^1.0.30001782",
|
||||||
@@ -2841,7 +2839,6 @@
|
|||||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/core": "^29.7.0",
|
"@jest/core": "^29.7.0",
|
||||||
"@jest/types": "^29.6.3",
|
"@jest/types": "^29.6.3",
|
||||||
@@ -4398,7 +4395,6 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
+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.80.17",
|
"version": "2.80.18",
|
||||||
"publisher": "g1nation",
|
"publisher": "g1nation",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "assets/icon.png",
|
"icon": "assets/icon.png",
|
||||||
|
|||||||
+47
-27
@@ -1942,35 +1942,37 @@ export class AgentExecutor {
|
|||||||
temperature: number;
|
temperature: number;
|
||||||
}): Promise<{ response: Response; engine: 'lmstudio' | 'ollama'; apiUrl: string }> {
|
}): Promise<{ response: Response; engine: 'lmstudio' | 'ollama'; apiUrl: string }> {
|
||||||
const { baseUrl, modelName, reqMessages, temperature } = params;
|
const { baseUrl, modelName, reqMessages, temperature } = params;
|
||||||
const primaryEngine = resolveEngine(baseUrl);
|
const engine = resolveEngine(baseUrl); // 사용자가 설정한 엔진만 사용
|
||||||
const engines = primaryEngine === 'lmstudio' ? ['lmstudio', 'ollama'] as const : ['ollama', 'lmstudio'] as const;
|
const apiUrl = buildApiUrl(baseUrl, engine, 'chat');
|
||||||
|
const messageVariants = this.buildEngineMessageVariants(reqMessages, engine);
|
||||||
|
const modelCandidates = this.buildModelCandidates(modelName, engine);
|
||||||
let lastError: Error | null = null;
|
let lastError: Error | null = null;
|
||||||
|
|
||||||
for (const engine of engines) {
|
// 같은 엔진 내에서만 model candidate / message variant retry
|
||||||
const apiUrl = buildApiUrl(baseUrl, engine, 'chat');
|
for (const candidateModel of modelCandidates) {
|
||||||
const messageVariants = this.buildEngineMessageVariants(reqMessages, engine);
|
for (const variant of messageVariants) {
|
||||||
const modelCandidates = this.buildModelCandidates(modelName, engine);
|
const streamBody = {
|
||||||
|
model: candidateModel,
|
||||||
for (const candidateModel of modelCandidates) {
|
messages: variant.messages,
|
||||||
for (const variant of messageVariants) {
|
stream: true,
|
||||||
const streamBody = {
|
...(engine === 'lmstudio'
|
||||||
model: candidateModel,
|
? { max_tokens: 4096, temperature }
|
||||||
messages: variant.messages,
|
: { options: { num_ctx: 32768, num_predict: 4096, temperature } }),
|
||||||
stream: true,
|
};
|
||||||
...(engine === 'lmstudio'
|
|
||||||
? { max_tokens: 4096, temperature }
|
|
||||||
: { options: { num_ctx: 32768, num_predict: 4096, temperature } }),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// 일시적 네트워크 오류용 retry (최대 2회, 지수 backoff)
|
||||||
|
const MAX_RETRIES = 2;
|
||||||
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
||||||
try {
|
try {
|
||||||
|
if (attempt > 0) {
|
||||||
|
const delay = 500 * Math.pow(2, attempt - 1); // 500ms, 1000ms
|
||||||
|
await new Promise(r => setTimeout(r, delay));
|
||||||
|
logInfo('AI streaming request retry.', { engine, attempt, model: candidateModel });
|
||||||
|
}
|
||||||
logInfo('AI streaming request started.', {
|
logInfo('AI streaming request started.', {
|
||||||
engine,
|
engine, apiUrl, model: candidateModel,
|
||||||
apiUrl,
|
variant: variant.name, messageCount: variant.messages.length,
|
||||||
model: candidateModel,
|
attempt
|
||||||
variant: variant.name,
|
|
||||||
messageCount: variant.messages.length,
|
|
||||||
roles: variant.messages.map(message => message.role),
|
|
||||||
firstUserPreview: summarizeText(String(variant.messages.find(message => message.role === 'user')?.content || ''), 300)
|
|
||||||
});
|
});
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -1988,7 +1990,12 @@ export class AgentExecutor {
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errText = await response.text();
|
const errText = await response.text();
|
||||||
lastError = new Error(`AI Engine error (${engine}/${variant.name}): ${response.status} - ${summarizeText(errText, 300)}`);
|
lastError = new Error(`AI Engine error (${engine}/${variant.name}): ${response.status} - ${summarizeText(errText, 300)}`);
|
||||||
logError('AI streaming request returned non-OK status.', { engine, variant: variant.name, apiUrl, status: response.status, body: summarizeText(errText, 500) });
|
logError('AI streaming request returned non-OK status.', {
|
||||||
|
engine, variant: variant.name, apiUrl,
|
||||||
|
status: response.status, body: summarizeText(errText, 500)
|
||||||
|
});
|
||||||
|
// 4xx는 재시도해도 의미없음. 5xx만 재시도.
|
||||||
|
if (response.status >= 400 && response.status < 500) break;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1996,13 +2003,26 @@ export class AgentExecutor {
|
|||||||
return { response, engine, apiUrl };
|
return { response, engine, apiUrl };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
lastError = error instanceof Error ? error : new Error(String(error));
|
lastError = error instanceof Error ? error : new Error(String(error));
|
||||||
logError('AI streaming request failed.', { engine, variant: variant.name, apiUrl, model: candidateModel, error: lastError.message });
|
// AbortError는 사용자가 취소한 것이므로 retry 금지
|
||||||
|
if (lastError.name === 'AbortError') {
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
logError('AI streaming request failed.', {
|
||||||
|
engine, variant: variant.name, apiUrl, model: candidateModel,
|
||||||
|
attempt, error: lastError.message
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw lastError || new Error('Unable to connect to AI engine.');
|
// 명확한 에러 메시지: 어느 엔진이 실패했는지 사용자에게 알림
|
||||||
|
const engineLabel = engine === 'lmstudio' ? 'LM Studio' : 'Ollama';
|
||||||
|
throw new Error(
|
||||||
|
`${engineLabel} 엔진에 연결할 수 없습니다. ` +
|
||||||
|
`${engineLabel}가 실행 중이고 모델 '${modelName}'이 로드되어 있는지 확인하세요. ` +
|
||||||
|
`(원인: ${lastError?.message || 'unknown'})`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private normalizeMessages(messages: ChatMessage[]) {
|
private normalizeMessages(messages: ChatMessage[]) {
|
||||||
|
|||||||
@@ -90,6 +90,14 @@ export function deactivate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function runInitialSetup(context: vscode.ExtensionContext) {
|
async function runInitialSetup(context: vscode.ExtensionContext) {
|
||||||
|
// 이미 사용자가 URL을 설정했다면 자동 감지를 스킵
|
||||||
|
const existingUrl = vscode.workspace.getConfiguration('g1nation').get<string>('ollamaUrl');
|
||||||
|
if (existingUrl && existingUrl.trim()) {
|
||||||
|
context.globalState.update('setupComplete', true);
|
||||||
|
logInfo('Initial setup skipped: ollamaUrl already configured.', { existingUrl });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let engineName = '';
|
let engineName = '';
|
||||||
let modelName = '';
|
let modelName = '';
|
||||||
|
|||||||
+30
-25
@@ -51,6 +51,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
private _currentSessionBrainId: string | null = null;
|
private _currentSessionBrainId: string | null = null;
|
||||||
private _currentNegativePrompt: string = '';
|
private _currentNegativePrompt: string = '';
|
||||||
private readonly _chronicle = new ProjectChronicleManager();
|
private readonly _chronicle = new ProjectChronicleManager();
|
||||||
|
private _modelDiscoveryInFlight = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _extensionUri: vscode.Uri,
|
private readonly _extensionUri: vscode.Uri,
|
||||||
@@ -75,13 +76,18 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
};
|
};
|
||||||
|
|
||||||
// [State Persistence Fix] 사이드바가 다시 보여질 때 세팅값 자동 복원
|
// [State Persistence Fix] 사이드바가 다시 보여질 때 세팅값 자동 복원
|
||||||
|
let _lastVisibilityRefresh = 0;
|
||||||
webviewView.onDidChangeVisibility(() => {
|
webviewView.onDidChangeVisibility(() => {
|
||||||
if (webviewView.visible) {
|
if (!webviewView.visible) return;
|
||||||
logInfo('Sidebar became visible, restoring state...');
|
const now = Date.now();
|
||||||
void this._sendModels();
|
// 5초 이내에 이미 갱신했으면 건너뜀
|
||||||
void this._sendBrainProfiles();
|
if (now - _lastVisibilityRefresh < 5000) return;
|
||||||
void this._sendAgentsList();
|
_lastVisibilityRefresh = now;
|
||||||
}
|
|
||||||
|
logInfo('Sidebar became visible, restoring state...');
|
||||||
|
void this._sendModels();
|
||||||
|
void this._sendBrainProfiles();
|
||||||
|
void this._sendAgentsList();
|
||||||
});
|
});
|
||||||
|
|
||||||
webviewView.webview.html = this._getHtml(webviewView.webview);
|
webviewView.webview.html = this._getHtml(webviewView.webview);
|
||||||
@@ -1950,26 +1956,26 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
|
|
||||||
private async _sendModels() {
|
private async _sendModels() {
|
||||||
if (!this._view) return;
|
if (!this._view) return;
|
||||||
|
if (this._modelDiscoveryInFlight) {
|
||||||
|
logInfo('Model discovery already in progress, skipping.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._modelDiscoveryInFlight = true;
|
||||||
try {
|
try {
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
const url = config.ollamaUrl;
|
const url = config.ollamaUrl;
|
||||||
let defaultModel = config.defaultModel;
|
let defaultModel = config.defaultModel;
|
||||||
let models: string[] = [];
|
let models: string[] = [];
|
||||||
|
|
||||||
const primaryEngine = resolveEngine(url);
|
const engine = resolveEngine(url); // 단일 엔진만
|
||||||
const engines = primaryEngine === 'lmstudio' ? ['lmstudio', 'ollama'] as const : ['ollama', 'lmstudio'] as const;
|
const modelsUrl = buildApiUrl(url, engine, 'models');
|
||||||
|
try {
|
||||||
for (const engine of engines) {
|
logInfo('Model discovery started.', { engine, modelsUrl });
|
||||||
const modelsUrl = buildApiUrl(url, engine, 'models');
|
const res = await fetch(modelsUrl, { signal: AbortSignal.timeout(5000) });
|
||||||
try {
|
const rawText = await res.text();
|
||||||
logInfo('Model discovery started.', { engine, modelsUrl });
|
if (!res.ok) {
|
||||||
const res = await fetch(modelsUrl, { signal: AbortSignal.timeout(5000) });
|
logError('Model discovery returned non-OK status.', { engine, modelsUrl, status: res.status, body: summarizeText(rawText, 300) });
|
||||||
const rawText = await res.text();
|
} else {
|
||||||
if (!res.ok) {
|
|
||||||
logError('Model discovery returned non-OK status.', { engine, modelsUrl, status: res.status, body: summarizeText(rawText, 300) });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = rawText ? JSON.parse(rawText) as any : {};
|
const data = rawText ? JSON.parse(rawText) as any : {};
|
||||||
models = engine === 'lmstudio'
|
models = engine === 'lmstudio'
|
||||||
? (data.data || []).map((m: any) => m.id)
|
? (data.data || []).map((m: any) => m.id)
|
||||||
@@ -1977,11 +1983,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
|
|
||||||
if (models.length > 0) {
|
if (models.length > 0) {
|
||||||
logInfo('Model discovery succeeded.', { engine, count: models.length, modelsPreview: models.slice(0, 5) });
|
logInfo('Model discovery succeeded.', { engine, count: models.length, modelsPreview: models.slice(0, 5) });
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
|
||||||
logError('Model discovery failed.', { engine, modelsUrl, error: e?.message || String(e) });
|
|
||||||
}
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
logError('Model discovery failed.', { engine, modelsUrl, error: e?.message || String(e) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (models.length === 0) {
|
if (models.length === 0) {
|
||||||
@@ -2017,8 +2022,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
this._view.webview.postMessage({ type: 'modelsList', value: { models, selected: defaultModel } });
|
this._view.webview.postMessage({ type: 'modelsList', value: { models, selected: defaultModel } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logError('Model list update failed.', err);
|
logError('Model list update failed.', err);
|
||||||
const fallbackModel = getConfig().defaultModel;
|
} finally {
|
||||||
this._view.webview.postMessage({ type: 'modelsList', value: { models: fallbackModel ? [fallbackModel] : [], selected: fallbackModel } });
|
this._modelDiscoveryInFlight = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user