From 1166afdf7625686e3aeeb3927e4647f477e7d1fe Mon Sep 17 00:00:00 2001 From: ehhuang Date: Thu, 20 Feb 2025 14:09:25 -0800 Subject: [PATCH] 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) image --- docs/_static/llama-stack-spec.html | 182 +++++++++--------- docs/_static/llama-stack-spec.yaml | 129 +++++++------ .../openapi_generator/pyopenapi/operations.py | 18 +- llama_stack/apis/telemetry/telemetry.py | 6 +- llama_stack/distribution/server/server.py | 2 + 5 files changed, 179 insertions(+), 158 deletions(-) diff --git a/docs/_static/llama-stack-spec.html b/docs/_static/llama-stack-spec.html index 6d199e29d..40c167685 100644 --- a/docs/_static/llama-stack-spec.html +++ b/docs/_static/llama-stack-spec.html @@ -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": { diff --git a/docs/_static/llama-stack-spec.yaml b/docs/_static/llama-stack-spec.yaml index f8d8ec5fe..c5043665b 100644 --- a/docs/_static/llama-stack-spec.yaml +++ b/docs/_static/llama-stack-spec.yaml @@ -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: diff --git a/docs/openapi_generator/pyopenapi/operations.py b/docs/openapi_generator/pyopenapi/operations.py index 88a403182..5c78b9124 100644 --- a/docs/openapi_generator/pyopenapi/operations.py +++ b/docs/openapi_generator/pyopenapi/operations.py @@ -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 diff --git a/llama_stack/apis/telemetry/telemetry.py b/llama_stack/apis/telemetry/telemetry.py index d010a7e3b..fe75677e7 100644 --- a/llama_stack/apis/telemetry/telemetry.py +++ b/llama_stack/apis/telemetry/telemetry.py @@ -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], diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py index 0d234d506..8fd95498f 100644 --- a/llama_stack/distribution/server/server.py +++ b/llama_stack/distribution/server/server.py @@ -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