fix(instance-ai): compact trace tool metadata

This commit is contained in:
Oleg Ivaniv 2026-05-06 17:05:42 +02:00
parent ca986ba28d
commit f2feb27180
No known key found for this signature in database
2 changed files with 19 additions and 70 deletions

View File

@ -738,6 +738,7 @@ describe('createInstanceAiTraceContext', () => {
expect(redacted.attributes['llm.tool_manifest_ref']).toBe(
redacted.attributes['llm.tool_schema_hash'],
);
expect(redacted.attributes['llm.available_tools']).toBeUndefined();
expect(jsonParse(redacted.attributes['tools'] as string)).toEqual(prompt.tools);
expect(jsonParse(redacted.attributes['invocation_params.tools'] as string)).toEqual(
prompt.tools,
@ -1084,7 +1085,7 @@ describe('createInstanceAiTraceContext', () => {
expect(telemetry.runtimeRootSpanEnabled).toBe(false);
});
it('attaches root agent config without duplicating it into llm steps', async () => {
it('attaches compact root agent config without duplicating tool schemas into agent spans', async () => {
const tracing = await createDetachedSubAgentTraceContext({
threadId: 'thread-1',
conversationId: 'thread-1',
@ -1131,21 +1132,22 @@ describe('createInstanceAiTraceContext', () => {
);
const actorInputs = tracing?.actorRun.inputs as Record<string, unknown>;
const loadedTools = actorInputs.loaded_tools as Array<Record<string, unknown>>;
const loadedToolManifest = jsonParse<Array<Record<string, unknown>>>(
actorInputs.loaded_tool_manifest as string,
);
const systemPrompt = actorInputs.system_prompt as Record<string, unknown>;
expect(actorInputs.task).toBe('Build a workflow');
expect(actorInputs.model).toBe('anthropic/claude-sonnet-4-6');
expect(actorInputs.loaded_tool_count).toBe(2);
expect(actorInputs.loaded_tool_names).toEqual(['build-workflow', 'submit-workflow']);
expect(actorInputs.assigned_tool_count).toBe(2);
expect(actorInputs.assigned_tool_names).toEqual(['build-workflow', 'submit-workflow']);
expect(actorInputs.assigned_tool_schema_hash).toEqual(expect.any(String));
expect(actorInputs.runtime_tool_count).toBe(1);
expect(actorInputs.runtime_tool_names).toEqual(['workspace_read_file']);
expect(actorInputs.loaded_tool_schema_hash).toEqual(expect.any(String));
expect(actorInputs.runtime_tool_schema_hash).toEqual(expect.any(String));
expect(actorInputs.loaded_tool_count).toBeUndefined();
expect(actorInputs.loaded_tool_names).toBeUndefined();
expect(actorInputs.loaded_tool_schema_hash).toBeUndefined();
expect(actorInputs.loaded_tool_manifest).toBeUndefined();
expect(actorInputs.loaded_tools).toBeUndefined();
expect(actorInputs.loaded_tool_catalog).toBeUndefined();
const actorSpan = agentsMock
.getSpans()
.find((span) => span.id === tracing?.actorRun.otelSpanId);
@ -1154,41 +1156,8 @@ describe('createInstanceAiTraceContext', () => {
);
expect(spanInputs.assigned_tool_names).toEqual(['build-workflow', 'submit-workflow']);
expect(spanInputs.runtime_tool_names).toEqual(['workspace_read_file']);
expect(loadedTools).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'build-workflow',
kind: 'local',
source: 'domain',
category: 'workflow',
side_effect: 'write',
}),
expect.objectContaining({
name: 'submit-workflow',
kind: 'local',
source: 'workspace',
category: 'workflow',
side_effect: 'write',
}),
]),
);
const buildWorkflowManifest = loadedToolManifest.find((tool) => tool.name === 'build-workflow');
expect(buildWorkflowManifest).toEqual(
expect.objectContaining({
name: 'build-workflow',
source: 'domain',
category: 'workflow',
side_effect: 'write',
approval: {
default_approval: false,
suspend: false,
resume: false,
},
}),
);
expect(buildWorkflowManifest?.input_schema).toEqual(
expect.objectContaining({ type: 'object' }),
);
expect(spanInputs.loaded_tool_manifest).toBeUndefined();
expect(spanInputs.loaded_tools).toBeUndefined();
expect(systemPrompt.part_01).toEqual(expect.any(String));
expect(systemPrompt.part_02).toEqual(expect.any(String));
});
@ -1229,7 +1198,8 @@ describe('createInstanceAiTraceContext', () => {
const actorInputs = tracing?.actorRun.inputs as Record<string, unknown>;
expect(actorInputs.model).toBe('anthropic/claude-sonnet-4-6');
expect(actorInputs.system_prompt).toBe('system prompt');
expect(actorInputs.loaded_tool_count).toBe(1);
expect(actorInputs.assigned_tool_count).toBe(1);
expect(actorInputs.assigned_tool_names).toEqual(['build-workflow']);
});
it('redacts model secrets from agent trace inputs', () => {

View File

@ -972,7 +972,6 @@ function enrichLangSmithToolAttributes(attributes: Record<string, unknown>): unk
attributes['llm.available_tool_count'] = normalizedTools.length;
attributes['llm.available_tool_names'] = toolNames;
attributes['llm.available_tools'] = serializedTools;
attributes['llm.tool_manifest_ref'] = schemaHash;
attributes['llm.tool_schema_hash'] = schemaHash;
attributes.tools = serializedTools;
@ -1420,41 +1419,21 @@ function summarizeToolSet(
const summaries = Object.entries(tools).map(([name, tool]) =>
summarizeToolForManifest(name, tool),
);
const catalogText = summaries
.map((tool) => {
const name = typeof tool.name === 'string' ? tool.name : 'unknown';
return typeof tool.description === 'string' ? `${name}: ${tool.description}` : name;
})
.join('\n');
const manifestHash = stableHash(summaries);
const toolNames = summaries
.map((tool) => (typeof tool.name === 'string' ? tool.name : undefined))
.filter((name): name is string => name !== undefined);
const aliases: Record<string, unknown> = {};
if (fieldPrefix === 'loaded') {
aliases.assigned_tool_count = summaries.length;
aliases.assigned_tool_names = toolNames;
return {
assigned_tool_count: summaries.length,
assigned_tool_names: toolNames,
assigned_tool_schema_hash: manifestHash,
};
}
if (fieldPrefix === 'runtime') {
aliases.runtime_tool_count = summaries.length;
aliases.runtime_tool_names = toolNames;
}
return {
...aliases,
[`${fieldPrefix}_tool_count`]: summaries.length,
[`${fieldPrefix}_tool_names`]: toolNames,
[`${fieldPrefix}_tool_manifest`]: serializeTraceText(JSON.stringify(summaries)),
[`${fieldPrefix}_tool_schema_hash`]: manifestHash,
[`${fieldPrefix}_tools`]: summaries.map((tool) => ({
name: tool.name,
description: tool.description,
kind: tool.kind,
source: tool.source,
category: tool.category,
side_effect: tool.side_effect,
})),
[`${fieldPrefix}_tool_catalog`]: serializeTraceText(catalogText),
};
}