Skip to content

Unflatten attributes of spans#119

Closed
Cirilla-zmh wants to merge 2 commits intoagentscope-ai:mainfrom
Cirilla-zmh:minghui/fea_genai_traces
Closed

Unflatten attributes of spans#119
Cirilla-zmh wants to merge 2 commits intoagentscope-ai:mainfrom
Cirilla-zmh:minghui/fea_genai_traces

Conversation

@Cirilla-zmh
Copy link
Collaborator

Change-Id: I829a16958d63f2da330c373ae87ed8a00cc09ab2
Co-developed-by: Cursor noreply@cursor.com

Description

This PR updates the trace system to support both flat and nested span attribute structures by removing automatic unflattening and enhancing attribute access utilities.

The previous implementation automatically unflattened span attributes, which could reduce readability. This change preserves the original structure while ensuring both formats are supported for better compatibility.

Changes

  • Backend: Removed unflattenAttributes() to preserve original OTLP structure. Enhanced value decoding to prioritize complex types and improved token extraction to handle string values.

  • Frontend: Updated SpanPanel and TraceTree components to use getNestedValue() utility for accessing span attributes, supporting both flat (e.g., attributes["gen_ai.operation.name"]) and nested (e.g., attributes.gen_ai.operation.name) structures.

  • Utilities: Enhanced getNestedValue() to first attempt direct key access for flat structures, then fall back to nested access for backward compatibility.

Checklist

Please check the following items before code is ready to be reviewed.

  • Code has been formatted with npm run format command in the root directory
  • Docstrings/comments are in appropriate style (TSDoc/JSDoc for TypeScript/JavaScript, Google style for Python)
  • Related documentation has been updated (e.g. links, examples, etc.)
  • Code is ready for review

Change-Id: I829a16958d63f2da330c373ae87ed8a00cc09ab2
Co-developed-by: Cursor <noreply@cursor.com>
Change-Id: Iaea2ac13b6c090d62c5551cb358261c50695723a
Co-developed-by: Cursor <noreply@cursor.com>
@cla-assistant
Copy link

cla-assistant bot commented Feb 4, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

1 similar comment
@cla-assistant
Copy link

cla-assistant bot commented Feb 4, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@gemini-code-assist
Copy link

Summary of Changes

Hello @Cirilla-zmh, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the trace system's ability to handle diverse span attribute formats. By ceasing the automatic unflattening of attributes on the backend, the system now retains the original OpenTelemetry Protocol structure, which can improve data fidelity and readability. Concurrently, frontend components have been updated to intelligently interpret these attributes, whether they are stored in a flat or nested manner, through an improved utility function. This change ensures broader compatibility and a more robust display of trace data.

Highlights

  • Backend Attribute Handling: The automatic unflattening of span attributes has been removed from the backend, preserving the original OTLP (OpenTelemetry Protocol) structure.
  • Frontend Compatibility: Frontend components (SpanPanel, TraceTree) now utilize an enhanced getNestedValue utility to access span attributes, ensuring compatibility with both flat and nested attribute structures.
  • Enhanced getNestedValue Utility: The getNestedValue utility now prioritizes direct key access for flat attribute structures before attempting nested access, improving flexibility.
  • Robust Value Decoding: Backend attribute decoding logic has been refined to prioritize complex types (arrays, key-value lists) and to correctly parse string values representing numbers for usage tokens.
Changelog
  • packages/client/src/pages/DashboardPage/RunPage/TracingComponent/TracePanel/SpanPanel/index.tsx
    • Integrated getNestedValue for accessing 'agentscope.function.input', 'agentscope.function.output', and 'gen_ai.operation.name' attributes.
  • packages/client/src/pages/DashboardPage/RunPage/TracingComponent/TracePanel/TraceTree/index.tsx
    • Adopted getNestedValue in getDisplayKind for various 'gen_ai' and 'agentscope' attributes.
    • Updated spanTitleDataMap to use getNestedValue for 'agentscope.function.name'.
  • packages/server/src/dao/Trace.ts
    • Improved extractInputTokens and extractOutputTokens to parse numeric values from string attributes.
  • packages/server/src/migrations/1730000000000-AddMessageReplyForeignKey.ts
    • Applied minor formatting adjustments to the class declaration.
  • packages/server/src/otel/processor.ts
    • Removed unflattenObject import and its usage, preserving original OTLP attribute structure.
    • Enhanced decodeAnyValue to prioritize decoding of complex types and to handle primitive types (string, int, double, bool) more accurately, especially concerning protobuf default values.
    • Updated hasGenAi check to support both flat and nested 'gen_ai' attribute structures.
    • Introduced parseJsonValue for recursive JSON string parsing within attributes, and integrated it into loadJsonStrings.
  • packages/shared/src/utils/objectUtils.ts
    • Modified getNestedValue to first attempt direct key lookup for flat attributes before performing nested traversal.
