mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
fix(core): Restore the system prompt in Instance AI LLM step debug (no-changelog) (#38887)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
33def0e800
commit
e15b3fc922
|
|
@ -677,6 +677,7 @@ export type { AgentRunState } from './schemas/agent-run-reducer';
|
|||
export {
|
||||
formatDebugJson,
|
||||
summarizeJsonValue,
|
||||
stepInstructions,
|
||||
parseSystemPromptForDisplay,
|
||||
parseMessageBlocks,
|
||||
parseUsageSummary,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
parseUsageSummary,
|
||||
summarizeJsonValue,
|
||||
extractObservationsBlock,
|
||||
stepInstructions,
|
||||
} from '../llm-step-display';
|
||||
|
||||
describe('llm-step-display', () => {
|
||||
|
|
@ -287,4 +288,36 @@ describe('llm-step-display', () => {
|
|||
{ type: 'text', text: 'Skill loading protocol' },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('stepInstructions', () => {
|
||||
it('reads the v7 `instructions` field', () => {
|
||||
expect(stepInstructions({ instructions: 'You are helpful.' })).toBe('You are helpful.');
|
||||
});
|
||||
|
||||
it('falls back to the pre-v7 `system` field for older snapshots', () => {
|
||||
expect(stepInstructions({ system: 'You are helpful.' })).toBe('You are helpful.');
|
||||
});
|
||||
|
||||
it('prefers `instructions` when a snapshot somehow carries both', () => {
|
||||
expect(stepInstructions({ instructions: 'new', system: 'old' })).toBe('new');
|
||||
});
|
||||
|
||||
it('returns undefined for a missing input', () => {
|
||||
expect(stepInstructions(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
// The shape real captures carry: `{ role, content, providerOptions }`.
|
||||
it('lets parseStepSummary size a v7 instructions object', () => {
|
||||
const summary = parseStepSummary(
|
||||
{ instructions: { role: 'system', content: 'x'.repeat(120) } },
|
||||
{ finishReason: 'stop' },
|
||||
);
|
||||
expect(summary.systemCharCount).toBe(120);
|
||||
});
|
||||
|
||||
it('still sizes a pre-v7 string system prompt', () => {
|
||||
const summary = parseStepSummary({ system: 'x'.repeat(42) }, { finishReason: 'stop' });
|
||||
expect(summary.systemCharCount).toBe(42);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -431,6 +431,16 @@ function extractObservationsFromBlock(block: ReadableContentBlock): {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The step's system prompt, wherever the SDK put it. AI SDK v7 standardizes it as
|
||||
* `instructions` (`StandardizedPrompt`); `system` is the pre-v7 name, kept as a
|
||||
* fallback so snapshots captured before the rename still render. Without this the
|
||||
* System section — and the `<observations>` block inside it — reads as empty.
|
||||
*/
|
||||
export function stepInstructions(input: Record<string, unknown> | undefined): unknown {
|
||||
return input?.instructions ?? input?.system;
|
||||
}
|
||||
|
||||
export function parseSystemPromptForDisplay(system: unknown): ParsedSystemPromptDisplay {
|
||||
const blocks = parseSystemBlocks(system);
|
||||
const observationsParts: string[] = [];
|
||||
|
|
@ -588,7 +598,15 @@ export function parseInputExtras(input: Record<string, unknown> | undefined): un
|
|||
if (!input) return undefined;
|
||||
|
||||
const extras: Record<string, unknown> = {};
|
||||
const primaryKeys = new Set(['system', 'messages', 'stepNumber', 'sdkStepNumber']);
|
||||
// `instructions` is the v7 name for `system` — both are rendered as the System
|
||||
// section, so neither belongs in the extras dump.
|
||||
const primaryKeys = new Set([
|
||||
'system',
|
||||
'instructions',
|
||||
'messages',
|
||||
'stepNumber',
|
||||
'sdkStepNumber',
|
||||
]);
|
||||
|
||||
for (const [key, value] of Object.entries(input)) {
|
||||
if (!primaryKeys.has(key)) {
|
||||
|
|
@ -753,11 +771,15 @@ export function parseStepSummary(
|
|||
}
|
||||
}
|
||||
|
||||
// Same field move as the System section: read through `stepInstructions`, or
|
||||
// this silently stays undefined for every v7 snapshot. `parseSystemBlocks`
|
||||
// handles the string, array and single-object forms alike.
|
||||
const instructions = stepInstructions(input);
|
||||
let systemCharCount: number | undefined;
|
||||
if (typeof input?.system === 'string') {
|
||||
systemCharCount = input.system.length;
|
||||
} else if (Array.isArray(input?.system)) {
|
||||
systemCharCount = parseSystemBlocks(input.system).reduce(
|
||||
if (typeof instructions === 'string') {
|
||||
systemCharCount = instructions.length;
|
||||
} else if (instructions !== undefined && instructions !== null) {
|
||||
systemCharCount = parseSystemBlocks(instructions).reduce(
|
||||
(total, block) => total + block.content.length,
|
||||
0,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import {
|
|||
parseStepSummary,
|
||||
parseSystemPromptForDisplay,
|
||||
parseUsageSummary,
|
||||
stepInstructions,
|
||||
} from '@n8n/api-types';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
|
@ -116,7 +117,7 @@ function renderStepDetail(
|
|||
step: InstanceAiRunDebugStep,
|
||||
workflowCode: InstanceAiRunDebugWorkflowCodeSnapshot[],
|
||||
): string {
|
||||
const parsedSystem = parseSystemPromptForDisplay(step.input?.system);
|
||||
const parsedSystem = parseSystemPromptForDisplay(stepInstructions(step.input));
|
||||
const messageBlocks = parseMessageBlocks(step.input?.messages);
|
||||
const inputExtras = parseInputExtras(step.input);
|
||||
const outputBlocks = parseOutputDisplayBlocks(step.output);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
parseOutputExtras,
|
||||
parseSystemPromptForDisplay,
|
||||
parseUsageSummary,
|
||||
stepInstructions,
|
||||
} from '@n8n/api-types';
|
||||
import { N8nText } from '@n8n/design-system';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
|
|
@ -26,7 +27,9 @@ const props = defineProps<{
|
|||
const i18n = useI18n();
|
||||
const outputSectionRef = ref<HTMLElement | null>(null);
|
||||
|
||||
const parsedSystemPrompt = computed(() => parseSystemPromptForDisplay(props.input?.system));
|
||||
const parsedSystemPrompt = computed(() =>
|
||||
parseSystemPromptForDisplay(stepInstructions(props.input)),
|
||||
);
|
||||
const systemBlocks = computed(() => parsedSystemPrompt.value.systemBlocks);
|
||||
const systemObservations = computed(() => parsedSystemPrompt.value.observations);
|
||||
const messageBlocks = computed(() => parseMessageBlocks(props.input?.messages));
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user