feat: implement next-gen vectorized engine, async architecture, and modernization roadmap v2.32.0

This commit is contained in:
Wonseok Jung
2026-04-30 23:44:36 +09:00
parent 39d46d7c54
commit cd1d6a3da8
20 changed files with 1086 additions and 282 deletions
+90
View File
@@ -0,0 +1,90 @@
import * as fs from 'fs';
import * as path from 'path';
import { getConfig } from '../config';
import { buildApiUrl, logError, logInfo, resolveEngine, summarizeText, _getBrainDir } from '../utils';
/**
* IAIService: AI 모델 호출에 대한 인터페이스
*/
export interface IAIService {
call(prompt: string): Promise<string>;
}
/**
* IBrainService: 지식 베이스(Brain) 조작에 대한 인터페이스
*/
export interface IBrainService {
inject(title: string, markdown: string): Promise<string>;
}
/**
* AIService: Ollama 및 LM Studio 폴백 로직을 포함한 AI 호출 구현체
*/
export class AIService implements IAIService {
public async call(prompt: string): Promise<string> {
const config = getConfig();
const primaryEngine = resolveEngine(config.ollamaUrl);
const engines = primaryEngine === 'lmstudio' ? ['lmstudio', 'ollama'] as const : ['ollama', 'lmstudio'] as const;
let lastError: Error | null = null;
for (const engine of engines) {
const apiUrl = buildApiUrl(config.ollamaUrl, engine, 'chat');
const payload = {
model: config.defaultModel,
messages: [{ role: 'user', content: prompt }],
stream: false
};
try {
logInfo('[AIService] Request started.', { engine, apiUrl });
const res = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(config.timeout)
});
const rawText = await res.text();
if (!res.ok) {
lastError = new Error(`AI call failed: ${res.status} ${summarizeText(rawText, 250)}`);
continue;
}
const data = rawText ? JSON.parse(rawText) as any : {};
const content = engine === 'lmstudio'
? (data.choices?.[0]?.message?.content || '')
: (data.message?.content || data.response || '');
return content;
} catch (error: any) {
lastError = error instanceof Error ? error : new Error(String(error));
logError(`[AIService] ${engine} failed:`, lastError.message);
}
}
throw lastError || new Error('All AI engines failed.');
}
}
/**
* BrainService: 지식 베이스 파일 시스템 저장 및 관리 구현체
*/
export class BrainService implements IBrainService {
public async inject(title: string, markdown: string): Promise<string> {
const brainDir = _getBrainDir();
if (!fs.existsSync(brainDir)) {
fs.mkdirSync(brainDir, { recursive: true });
}
const today = new Date().toISOString().split('T')[0];
const datePath = path.join(brainDir, '00_Raw', today);
if (!fs.existsSync(datePath)) {
fs.mkdirSync(datePath, { recursive: true });
}
const safeTitle = title.replace(/[^a-zA-Z0-9가-힣]/gi, '_');
const filePath = path.join(datePath, `${safeTitle}.md`);
fs.writeFileSync(filePath, markdown, 'utf-8');
return filePath;
}
}