feat: extractFromLogs for AI response text, remove failed RPCs, summary fallback

This commit is contained in:
2026-03-07 20:31:58 +09:00
parent b0c2f865c8
commit 0d90b257c3
4 changed files with 97 additions and 132 deletions

Binary file not shown.

View File

@@ -331,81 +331,38 @@ function activate(context) {
if (isFirstPoll) { if (isFirstPoll) {
console.log(`Gravity Bridge: [LS] init ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 30)}"`); console.log(`Gravity Bridge: [LS] init ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 30)}"`);
} }
else if (stepIdx > 0) { else if (stepIdx > 0 && summary) {
// New conversation with AI response! Try to get actual text // New conversation with AI response!
console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`); console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`);
// Try multiple methods to get actual AI response text // Try to extract AI text from extensionLogs
const rpcAttempts = [ const aiText = extractFromLogs(parsed);
{ m: 'LoadTrajectory', p: { trajectoryId: trajId } }, if (aiText) {
{ m: 'LoadTrajectory', p: { googleAgentId: agentId } }, writeChatSnapshot(aiText);
{ m: 'GetCascadeTrajectory', p: { googleAgentId: agentId } }, console.log(`Gravity Bridge: [LS] → relayed AI text (${aiText.length} chars) from logs`);
{ m: 'GetCascadeTrajectorySteps', p: { googleAgentId: agentId } },
];
let gotText = false;
for (const a of rpcAttempts) {
const res = await lsRPC(a.m, a.p);
if (res && !res.code) {
console.log(`Gravity Bridge: [LS] ✅ ${a.m}(${Object.keys(a.p)[0]}) → keys: ${Object.keys(res).join(', ')}`);
console.log(`Gravity Bridge: [LS] sample: ${JSON.stringify(res).substring(0, 1000)}`);
// Try to extract text
const steps = res.steps || res.cortexSteps || [];
if (Array.isArray(steps) && steps.length > 0) {
extractAndRelaySteps(steps.slice(-3)); // last 3 steps
gotText = true;
}
break;
}
else {
const err = res?.message || res?.code || 'empty';
console.log(`Gravity Bridge: [LS] ❌ ${a.m}(${Object.keys(a.p)[0]}): ${typeof err === 'string' ? err.substring(0, 60) : err}`);
}
} }
// Fallback: relay summary else {
if (!gotText && summary) {
writeChatSnapshot(`**${summary}**\n\n(새 대화, step ${stepIdx})`); writeChatSnapshot(`**${summary}**\n\n(새 대화, step ${stepIdx})`);
lastStepIndex[key + '_summary'] = summary;
console.log(`Gravity Bridge: [LS] → summary fallback to Discord`); console.log(`Gravity Bridge: [LS] → summary fallback to Discord`);
} }
lastStepIndex[key + '_summary'] = summary;
} }
continue; continue;
} }
if (stepIdx > prev) { if (stepIdx > prev) {
// New steps detected! // Existing conversation has new steps
console.log(`Gravity Bridge: [LS] ${key.substring(0, 8)} steps: ${prev}${stepIdx} "${summary.substring(0, 40)}"`); console.log(`Gravity Bridge: [LS] ${key.substring(0, 8)} steps: ${prev}${stepIdx} "${summary.substring(0, 40)}"`);
// Try RPC to get full step text // Try to extract AI text from extensionLogs
const attempts = [ const aiText = extractFromLogs(parsed);
{ method: 'GetCascadeTrajectorySteps', params: { googleAgentId: agentId, trajectoryId: trajId, startStepIndex: prev } }, if (aiText) {
{ method: 'GetCascadeTrajectory', params: { googleAgentId: agentId, trajectoryId: trajId } }, writeChatSnapshot(aiText);
]; console.log(`Gravity Bridge: [LS] → relayed AI text (${aiText.length} chars)`);
let stepsResult = null;
for (const attempt of attempts) {
const res = await lsRPC(attempt.method, attempt.params);
if (res && !res.code && !res.message?.includes('not found')) {
stepsResult = res;
console.log(`Gravity Bridge: [LS] ✅ ${attempt.method} worked!`);
console.log(`Gravity Bridge: [LS] keys: ${Object.keys(res).join(', ')}`);
console.log(`Gravity Bridge: [LS] sample: ${JSON.stringify(res).substring(0, 800)}`);
pollFailCount = 0;
break;
}
} }
if (stepsResult) { else if (summary && summary !== lastStepIndex[key + '_summary']) {
const steps = stepsResult.steps || stepsResult.cortexSteps || stepsResult.cortex_steps || []; // Summary changed = new topic
if (Array.isArray(steps) && steps.length > 0) { writeChatSnapshot(`**${summary}**\n\n(step ${prev}${stepIdx})`);
const newSteps = steps.slice(-(stepIdx - prev)); console.log(`Gravity Bridge: [LS] → summary change relayed`);
extractAndRelaySteps(newSteps);
}
}
else {
// Fallback: relay summary text
const summaryKey = key + '_summary';
if (summary && summary !== lastStepIndex[summaryKey]) {
writeChatSnapshot(`[AI 응답 감지]\n\n**${summary}**\n\n(step ${prev}${stepIdx})`);
lastStepIndex[summaryKey] = summary;
console.log(`Gravity Bridge: [LS] relayed summary: "${summary.substring(0, 80)}"`);
}
pollFailCount++;
} }
lastStepIndex[key + '_summary'] = summary;
lastStepIndex[key] = stepIdx; lastStepIndex[key] = stepIdx;
} }
} }
@@ -450,6 +407,34 @@ function activate(context) {
console.log(`Gravity Bridge: [LS] relayed ${messages.length} response(s) (${combined.length} chars) to Discord`); console.log(`Gravity Bridge: [LS] relayed ${messages.length} response(s) (${combined.length} chars) to Discord`);
} }
} }
function extractFromLogs(diagData) {
// Try to find AI response text in extension logs
const logs = diagData.extensionLogs || diagData.extension_logs || '';
if (!logs || typeof logs !== 'string' || logs.length < 10) {
return null;
}
// Look for patterns that indicate AI response text
// Pattern 1: notify_user Message content
const notifyMatch = logs.match(/notify_user.*?"Message"\s*:\s*"([^"]{20,})"/s);
if (notifyMatch) {
return notifyMatch[1];
}
// Pattern 2: "content": "..." blocks from assistant role
const contentMatches = logs.match(/"content"\s*:\s*"([^"]{50,})"/g);
if (contentMatches && contentMatches.length > 0) {
const lastContent = contentMatches[contentMatches.length - 1];
const m = lastContent.match(/"content"\s*:\s*"(.+)"/);
if (m) {
return m[1].replace(/\\n/g, '\n').replace(/\\"/g, '"');
}
}
// Pattern 3: Look for Korean text blocks (likely user-facing response)
const koreanBlocks = logs.match(/[\uAC00-\uD7A3]{10,}[^"]{0,200}/g);
if (koreanBlocks && koreanBlocks.length > 0) {
return koreanBlocks[koreanBlocks.length - 1].substring(0, 500);
}
return null;
}
// Start LS bridge after a delay // Start LS bridge after a delay
setTimeout(async () => { setTimeout(async () => {
const found = await discoverLS(); const found = await discoverLS();

File diff suppressed because one or more lines are too long

View File

@@ -310,87 +310,39 @@ export function activate(context: vscode.ExtensionContext) {
lastStepIndex[key] = stepIdx; lastStepIndex[key] = stepIdx;
if (isFirstPoll) { if (isFirstPoll) {
console.log(`Gravity Bridge: [LS] init ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 30)}"`); console.log(`Gravity Bridge: [LS] init ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 30)}"`);
} else if (stepIdx > 0) { } else if (stepIdx > 0 && summary) {
// New conversation with AI response! Try to get actual text // New conversation with AI response!
console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`); console.log(`Gravity Bridge: [LS] NEW conversation ${key.substring(0, 8)} at step ${stepIdx} "${summary.substring(0, 40)}"`);
// Try multiple methods to get actual AI response text // Try to extract AI text from extensionLogs
const rpcAttempts = [ const aiText = extractFromLogs(parsed);
{ m: 'LoadTrajectory', p: { trajectoryId: trajId } }, if (aiText) {
{ m: 'LoadTrajectory', p: { googleAgentId: agentId } }, writeChatSnapshot(aiText);
{ m: 'GetCascadeTrajectory', p: { googleAgentId: agentId } }, console.log(`Gravity Bridge: [LS] → relayed AI text (${aiText.length} chars) from logs`);
{ m: 'GetCascadeTrajectorySteps', p: { googleAgentId: agentId } }, } else {
];
let gotText = false;
for (const a of rpcAttempts) {
const res = await lsRPC(a.m, a.p);
if (res && !res.code) {
console.log(`Gravity Bridge: [LS] ✅ ${a.m}(${Object.keys(a.p)[0]}) → keys: ${Object.keys(res).join(', ')}`);
console.log(`Gravity Bridge: [LS] sample: ${JSON.stringify(res).substring(0, 1000)}`);
// Try to extract text
const steps = res.steps || res.cortexSteps || [];
if (Array.isArray(steps) && steps.length > 0) {
extractAndRelaySteps(steps.slice(-3)); // last 3 steps
gotText = true;
}
break;
} else {
const err = res?.message || res?.code || 'empty';
console.log(`Gravity Bridge: [LS] ❌ ${a.m}(${Object.keys(a.p)[0]}): ${typeof err === 'string' ? err.substring(0, 60) : err}`);
}
}
// Fallback: relay summary
if (!gotText && summary) {
writeChatSnapshot(`**${summary}**\n\n(새 대화, step ${stepIdx})`); writeChatSnapshot(`**${summary}**\n\n(새 대화, step ${stepIdx})`);
lastStepIndex[key + '_summary'] = summary;
console.log(`Gravity Bridge: [LS] → summary fallback to Discord`); console.log(`Gravity Bridge: [LS] → summary fallback to Discord`);
} }
lastStepIndex[key + '_summary'] = summary;
} }
continue; continue;
} }
if (stepIdx > prev) { if (stepIdx > prev) {
// New steps detected! // Existing conversation has new steps
console.log(`Gravity Bridge: [LS] ${key.substring(0, 8)} steps: ${prev}${stepIdx} "${summary.substring(0, 40)}"`); console.log(`Gravity Bridge: [LS] ${key.substring(0, 8)} steps: ${prev}${stepIdx} "${summary.substring(0, 40)}"`);
// Try RPC to get full step text // Try to extract AI text from extensionLogs
const attempts = [ const aiText = extractFromLogs(parsed);
{ method: 'GetCascadeTrajectorySteps', params: { googleAgentId: agentId, trajectoryId: trajId, startStepIndex: prev } }, if (aiText) {
{ method: 'GetCascadeTrajectory', params: { googleAgentId: agentId, trajectoryId: trajId } }, writeChatSnapshot(aiText);
]; console.log(`Gravity Bridge: [LS] → relayed AI text (${aiText.length} chars)`);
} else if (summary && summary !== lastStepIndex[key + '_summary']) {
let stepsResult: any = null; // Summary changed = new topic
for (const attempt of attempts) { writeChatSnapshot(`**${summary}**\n\n(step ${prev}${stepIdx})`);
const res = await lsRPC(attempt.method, attempt.params); console.log(`Gravity Bridge: [LS] → summary change relayed`);
if (res && !res.code && !res.message?.includes('not found')) {
stepsResult = res;
console.log(`Gravity Bridge: [LS] ✅ ${attempt.method} worked!`);
console.log(`Gravity Bridge: [LS] keys: ${Object.keys(res).join(', ')}`);
console.log(`Gravity Bridge: [LS] sample: ${JSON.stringify(res).substring(0, 800)}`);
pollFailCount = 0;
break;
}
} }
lastStepIndex[key + '_summary'] = summary;
if (stepsResult) {
const steps = stepsResult.steps || stepsResult.cortexSteps || stepsResult.cortex_steps || [];
if (Array.isArray(steps) && steps.length > 0) {
const newSteps = steps.slice(-(stepIdx - prev));
extractAndRelaySteps(newSteps);
}
} else {
// Fallback: relay summary text
const summaryKey = key + '_summary';
if (summary && summary !== lastStepIndex[summaryKey]) {
writeChatSnapshot(`[AI 응답 감지]\n\n**${summary}**\n\n(step ${prev}${stepIdx})`);
lastStepIndex[summaryKey] = summary;
console.log(`Gravity Bridge: [LS] relayed summary: "${summary.substring(0, 80)}"`);
}
pollFailCount++;
}
lastStepIndex[key] = stepIdx; lastStepIndex[key] = stepIdx;
} }
} }
@@ -447,6 +399,34 @@ export function activate(context: vscode.ExtensionContext) {
} }
} }
function extractFromLogs(diagData: any): string | null {
// Try to find AI response text in extension logs
const logs = diagData.extensionLogs || diagData.extension_logs || '';
if (!logs || typeof logs !== 'string' || logs.length < 10) { return null; }
// Look for patterns that indicate AI response text
// Pattern 1: notify_user Message content
const notifyMatch = logs.match(/notify_user.*?"Message"\s*:\s*"([^"]{20,})"/s);
if (notifyMatch) { return notifyMatch[1]; }
// Pattern 2: "content": "..." blocks from assistant role
const contentMatches = logs.match(/"content"\s*:\s*"([^"]{50,})"/g);
if (contentMatches && contentMatches.length > 0) {
const lastContent = contentMatches[contentMatches.length - 1];
const m = lastContent.match(/"content"\s*:\s*"(.+)"/);
if (m) { return m[1].replace(/\\n/g, '\n').replace(/\\"/g, '"'); }
}
// Pattern 3: Look for Korean text blocks (likely user-facing response)
const koreanBlocks = logs.match(/[\uAC00-\uD7A3]{10,}[^"]{0,200}/g);
if (koreanBlocks && koreanBlocks.length > 0) {
return koreanBlocks[koreanBlocks.length - 1].substring(0, 500);
}
return null;
}
// Start LS bridge after a delay // Start LS bridge after a delay
setTimeout(async () => { setTimeout(async () => {