Activity
  • The pull request author has completed all items in the provided checklist, indicating readiness for review.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully refactors the attribute handling across the application to support both flat and nested span attribute structures. By removing the automatic unflattening in the backend and enhancing the getNestedValue utility, the system now offers greater flexibility and compatibility with various OTLP attribute formats. The changes in SpanPanel and TraceTree correctly leverage the updated utility, and the SpanProcessor's decodeAnyValue method has been significantly improved for robust attribute decoding. The token extraction logic in Trace.ts is also more resilient to different data types.

Comment on lines +127 to 130
// Keep attributes in original structure (no unflatten) - preserve as-is from OTLP
return this.loadJsonStrings(
this.decodeKeyValues(attrs),
) as unknown as Attributes;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The removal of this.unflattenAttributes directly addresses the PR's goal of preserving the original OTLP structure, which is a significant architectural change.

Comment on lines +417 to +454
/**
* Recursively parse JSON strings in attributes so nested values (e.g. from
* kvlist_value or SDK) are usable. Only plain objects are recursed; arrays
* and primitives are left as-is to avoid breaking OTLP array/bytes.
*/
private static loadJsonStrings(
attributes: Record<string, unknown>,
): Record<string, unknown> {
const result: Record<string, unknown> = {};
for (const [key, value] of Object.entries(attributes)) {
if (typeof value === 'string') {
try {
result[key] = JSON.parse(value);
} catch {
result[key] = value;
}
} else {
result[key] = value;
}
result[key] = this.parseJsonValue(value);
}
return result;
}

private static parseJsonValue(value: unknown): unknown {
if (typeof value === 'string') {
try {
return this.parseJsonValue(JSON.parse(value));
} catch {
return value;
}
}
if (
value !== null &&
typeof value === 'object' &&
!Array.isArray(value) &&
!(value instanceof Uint8Array)
) {
const obj = value as Record<string, unknown>;
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(obj)) {
out[k] = this.parseJsonValue(v);
}
return out;
}
return value;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The introduction of parseJsonValue and its recursive application within loadJsonStrings is a robust solution for deeply parsing JSON strings embedded in attributes. This ensures that nested JSON structures are correctly deserialized without inadvertently modifying arrays or byte values.

Comment on lines +10 to +16
// Convert path to string if it's an array
const pathString = Array.isArray(path) ? path.join(separator) : path;

// First, try direct access for flat structure (e.g., "gen_ai.input.messages" as a key)
if (pathString in obj) {
return obj[pathString];
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The getNestedValue utility now first attempts direct key access for flat structures before falling back to nested access. This is a crucial enhancement for supporting both attribute formats as described in the pull request, ensuring backward compatibility and flexibility.

Comment on lines +193 to +196
if (typeof value === 'number' && !Number.isNaN(value)) return value;
if (typeof value === 'string') {
const n = Number(value);
return Number.isNaN(n) ? undefined : n;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for extracting input and output tokens has been improved to handle string values by attempting conversion to a number. This adds robustness to the data processing.

Comment on lines +190 to +192
const hasGenAi =
Object.keys(attributes).some((key) => key.startsWith('gen_ai.')) ||
getNestedValue(attributes, 'gen_ai') !== undefined;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The hasGenAi check is now more comprehensive, supporting both flat and nested attribute structures. This ensures better compatibility and accurate detection of gen_ai attributes.

@github-actions
Copy link

This PR is marked as stale because there has been no activity for 30 days. Remove stale label or add new comments or this PR will be closed in 5 day.

@github-actions
Copy link

Close this stale PR.

@github-actions github-actions bot closed this Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant