Column | Type | Example value |
---|---|---|
span_id | UUID | "00000000-0000-0000-1234-426614174000" |
status | String | "error" |
name | String | "openai.chat" |
path | String | "workflow.process.step1.openai.chat" |
trace_id | UUID | "12345678-90ab-cdef-1234-426614174000" |
parent_span_id | UUID | "00000000-0000-0000-a456-abcd5667ef09" |
span_type | UInt8 | 1 |
start_time | DateTime64 | "2021-01-01 00:00:00+00" |
end_time | DateTime64 | "2021-01-01 00:00:00+00" |
input | String | "[{\"role\": \"user\", \"content\": \"Hello, world!\"}]" |
output | String | "[{\"role\": \"assistant\", \"content\": \"Hi! How can I help you today?\"}]" |
attributes | String | "{\"gen_ai.system\": \"openai\", \"gen_ai.model\": \"gpt-4o\"}" |
request_model | String | "gpt-4.1-mini" |
response_model | String | "gpt-4.1-mini-2025-04-14" |
model | String | "gpt-4.1-mini-2025-04-14" |
provider | String | "openai" |
input_tokens | UInt64 | 150 |
output_tokens | UInt64 | 100 |
total_tokens | UInt64 | 250 |
total_cost | Float64 | 0.6897 |
input_cost | Float64 | 0.5667 |
output_cost | Float64 | 0.123 |
["outer", "inner"]
, the path
column will be "outer.inner"
.
If needed, you can still access the array value by accessing the attributes
using simpleJSONExtractRaw(attributes, 'lmnr.span.path')
.
parent_span_id
will be a 0 UUID, i.e. "00000000-0000-0000-0000-000000000000"
.
span_type
column and their meanings:
input
and output
columns are stored as either raw strings or stringified JSONs. The best way to parse them is to try
to parse them as JSON, and if it fails, use the raw string. You can also use isValidJSON
function right in the query to test for this.
input
and output
columns are also indexed on content, so you can use them in WHERE conditions. Use ILIKE
instead of LIKE
, because the index is case-insensitive.
attributes
column is stored as a string in JSON format. That is, you can safely JSON.parse
/ json.loads
them. In addition,
you can use JSON* and simpleJSON* functions on them right in the queries. Attributes are guaranteed to be a valid JSON object.
model
column is set to the response model if present, otherwise it is set to the request model.
total_tokens = input_tokens + output_tokens
and total_cost = input_cost + output_cost
.
However, you can manually report these values using the relevant attributes. In this case, totals may
not be equal to the sum of the input and output tokens and costs.
Column | Type | Example value |
---|---|---|
trace_id | UUID | "01234567-1234-cdef-1234-426614174000" |
trace_type | UInt8 | 0 |
start_time | DateTime64 | "2021-01-01 00:00:00+00" |
end_time | DateTime64 | "2021-01-01 00:00:00+00" |
duration | Float64 | 1.23 |
input_tokens | UInt64 | 150 |
output_tokens | UInt64 | 100 |
total_tokens | UInt64 | 250 |
total_cost | Float64 | 0.6897 |
input_cost | Float64 | 0.5667 |
output_cost | Float64 | 0.123 |
status | String | "error" |
user_id | String | "user_123" |
session_id | String | "session_123" |
metadata | String | "{\"key\": \"value\"}" |
top_span_id | UUID | "00000000-0000-0000-1234-426614174000" |
trace_type
column and their meanings:
end_time - start_time
.
error
. Empty status means success.
JSON.parse
/ json.loads
it. In addition,
you can use JSON* and simpleJSON* functions on it right in the queries. Metadata is guaranteed to be a valid JSON object.
Column | Type | Example value |
---|---|---|
id | UUID | "01234567-89ab-4def-1234-426614174000" |
span_id | UUID | "00000000-0000-0000-1234-426614174000" |
name | String | "My custom event" |
timestamp | DateTime | "2021-01-01 00:00:00+00" |
attributes | String | "{\"key\": \"value\"}" |
user_id | String | "user_123" |
session_id | String | "session_123" |
attributes
column is stored as a string in JSON format. That is, you can safely JSON.parse
/ json.loads
it. In addition,
you can use JSON* and simpleJSON* functions on it right in the queries. Attributes are guaranteed to be a valid JSON object.
Column | Type | Example value |
---|---|---|
id | UUID | "01234567-89ab-4def-1234-426614174000" |
trace_id | UUID | "01234567-1234-cdef-1234-426614174000" |
evaluation_id | UUID | "98765432-1098-4654-3210-987654321098" |
created_at | DateTime64 | "2021-01-01 00:00:00+00" |
data | String | "{\"key\": \"value\"}" |
target | String | "{\"key\": \"value\"}" |
metadata | String | "{\"key\": \"value\"}" |
index | UInt64 | 0 |
{ "score1": 0.85, "score2": 0.90, "score3": 0.95 }
will be stored as 3 rows in the evaluation_scores
table.
Column | Type | Example value |
---|---|---|
evaluation_id | UUID | "01234567-89ab-cdef-1234-426614174000" |
evaluation_datapoint_id | UUID | "98765432-1098-7654-3210-987654321098" |
timestamp | DateTime64 | "2021-01-01 00:00:00+00" |
name | String | "My custom score" |
value | Float64 | 0.85 |
metadata | String | "{\"key\": \"value\"}" |
trace_id | UUID | "01234567-89ab-cdef-1234-426614174000" |
start_time
filter to your query.
Spans table (and thus traces
aggregation on it) is sorted by start_time
and trace_id
, so if you
apply a start_time
WHERE condition, the query will run faster.
Advantages:
input
and output
columns of the spans
table.
The search is optimized to be case-insensitive, so use ILIKE
instead of LIKE
.