fix: some telemetry APIs don't currently work (#1188)

Summary:

This bug is surfaced by using the http LS client. The issue is that
non-scalar values in 'GET' method are `body` params in fastAPI, but our
spec generation script doesn't respect that. We fix by just making them
POST method instead.

Test Plan:
Test API call with newly sync'd client
(https://github.com/meta-llama/llama-stack-client-python/pull/149)

<img width="1114" alt="image"
src="https://github.com/user-attachments/assets/7710aca5-d163-4e00-a465-14e6fcaac2b2"
/>
This commit is contained in:
ehhuang 2025-02-20 14:09:25 -08:00 committed by GitHub
parent ea1faae50e
commit 1166afdf76
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 179 additions and 158 deletions

View file

@ -1073,7 +1073,7 @@
}
},
"/v1/telemetry/spans/{span_id}/tree": {
"get": {
"post": {
"responses": {
"200": {
"description": "OK",
@ -1098,27 +1098,18 @@
"schema": {
"type": "string"
}
},
{
"name": "attributes_to_return",
"in": "query",
"required": false,
"schema": {
"type": "array",
"items": {
"type": "string"
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetSpanTreeRequest"
}
}
},
{
"name": "max_depth",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
}
]
"required": true
}
}
},
"/v1/tools/{tool_name}": {
@ -2263,7 +2254,7 @@
}
},
"/v1/telemetry/spans": {
"get": {
"post": {
"responses": {
"200": {
"description": "OK",
@ -2280,42 +2271,21 @@
"Telemetry"
],
"description": "",
"parameters": [
{
"name": "attribute_filters",
"in": "query",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QueryCondition"
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QuerySpansRequest"
}
}
},
{
"name": "attributes_to_return",
"in": "query",
"required": true,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "max_depth",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
}
]
"required": true
}
}
},
"/v1/telemetry/traces": {
"get": {
"post": {
"responses": {
"200": {
"description": "OK",
@ -2332,46 +2302,17 @@
"Telemetry"
],
"description": "",
"parameters": [
{
"name": "attribute_filters",
"in": "query",
"required": false,
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QueryCondition"
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QueryTracesRequest"
}
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "offset",
"in": "query",
"required": false,
"schema": {
"type": "integer"
}
},
{
"name": "order_by",
"in": "query",
"required": false,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
]
"required": true
}
}
},
"/v1/eval/benchmarks/{benchmark_id}/jobs": {
@ -6056,6 +5997,22 @@
],
"title": "Span"
},
"GetSpanTreeRequest": {
"type": "object",
"properties": {
"attributes_to_return": {
"type": "array",
"items": {
"type": "string"
}
},
"max_depth": {
"type": "integer"
}
},
"additionalProperties": false,
"title": "GetSpanTreeRequest"
},
"SpanStatus": {
"type": "string",
"enum": [
@ -7673,6 +7630,32 @@
],
"title": "QueryConditionOp"
},
"QuerySpansRequest": {
"type": "object",
"properties": {
"attribute_filters": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QueryCondition"
}
},
"attributes_to_return": {
"type": "array",
"items": {
"type": "string"
}
},
"max_depth": {
"type": "integer"
}
},
"additionalProperties": false,
"required": [
"attribute_filters",
"attributes_to_return"
],
"title": "QuerySpansRequest"
},
"QuerySpansResponse": {
"type": "object",
"properties": {
@ -7689,6 +7672,31 @@
],
"title": "QuerySpansResponse"
},
"QueryTracesRequest": {
"type": "object",
"properties": {
"attribute_filters": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QueryCondition"
}
},
"limit": {
"type": "integer"
},
"offset": {
"type": "integer"
},
"order_by": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"title": "QueryTracesRequest"
},
"QueryTracesResponse": {
"type": "object",
"properties": {

View file

@ -647,7 +647,7 @@ paths:
schema:
type: string
/v1/telemetry/spans/{span_id}/tree:
get:
post:
responses:
'200':
description: OK
@ -664,18 +664,12 @@ paths:
required: true
schema:
type: string
- name: attributes_to_return
in: query
required: false
schema:
type: array
items:
type: string
- name: max_depth
in: query
required: false
schema:
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/GetSpanTreeRequest'
required: true
/v1/tools/{tool_name}:
get:
responses:
@ -1370,7 +1364,7 @@ paths:
$ref: '#/components/schemas/QueryChunksRequest'
required: true
/v1/telemetry/spans:
get:
post:
responses:
'200':
description: OK
@ -1381,28 +1375,15 @@ paths:
tags:
- Telemetry
description: ''
parameters:
- name: attribute_filters
in: query
required: true
schema:
type: array
items:
$ref: '#/components/schemas/QueryCondition'
- name: attributes_to_return
in: query
required: true
schema:
type: array
items:
type: string
- name: max_depth
in: query
required: false
schema:
type: integer
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/QuerySpansRequest'
required: true
/v1/telemetry/traces:
get:
post:
responses:
'200':
description: OK
@ -1413,31 +1394,13 @@ paths:
tags:
- Telemetry
description: ''
parameters:
- name: attribute_filters
in: query
required: false
schema:
type: array
items:
$ref: '#/components/schemas/QueryCondition'
- name: limit
in: query
required: false
schema:
type: integer
- name: offset
in: query
required: false
schema:
type: integer
- name: order_by
in: query
required: false
schema:
type: array
items:
type: string
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/QueryTracesRequest'
required: true
/v1/eval/benchmarks/{benchmark_id}/jobs:
post:
responses:
@ -3933,6 +3896,17 @@ components:
- name
- start_time
title: Span
GetSpanTreeRequest:
type: object
properties:
attributes_to_return:
type: array
items:
type: string
max_depth:
type: integer
additionalProperties: false
title: GetSpanTreeRequest
SpanStatus:
type: string
enum:
@ -4975,6 +4949,24 @@ components:
- gt
- lt
title: QueryConditionOp
QuerySpansRequest:
type: object
properties:
attribute_filters:
type: array
items:
$ref: '#/components/schemas/QueryCondition'
attributes_to_return:
type: array
items:
type: string
max_depth:
type: integer
additionalProperties: false
required:
- attribute_filters
- attributes_to_return
title: QuerySpansRequest
QuerySpansResponse:
type: object
properties:
@ -4986,6 +4978,23 @@ components:
required:
- data
title: QuerySpansResponse
QueryTracesRequest:
type: object
properties:
attribute_filters:
type: array
items:
$ref: '#/components/schemas/QueryCondition'
limit:
type: integer
offset:
type: integer
order_by:
type: array
items:
type: string
additionalProperties: false
title: QueryTracesRequest
QueryTracesResponse:
type: object
properties:

View file

@ -150,7 +150,14 @@ def _get_endpoint_functions(
print(f"Processing {colored(func_name, 'white')}...")
operation_name = func_name
if operation_name.startswith("get_") or operation_name.endswith("/get"):
if webmethod.method == "GET":
prefix = "get"
elif webmethod.method == "DELETE":
prefix = "delete"
elif webmethod.method == "POST":
prefix = "post"
elif operation_name.startswith("get_") or operation_name.endswith("/get"):
prefix = "get"
elif (
operation_name.startswith("delete_")
@ -160,13 +167,8 @@ def _get_endpoint_functions(
):
prefix = "delete"
else:
if webmethod.method == "GET":
prefix = "get"
elif webmethod.method == "DELETE":
prefix = "delete"
else:
# by default everything else is a POST
prefix = "post"
# by default everything else is a POST
prefix = "post"
yield prefix, operation_name, func_name, func_ref

View file

@ -216,7 +216,7 @@ class Telemetry(Protocol):
@webmethod(route="/telemetry/events", method="POST")
async def log_event(self, event: Event, ttl_seconds: int = DEFAULT_TTL_DAYS * 86400) -> None: ...
@webmethod(route="/telemetry/traces", method="GET")
@webmethod(route="/telemetry/traces", method="POST")
async def query_traces(
self,
attribute_filters: Optional[List[QueryCondition]] = None,
@ -231,7 +231,7 @@ class Telemetry(Protocol):
@webmethod(route="/telemetry/traces/{trace_id:path}/spans/{span_id:path}", method="GET")
async def get_span(self, trace_id: str, span_id: str) -> Span: ...
@webmethod(route="/telemetry/spans/{span_id:path}/tree", method="GET")
@webmethod(route="/telemetry/spans/{span_id:path}/tree", method="POST")
async def get_span_tree(
self,
span_id: str,
@ -239,7 +239,7 @@ class Telemetry(Protocol):
max_depth: Optional[int] = None,
) -> QuerySpanTreeResponse: ...
@webmethod(route="/telemetry/spans", method="GET")
@webmethod(route="/telemetry/spans", method="POST")
async def query_spans(
self,
attribute_filters: List[QueryCondition],

View file

@ -481,6 +481,8 @@ def main():
def extract_path_params(route: str) -> List[str]:
segments = route.split("/")
params = [seg[1:-1] for seg in segments if seg.startswith("{") and seg.endswith("}")]
# to handle path params like {param:path}
params = [param.split(":")[0] for param in params]
return params