feat: support multiple event proposals in single AI response
- Change proposedChange to proposedChanges array in ChatMessage type - Add unique id and individual respondedAction to each ProposedEventChange - Implement arrow navigation UI for multiple proposals with "Event X von Y" counter - Add updateProposalResponse() method for per-proposal confirm/reject tracking - GPTAdapter now collects multiple tool call results into proposals array - Add RRULE documentation to system prompt (separate events for different times) - Fix RRULE parsing to strip RRULE: prefix if present - Add log summarization for large args (conversationHistory, existingEvents) - Keep proposedChanges logged in full for debugging AI issues
This commit is contained in:
@@ -1,5 +1,57 @@
|
||||
import { createLogger } from "./logger";
|
||||
|
||||
/**
|
||||
* Summarize args for logging to avoid huge log entries.
|
||||
* - Arrays: show length only
|
||||
* - Long strings: truncate
|
||||
* - Objects with conversationHistory/existingEvents: summarize
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function summarizeArgs(args: any[]): any[] {
|
||||
return args.map((arg) => summarizeValue(arg));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function summarizeValue(value: any, depth = 0): any {
|
||||
if (depth > 2) return "[...]";
|
||||
|
||||
if (value === null || value === undefined) return value;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return `[Array(${value.length})]`;
|
||||
}
|
||||
|
||||
if (typeof value === "string" && value.length > 100) {
|
||||
return value.substring(0, 100) + "...";
|
||||
}
|
||||
|
||||
if (typeof value === "object") {
|
||||
// Summarize known large fields
|
||||
const summarized: Record<string, unknown> = {};
|
||||
for (const [key, val] of Object.entries(value)) {
|
||||
if (key === "conversationHistory" && Array.isArray(val)) {
|
||||
summarized[key] = `[${val.length} messages]`;
|
||||
} else if (key === "existingEvents" && Array.isArray(val)) {
|
||||
summarized[key] = `[${val.length} events]`;
|
||||
} else if (key === "proposedChanges" && Array.isArray(val)) {
|
||||
// Log full proposedChanges for debugging AI issues
|
||||
summarized[key] = val.map((p) => summarizeValue(p, depth + 1));
|
||||
} else if (Array.isArray(val)) {
|
||||
summarized[key] = `[Array(${val.length})]`;
|
||||
} else if (typeof val === "object" && val !== null) {
|
||||
summarized[key] = summarizeValue(val, depth + 1);
|
||||
} else if (typeof val === "string" && val.length > 100) {
|
||||
summarized[key] = val.substring(0, 100) + "...";
|
||||
} else {
|
||||
summarized[key] = val;
|
||||
}
|
||||
}
|
||||
return summarized;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function Logged(name: string) {
|
||||
const log = createLogger(name);
|
||||
|
||||
@@ -27,8 +79,8 @@ export function Logged(name: string) {
|
||||
const start = performance.now();
|
||||
const method = String(propKey);
|
||||
|
||||
// Pino's redact handles sanitization - just pass args directly
|
||||
log.debug({ method, args: methodArgs }, `${method} started`);
|
||||
// Summarize args to avoid huge log entries
|
||||
log.debug({ method, args: summarizeArgs(methodArgs) }, `${method} started`);
|
||||
|
||||
const logCompletion = (err?: unknown) => {
|
||||
const duration = Math.round(performance.now() - start);
|
||||
|
||||
Reference in New Issue
Block a user