fix(ai-builder): Include langsmith threadId on traces in code-builder (no-changelog) (#27424)

This commit is contained in:
Benjamin Schroth
2026-03-23 13:26:29 +01:00
committed by GitHub
parent efa5d02979
commit 60d6823877
2 changed files with 47 additions and 2 deletions
@@ -76,7 +76,7 @@ function createMockBuilder() {
}
/**
* Custom callback handler that captures chain end outputs.
* Custom callback handler that captures chain end outputs and chain start metadata.
* We use a class-based handler to ensure proper integration with
* LangChain's CallbackManager.
*/
@@ -84,6 +84,20 @@ class ChainEndTracker extends BaseCallbackHandler {
name = 'chain-end-tracker';
chainEndOutputs: Array<Record<string, unknown>> = [];
chainStartMetadata: Array<Record<string, unknown>> = [];
async handleChainStart(
_chain: unknown,
_inputs: unknown,
_runId: string,
_parentRunId?: string,
_tags?: string[],
metadata?: Record<string, unknown>,
): Promise<void> {
if (metadata) {
this.chainStartMetadata.push(metadata);
}
}
async handleChainEnd(outputs: Record<string, unknown>): Promise<void> {
this.chainEndOutputs.push(outputs);
@@ -205,6 +219,33 @@ describe('CodeBuilderAgent tracing', () => {
]);
});
it('should include ls_thread_id from runMetadata in handleChainStart metadata', async () => {
const tracker = new ChainEndTracker();
const agent = new CodeBuilderAgent({
llm: createMockLlm(),
nodeTypes: [],
callbacks: [tracker],
enableTextEditor: false,
runMetadata: { ls_thread_id: 'workflow-test-wf-user-test-user' },
});
const chunks = [];
for await (const chunk of agent.chat(
{ id: 'msg-4', message: 'Create a simple workflow' },
'user-1',
)) {
chunks.push(chunk);
}
// The parent chain start should include ls_thread_id from runMetadata
const parentStartMetadata = tracker.chainStartMetadata.find((m) => 'ls_thread_id' in m);
expect(parentStartMetadata).toBeDefined();
expect(parentStartMetadata).toMatchObject({
ls_thread_id: 'workflow-test-wf-user-test-user',
});
});
it('should set output to null when no workflow is produced', async () => {
// Make parse fail on all attempts so no workflow is generated
parseWorkflowCodeToBuilder.mockImplementation(() => {
@@ -340,6 +340,9 @@ export class WorkflowBuilderAgent {
userId: string | undefined,
abortSignal: AbortSignal | undefined,
) {
const workflowId = payload.workflowContext?.currentWorkflow?.id;
const threadId = SessionManagerService.generateThreadId(workflowId, userId);
const codeWorkflowBuilder = new CodeWorkflowBuilder({
llm: this.stageLLMs.builder,
nodeTypes: this.parsedNodeTypes,
@@ -351,7 +354,8 @@ export class WorkflowBuilderAgent {
runMetadata: {
...this.runMetadata,
userMessageId: payload.id,
workflowId: payload.workflowContext?.currentWorkflow?.id,
workflowId,
ls_thread_id: threadId,
},
onTelemetryEvent: this.onTelemetryEvent,
generatePinData: payload.featureFlags?.pinData ?? true,