Update ConnectAI codebase

This commit is contained in:
g1nation
2026-05-18 08:15:01 +09:00
parent 88664c7c6e
commit 86cacaeb03
38 changed files with 1043 additions and 99 deletions
+66
View File
@@ -1302,6 +1302,16 @@ export class AgentExecutor {
memoryLayers: this._lastRetrievalInfo?.usedMemoryLayers ?? [],
note: `continuations=${continuationCount} historyDropped=${reqMessages.length - budgetedHistory.length}`,
});
// ── Devil Agent (도현) — 비활성 시 silent skip. 활성 시 별도 LLM 호출로 반박 카드 emit. ──
// 비동기 — main turn 완료에 영향 없음. 실패해도 main 답변은 보존됨.
void this._maybeEmitDevilRebuttal({
userPrompt: prompt || '',
assistantAnswer: finalAssistantContent,
baseUrl: ollamaUrl,
modelName: actualModel,
contextLength: ctxLimits.contextLength,
engine,
});
} else {
this.webview.postMessage({ type: 'streamChunk', value: finalAssistantContent });
}
@@ -2885,6 +2895,62 @@ export class AgentExecutor {
* "lock() request could not be registered" error this method is helping
* to avoid.
*/
/**
* Devil Agent 반박 emit — main turn 완료 직후 호출 (fire-and-forget).
* 비활성 시 즉시 return. 활성 시 별도 LLM 호출 (callNonStreaming 재사용) 로 짧은 비판 생성.
* 성공 시 webview 에 'devilRebuttal' 메시지 전송 → UI 가 카드로 렌더.
*/
private async _maybeEmitDevilRebuttal(opts: {
userPrompt: string;
assistantAnswer: string;
baseUrl: string;
modelName: string;
contextLength: number;
engine: 'lmstudio' | 'ollama';
}): Promise<void> {
try {
const { isDevilAgentEnabled, generateDevilRebuttal, DEVIL_PERSONA_NAME } =
await import('./features/devilAgent');
if (!isDevilAgentEnabled()) return;
if (!opts.userPrompt.trim() || !opts.assistantAnswer.trim()) return;
// Local callLLM wrapper — callNonStreaming 재사용 (cloud / local 자동 라우팅).
const callLLM = async (system: string, userMessage: string, maxTokens: number) => {
const r = await this.callNonStreaming({
baseUrl: opts.baseUrl,
modelName: opts.modelName,
engine: opts.engine,
messages: [
{ role: 'system', content: system },
{ role: 'user', content: userMessage },
],
temperature: 0.7,
maxTokens,
contextLength: opts.contextLength,
signal: this.abortController?.signal,
});
return r.text;
};
const rebuttal = await generateDevilRebuttal(callLLM, {
userPrompt: opts.userPrompt,
assistantAnswer: opts.assistantAnswer,
});
if (!rebuttal) return;
this.webview?.postMessage({
type: 'devilRebuttal',
value: {
persona: DEVIL_PERSONA_NAME,
text: rebuttal,
// 사용자가 '재반박' 누를 때 원래 컨텍스트로 돌아갈 수 있게 stash.
userPrompt: opts.userPrompt,
assistantAnswer: opts.assistantAnswer,
},
});
} catch (e: any) {
// Devil 실패는 main 답변에 영향 없음 — silent log.
logInfo('Devil rebuttal skipped.', { error: e?.message ?? String(e) });
}
}
private async callNonStreaming(params: {
baseUrl: string;
modelName: string;