mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
feat(ai-builder): Trace the resources the editor attaches to a message (no-changelog) (#35475)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1f2b96f330
commit
b6afb3bdbe
|
|
@ -3914,6 +3914,14 @@ describe('InstanceAiService — editor handoff context resources', () => {
|
|||
expect(source).toContain('buildContextResourcesBlock(contextAttachments)');
|
||||
expect(source).not.toContain('buildContextResourcesBlock(workflowAttachments)');
|
||||
});
|
||||
|
||||
it('traces the attached resources, which the raw message no longer shows', () => {
|
||||
const source = InstanceAiService.toString();
|
||||
|
||||
// Assignment-form tolerant: the wiring is what matters, not whether the
|
||||
// field is spread into the literal or set on it afterwards.
|
||||
expect(source).toMatch(/resourceAttachments\s*[:=]\s*contextAttachments\.map/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('InstanceAiService — workflow verification follow-up gate', () => {
|
||||
|
|
|
|||
|
|
@ -200,6 +200,14 @@ function getErrorMessage(error: unknown): string {
|
|||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
/** A resource attachment as the trace records it: the reference, not its contents. */
|
||||
type TracedResourceAttachment = {
|
||||
type: InstanceAiResourceAttachment['type'];
|
||||
id: string;
|
||||
projectId?: string;
|
||||
executionId?: string;
|
||||
};
|
||||
|
||||
/** Root-run outputs for a suspended segment — keep the LangSmith turn readable (AGENT-371). */
|
||||
function buildSuspensionTraceOutputs(runId: string, suspension: SuspensionInfo | undefined) {
|
||||
const rawMessage = suspension?.suspendPayload.message;
|
||||
|
|
@ -3295,18 +3303,33 @@ export class InstanceAiService {
|
|||
errorReporterExecutionToken = this.instanceAiErrorReporter.beginRun(runId);
|
||||
|
||||
messageId = nanoid();
|
||||
const traceInput: Record<string, unknown> = {
|
||||
message,
|
||||
...(fileAttachments.length
|
||||
? {
|
||||
attachments: fileAttachments.map((attachment) => ({
|
||||
mimeType: attachment.mimeType,
|
||||
size: attachment.data.length,
|
||||
})),
|
||||
}
|
||||
: {}),
|
||||
...(messageGroupId ? { messageGroupId } : {}),
|
||||
};
|
||||
const traceInput: Record<string, unknown> = { message };
|
||||
if (fileAttachments.length) {
|
||||
traceInput.attachments = fileAttachments.map((attachment) => ({
|
||||
mimeType: attachment.mimeType,
|
||||
size: attachment.data.length,
|
||||
}));
|
||||
}
|
||||
// `message` is the user's raw text, so without this the trace has no
|
||||
// record that the editor handed the agent a resource.
|
||||
if (contextAttachments.length) {
|
||||
traceInput.resourceAttachments = contextAttachments.map((attachment) => {
|
||||
const resource: TracedResourceAttachment = {
|
||||
type: attachment.type,
|
||||
id: attachment.id,
|
||||
};
|
||||
if (attachment.type === 'agent') {
|
||||
resource.projectId = attachment.projectId;
|
||||
}
|
||||
if (attachment.type === 'workflow' && attachment.executionId) {
|
||||
resource.executionId = attachment.executionId;
|
||||
}
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
if (messageGroupId) {
|
||||
traceInput.messageGroupId = messageGroupId;
|
||||
}
|
||||
|
||||
// Shared with createExecutionEnvironment so one ProxyTokenManager backs tracing + the run.
|
||||
const proxyRunConfig = await this.createProxyRunConfig(user);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user