Skip to content

Commit af3f015

Browse files
Bug fixes for traces (#477)
* att * clean up * fixes * fixes * fixes * remove unneccesarry logs
1 parent 72ea17c commit af3f015

File tree

9 files changed

+195
-64
lines changed

9 files changed

+195
-64
lines changed

.changeset/long-ads-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@inkeep/agents-manage-ui": patch
3+
---
4+
5+
bug fix for traces

agents-manage-ui/src/app/api/signoz/conversations/[conversationId]/route.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ async function signozQuery(payload: any): Promise<SigNozResp> {
7272
}
7373

7474
try {
75-
logger.info({ payload }, 'SigNoz payload');
7675
const signozEndpoint = `${SIGNOZ_URL}/api/v4/query_range`;
7776
const response = await axios.post(signozEndpoint, payload, {
7877
headers: {
@@ -82,11 +81,11 @@ async function signozQuery(payload: any): Promise<SigNozResp> {
8281
timeout: 30000,
8382
});
8483
const json = response.data as SigNozResp;
85-
const responseData = json?.data?.result?.map((r) => ({
84+
const responseData = json?.data?.result ? json.data.result.map((r) => ({
8685
queryName: r.queryName,
8786
count: r.list?.length,
88-
}));
89-
logger.info({ responseData }, 'SigNoz response (truncated)');
87+
})) : [];
88+
logger.debug({ responseData }, 'SigNoz response (truncated)');
9089
return json;
9190
} catch (e) {
9291
logger.error({ error: e }, 'SigNoz query error');
@@ -553,7 +552,7 @@ function buildConversationListPayload(
553552
...QUERY_FIELD_CONFIGS.STRING_TAG,
554553
},
555554
{
556-
key: SPAN_KEYS.AI_RESPONSE_MODEL,
555+
key: SPAN_KEYS.AI_MODEL_ID,
557556
...QUERY_FIELD_CONFIGS.STRING_TAG,
558557
},
559558
{
@@ -620,7 +619,7 @@ function buildConversationListPayload(
620619
...QUERY_FIELD_CONFIGS.STRING_TAG,
621620
},
622621
{
623-
key: SPAN_KEYS.AI_RESPONSE_MODEL,
622+
key: SPAN_KEYS.AI_MODEL_ID,
624623
...QUERY_FIELD_CONFIGS.STRING_TAG,
625624
},
626625
{
@@ -1068,7 +1067,7 @@ export async function GET(
10681067
result: hasError
10691068
? 'AI generation failed'
10701069
: `AI text generated successfully (${durMs.toFixed(2)}ms)`,
1071-
aiModel: getString(span, SPAN_KEYS.AI_RESPONSE_MODEL, 'Unknown Model'),
1070+
aiModel: getString(span, SPAN_KEYS.AI_MODEL_ID, 'Unknown Model'),
10721071
inputTokens: getNumber(span, SPAN_KEYS.GEN_AI_USAGE_INPUT_TOKENS, 0),
10731072
outputTokens: getNumber(span, SPAN_KEYS.GEN_AI_USAGE_OUTPUT_TOKENS, 0),
10741073
aiResponseText: getString(span, SPAN_KEYS.AI_RESPONSE_TEXT, '') || undefined,
@@ -1118,7 +1117,7 @@ export async function GET(
11181117
? 'AI streaming failed'
11191118
: `AI text streamed successfully (${durMs.toFixed(2)}ms)`,
11201119
aiStreamTextContent: getString(span, SPAN_KEYS.AI_RESPONSE_TEXT, ''),
1121-
aiStreamTextModel: getString(span, SPAN_KEYS.AI_RESPONSE_MODEL, 'Unknown Model'),
1120+
aiStreamTextModel: getString(span, SPAN_KEYS.AI_MODEL_ID, 'Unknown Model'),
11221121
aiStreamTextOperationId: getString(span, SPAN_KEYS.AI_OPERATION_ID, '') || undefined,
11231122
inputTokens: getNumber(span, SPAN_KEYS.GEN_AI_USAGE_INPUT_TOKENS, 0),
11241123
outputTokens: getNumber(span, SPAN_KEYS.GEN_AI_USAGE_OUTPUT_TOKENS, 0),

agents-manage-ui/src/app/api/signoz/route.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ export async function POST(request: NextRequest) {
9292
logger.info({ status: response.status }, 'SigNoz response received');
9393

9494
const data = response.data;
95-
logger.info({ dataLength: JSON.stringify(data).length }, 'SigNoz response data received');
9695

9796
return NextResponse.json(data);
9897
} catch (error) {

agents-manage-ui/src/components/traces/conversation-detail.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getSignozTracesExplorerUrl } from '@/lib/utils/signoz-links';
2323
import { SignozLink } from './signoz-link';
2424
import { InfoRow } from './timeline/blocks';
2525
import { TimelineWrapper } from './timeline/timeline-wrapper';
26+
import { useRuntimeConfig } from '@/contexts/runtime-config-context';
2627

2728
interface ConversationDetailProps {
2829
conversationId: string;
@@ -34,6 +35,7 @@ export function ConversationDetail({ conversationId, onBack }: ConversationDetai
3435
const [loading, setLoading] = useState(true);
3536
const [error, setError] = useState<string | null>(null);
3637
const { tenantId, projectId } = useParams();
38+
const { SIGNOZ_URL } = useRuntimeConfig();
3739

3840
useEffect(() => {
3941
const fetchConversationDetail = async () => {
@@ -264,7 +266,7 @@ export function ConversationDetail({ conversationId, onBack }: ConversationDetai
264266
size="sm"
265267
className="mt-3 w-full flex items-center justify-center gap-1"
266268
onClick={() => {
267-
window.open(getSignozTracesExplorerUrl(conversationId as string), '_blank');
269+
window.open(getSignozTracesExplorerUrl(conversationId as string, SIGNOZ_URL), '_blank');
268270
}}
269271
>
270272
<ExternalLinkIcon className="h-3 w-3" />

agents-manage-ui/src/components/traces/timeline/render-panel-content.tsx

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { Bubble, CodeBubble } from '@/components/traces/timeline/bubble';
1313
import type { ConversationDetail, SelectedPanel } from '@/components/traces/timeline/types';
1414
import { Badge } from '@/components/ui/badge';
15+
import { SpanAttributes } from '@/components/traces/timeline/span-attributes';
1516

1617
export function renderPanelContent({
1718
selected,
@@ -68,15 +69,10 @@ export function renderPanelContent({
6869
</div>
6970
) : null;
7071

71-
const AdvancedBlock = (
72-
<div className="space-y-3">
73-
<div className="text-xs text-muted-foreground mb-2">Span attributes:</div>
74-
{span ? (
75-
<Streamdown>{`\`\`\`json\n${JSON.stringify(span, null, 2)}\n\`\`\``}</Streamdown>
76-
) : (
77-
<div className="text-center py-4 text-xs text-muted-foreground">Span not found</div>
78-
)}
79-
</div>
72+
const AdvancedBlock = span ? (
73+
<SpanAttributes span={span.data} />
74+
) : (
75+
<div className="text-center py-4 text-xs text-muted-foreground">Span not found</div>
8076
);
8177

8278
switch (selected.type) {
@@ -247,19 +243,8 @@ export function renderPanelContent({
247243
<Info label="Timestamp" value={formatDateTime(a.timestamp)} />
248244
</Section>
249245
<Divider />
250-
<div className="mt-2">
251-
{SignozButton}
252-
<div className="space-y-3">
253-
<div className="text-xs text-muted-foreground mb-2">Span Attributes:</div>
254-
{span ? (
255-
<Streamdown>{`\`\`\`json\n${JSON.stringify(span, null, 2)}\n\`\`\``}</Streamdown>
256-
) : (
257-
<div className="text-center py-4 text-xs text-muted-foreground">
258-
Context resolver span not found
259-
</div>
260-
)}
261-
</div>
262-
</div>
246+
{SignozButton}
247+
{AdvancedBlock}
263248
</>
264249
);
265250

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
'use client';
2+
3+
import { Streamdown } from 'streamdown';
4+
import { CodeBubble } from '@/components/traces/timeline/bubble';
5+
import { LabeledBlock } from '@/components/traces/timeline/blocks';
6+
7+
// Constants for attribute categorization and sorting
8+
const PROCESS_ATTRIBUTE_PREFIXES = ['host.', 'process.'] as const;
9+
const PINNED_ATTRIBUTE_KEYS = [
10+
'name',
11+
'spanID',
12+
'parentSpanID',
13+
'traceID',
14+
'tenant.id',
15+
'project.id',
16+
'graph.id',
17+
'conversation.id',
18+
] as const;
19+
20+
// Type definitions
21+
type SpanAttribute = string | number | boolean | object | null | undefined;
22+
type AttributeMap = Record<string, SpanAttribute>;
23+
24+
interface SpanAttributesProps {
25+
span: AttributeMap;
26+
className?: string;
27+
}
28+
29+
interface SeparatedAttributes {
30+
processAttributes: AttributeMap;
31+
otherAttributes: AttributeMap;
32+
hasProcessAttributes: boolean;
33+
}
34+
35+
interface ProcessAttributesSectionProps {
36+
processAttributes: AttributeMap;
37+
}
38+
39+
/**
40+
* Separates span attributes into process-related and other attributes
41+
*/
42+
function separateAttributes(span: AttributeMap): SeparatedAttributes {
43+
const processAttributes: AttributeMap = {};
44+
const otherAttributes: AttributeMap = {};
45+
46+
Object.entries(span).forEach(([key, value]) => {
47+
const isProcessAttribute = PROCESS_ATTRIBUTE_PREFIXES.some(prefix =>
48+
key.startsWith(prefix)
49+
);
50+
51+
if (isProcessAttribute) {
52+
processAttributes[key] = value;
53+
} else {
54+
otherAttributes[key] = value;
55+
}
56+
});
57+
58+
return {
59+
processAttributes,
60+
otherAttributes,
61+
hasProcessAttributes: Object.keys(processAttributes).length > 0,
62+
};
63+
}
64+
65+
/**
66+
* Sorts attributes with pinned keys first, then alphabetically
67+
*/
68+
function sortAttributes(attributes: AttributeMap): AttributeMap {
69+
const pinnedAttributes: AttributeMap = {};
70+
const remainingAttributes: AttributeMap = {};
71+
72+
// Extract pinned attributes in order
73+
PINNED_ATTRIBUTE_KEYS.forEach(key => {
74+
if (key in attributes) {
75+
pinnedAttributes[key] = attributes[key];
76+
}
77+
});
78+
79+
// Get remaining attributes sorted alphabetically
80+
const remainingKeys = Object.keys(attributes)
81+
.filter(key => !PINNED_ATTRIBUTE_KEYS.includes(key as any))
82+
.sort();
83+
84+
remainingKeys.forEach(key => {
85+
remainingAttributes[key] = attributes[key];
86+
});
87+
88+
return { ...pinnedAttributes, ...remainingAttributes };
89+
}
90+
91+
/**
92+
* Renders process attributes
93+
*/
94+
function ProcessAttributesSection({ processAttributes }: ProcessAttributesSectionProps) {
95+
return (
96+
<div className="border rounded-lg border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50">
97+
<div className="px-3 py-2 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 rounded-t-lg">
98+
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
99+
Process Attributes
100+
</span>
101+
</div>
102+
103+
<div className="p-3 rounded-b-lg">
104+
<CodeBubble className="max-h-60 overflow-y-auto">
105+
<Streamdown>{`\`\`\`json\n${JSON.stringify(processAttributes, null, 2)}\n\`\`\``}</Streamdown>
106+
</CodeBubble>
107+
</div>
108+
</div>
109+
);
110+
}
111+
112+
/**
113+
* Main component for displaying span attributes with proper categorization and sorting
114+
*/
115+
export function SpanAttributes({ span, className }: SpanAttributesProps) {
116+
const { processAttributes, otherAttributes, hasProcessAttributes } = separateAttributes(span);
117+
const sortedOtherAttributes = sortAttributes(otherAttributes);
118+
119+
const hasOtherAttributes = Object.keys(otherAttributes).length > 0;
120+
const hasAnyAttributes = hasOtherAttributes || hasProcessAttributes;
121+
122+
return (
123+
<div className={`space-y-3 ${className ?? ''}`}>
124+
{/* Main span attributes */}
125+
{hasOtherAttributes && (
126+
<LabeledBlock label="Advanced Span Attributes">
127+
<CodeBubble className="max-h-60 overflow-y-auto">
128+
<Streamdown>{`\`\`\`json\n${JSON.stringify(sortedOtherAttributes, null, 2)}\n\`\`\``}</Streamdown>
129+
</CodeBubble>
130+
</LabeledBlock>
131+
)}
132+
133+
{/* Process attributes section */}
134+
{hasProcessAttributes && (
135+
<ProcessAttributesSection processAttributes={processAttributes} />
136+
)}
137+
138+
{/* Empty state */}
139+
{!hasAnyAttributes && (
140+
<div className="text-center py-4 text-xs text-muted-foreground">
141+
No span attributes available
142+
</div>
143+
)}
144+
</div>
145+
);
146+
}

agents-manage-ui/src/components/traces/timeline/timeline-wrapper.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,18 +216,25 @@ export function TimelineWrapper({
216216
.map((activity) => activity.id);
217217
}, [sortedActivities]);
218218

219+
// Memoize stream text IDs for cleaner collapse logic
220+
const streamTextIds = useMemo(() => {
221+
return sortedActivities
222+
.filter((activity) => activity.type === ACTIVITY_TYPES.AI_MODEL_STREAMED_TEXT)
223+
.map((activity) => activity.id);
224+
}, [sortedActivities]);
225+
219226
// Initialize AI messages based on view type when activities change
220227
useEffect(() => {
221228
if (enableAutoScroll) {
222-
// Live trace view: default collapsed
229+
// Live trace view: collapse all AI messages
223230
setCollapsedAiMessages(new Set(aiMessageIds));
224231
setAiMessagesGloballyCollapsed(true);
225232
} else {
226-
// Conversation details view: default expanded
227-
setCollapsedAiMessages(new Set());
228-
setAiMessagesGloballyCollapsed(false);
233+
// Conversation details view: collapse only ai.streamText.doStream spans
234+
setCollapsedAiMessages(new Set(streamTextIds));
235+
setAiMessagesGloballyCollapsed(streamTextIds.length === aiMessageIds.length);
229236
}
230-
}, [aiMessageIds, enableAutoScroll]);
237+
}, [aiMessageIds, streamTextIds, enableAutoScroll]);
231238

232239
// Functions to handle expand/collapse all (memoized to prevent unnecessary re-renders)
233240
const expandAllAiMessages = useCallback(() => {

agents-manage-ui/src/lib/api/signoz-sql.ts

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,6 @@ export async function fetchAllSpanAttributes_SQL(
4343
data: Record<string, any>;
4444
}>
4545
> {
46-
console.log(`🔍 DEBUG - fetchAllSpanAttributes_SQL called with:`, {
47-
conversationId,
48-
sigNozUrl,
49-
});
50-
5146
const results: Array<{
5247
spanId: string;
5348
traceId: string;
@@ -59,8 +54,6 @@ export async function fetchAllSpanAttributes_SQL(
5954
let offset = 0;
6055
const tableName = 'distributed_signoz_index_v3';
6156

62-
console.log(`🔍 DEBUG - Using table: ${tableName}, LIMIT: ${LIMIT}`);
63-
6457
const basePayload = {
6558
start: new Date('2020-01-01T00:00:00Z').getTime(),
6659
end: Date.now(),
@@ -109,26 +102,21 @@ export async function fetchAllSpanAttributes_SQL(
109102
timeout: 30000,
110103
});
111104

112-
console.log(`🔍 DEBUG - Page response status: ${response.status} ${response.statusText}`);
113-
114105
const json = response.data;
115-
console.log(`🔍 DEBUG - Page response JSON:`, JSON.stringify(json, null, 2));
116106

117107
const result = json?.data?.result?.[0];
118108
let rows: SpanRow[] = [];
119-
rows = result.series
120-
.map((s: any) => ({
121-
trace_id: s.labels?.trace_id,
122-
span_id: s.labels?.span_id,
123-
parent_span_id: s.labels?.parent_span_id,
124-
timestamp: s.labels?.timestamp,
125-
name: s.labels?.name,
126-
attributes_string_json: s.labels?.attributes_string_json,
127-
attributes_number_json: s.labels?.attributes_number_json,
128-
attributes_bool_json: s.labels?.attributes_bool_json,
129-
resources_string_json: s.labels?.resources_string_json,
130-
}))
131-
.filter((r: any) => r.trace_id && r.span_id); // Filter out incomplete rows
109+
rows = result?.series ? result.series.map((s: any) => ({
110+
trace_id: s.labels?.trace_id,
111+
span_id: s.labels?.span_id,
112+
parent_span_id: s.labels?.parent_span_id,
113+
timestamp: s.labels?.timestamp,
114+
name: s.labels?.name,
115+
attributes_string_json: s.labels?.attributes_string_json,
116+
attributes_number_json: s.labels?.attributes_number_json,
117+
attributes_bool_json: s.labels?.attributes_bool_json,
118+
resources_string_json: s.labels?.resources_string_json,
119+
})).filter((r: any) => r.trace_id && r.span_id) : []; // Filter out incomplete rows
132120

133121
if (!rows.length) {
134122
break;
@@ -162,7 +150,6 @@ export async function fetchAllSpanAttributes_SQL(
162150

163151
offset += LIMIT;
164152
if (rows.length < LIMIT) {
165-
console.log(`🔍 DEBUG - Last page (${rows.length} < ${LIMIT}), breaking pagination`);
166153
break;
167154
}
168155
} catch (error) {

0 commit comments

Comments
 (0)