From df5ed264b25004a2730a9712cd733c489a116320 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 20 Feb 2025 13:41:51 -0800 Subject: [PATCH] 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) --- 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 2b6e1d11c..b051a81f1 100644 --- a/docs/_static/llama-stack-spec.html +++ b/docs/_static/llama-stack-spec.html @@ -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": { diff --git a/docs/_static/llama-stack-spec.yaml b/docs/_static/llama-stack-spec.yaml index 99300fedf..88663f78c 100644 --- a/docs/_static/llama-stack-spec.yaml +++ b/docs/_static/llama-stack-spec.yaml @@ -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: 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