fix: some telemetry APIs don't currently work

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)
This commit is contained in:
Eric Huang 2025-02-20 13:41:51 -08:00
parent fbec826883
commit df5ed264b2
5 changed files with 179 additions and 158 deletions

View file

@ -1216,7 +1216,7 @@
}
},
"/v1/telemetry/spans/{span_id}/tree": {
"get": {
"post": {
"responses": {
"200": {
"description": "OK",
@ -1241,27 +1241,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}": {
@ -2290,7 +2281,7 @@
}
},
"/v1/telemetry/spans": {
"get": {
"post": {
"responses": {
"200": {
"description": "OK",
@ -2307,42 +2298,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",
@ -2359,46 +2329,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": {
@ -6146,6 +6087,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": [
@ -7688,6 +7645,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": {
@ -7704,6 +7687,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

@ -726,7 +726,7 @@ paths:
schema:
type: string
/v1/telemetry/spans/{span_id}/tree:
get:
post:
responses:
'200':
description: OK
@ -743,18 +743,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:
@ -1379,7 +1373,7 @@ paths:
$ref: '#/components/schemas/QueryChunksRequest'
required: true
/v1/telemetry/spans:
get:
post:
responses:
'200':
description: OK
@ -1390,28 +1384,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
@ -1422,31 +1403,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:
@ -3961,6 +3924,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:
@ -4947,6 +4921,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:
@ -4958,6 +4950,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