Skip to content

observability: replace lossy map<string,string> attributes with google.protobuf.Struct #24

Description

@tanmayv25

Summary

LoadInfo.attributes and RuntimeEvent.attributes in observability.proto use map<string, string>, which is lossy: every value is coerced to a string, so the value type (int / float / bool) and structure (list / nested object) are erased. Consumers (planners / controllers) must re-parse each key by an out-of-band convention, and structured values have to be re-encoded as JSON strings inside the proto.

This is also inconsistent with the schema's other open-ended fields, which already use google.protobuf.Struct:

Affected fields

Location Current Proposed
observability.protoLoadInfo.attributes = 30 map<string, string> google.protobuf.Struct
observability.protoRuntimeEvent.attributes = 4 map<string, string> google.protobuf.Struct

These carry mixed-type telemetry (counts, ratios, flags, lists) — exactly the case where Struct preserves value types and map<string,string> flattens them.

Why it matters

Example RuntimeEvent attributes as they'd naturally be produced:

queue_depth = 42        (integer)
utilization = 0.87      (float)
is_prefill  = true      (bool)
batch_ids   = [1,2,3]   (list)

With map<string, string> these become "42", "0.87", "true", "[1,2,3]" — every consumer re-implements parsing, bool/number encoding is ambiguous, and lists/nested objects require JSON-in-a-string (double encoding). Struct (which is map<string, google.protobuf.Value>) preserves the JSON value types directly.

Options

A. In-place type change (pre-adoption only):

google.protobuf.Struct attributes = 30; // LoadInfo, was map<string, string>
google.protobuf.Struct attributes = 4;  // RuntimeEvent, was map<string, string>

Wire-breaking (a field type change), so only safe while the schema is pre-adoption at revision 1. buf breaking will correctly flag it.

B. New field + deprecate (safe post-adoption):

map<string, string> attributes = 30 [deprecated = true];
google.protobuf.Struct attributes_struct = 31;

Additive; both coexist during migration, old field reserved later.

Caveat

Struct numbers are IEEE-754 doubles (exact only to 2^53), so large int64 telemetry (e.g. cumulative token counters) must be carried as strings or dedicated typed fields — the schema already documents this pattern for KvSessionRef.attributes_struct. See protobuf#7643 and the ProtoJSON format docs.

Recommendation

Since the schema is still pre-adoption at revision 1, Option A is the cheap moment to make it consistent. Deferred from #23 because that PR is strictly additive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions