mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-12-03 09:53:45 +00:00
chore: re-add missing endpoints
_filter_combined_schema was using path-level filtering with _is_path_deprecated, which excluded entire paths if any operation was deprecated. Since /v1/toolgroups has both GET (not deprecated) and POST (deprecated), the entire path was excluded, removing the GET operation and its response schema. Updated _filter_combined_schema to use operation-level filtering, matching _filter_schema_by_version Signed-off-by: Sébastien Han <seb@redhat.com>
This commit is contained in:
parent
2cb0c31edd
commit
73861b504d
3 changed files with 6833 additions and 1554 deletions
File diff suppressed because it is too large
Load diff
410
docs/static/stainless-llama-stack-spec.yaml
vendored
410
docs/static/stainless-llama-stack-spec.yaml
vendored
|
|
@ -1469,64 +1469,6 @@ paths:
|
||||||
description: Default Response
|
description: Default Response
|
||||||
$ref: '#/components/responses/DefaultError'
|
$ref: '#/components/responses/DefaultError'
|
||||||
/v1/responses:
|
/v1/responses:
|
||||||
post:
|
|
||||||
tags:
|
|
||||||
- Agents
|
|
||||||
summary: Create Openai Response
|
|
||||||
description: Create a model response.
|
|
||||||
operationId: create_openai_response_v1_responses_post
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/_responses_Request'
|
|
||||||
x-llama-stack-extra-body-params:
|
|
||||||
guardrails:
|
|
||||||
$defs:
|
|
||||||
ResponseGuardrailSpec:
|
|
||||||
description: |-
|
|
||||||
Specification for a guardrail to apply during response generation.
|
|
||||||
|
|
||||||
:param type: The type/identifier of the guardrail.
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
title: Type
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- type
|
|
||||||
title: ResponseGuardrailSpec
|
|
||||||
type: object
|
|
||||||
anyOf:
|
|
||||||
- items:
|
|
||||||
anyOf:
|
|
||||||
- type: string
|
|
||||||
- $ref: '#/components/schemas/ResponseGuardrailSpec'
|
|
||||||
type: array
|
|
||||||
- type: 'null'
|
|
||||||
description: List of guardrails to apply during response generation. Guardrails provide safety and content moderation.
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: An OpenAIResponseObject.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/OpenAIResponseObject'
|
|
||||||
text/event-stream:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/OpenAIResponseObjectStream'
|
|
||||||
'400':
|
|
||||||
$ref: '#/components/responses/BadRequest400'
|
|
||||||
description: Bad Request
|
|
||||||
'429':
|
|
||||||
$ref: '#/components/responses/TooManyRequests429'
|
|
||||||
description: Too Many Requests
|
|
||||||
'500':
|
|
||||||
$ref: '#/components/responses/InternalServerError500'
|
|
||||||
description: Internal Server Error
|
|
||||||
default:
|
|
||||||
$ref: '#/components/responses/DefaultError'
|
|
||||||
description: Default Response
|
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Agents
|
- Agents
|
||||||
|
|
@ -1587,6 +1529,64 @@ paths:
|
||||||
default:
|
default:
|
||||||
$ref: '#/components/responses/DefaultError'
|
$ref: '#/components/responses/DefaultError'
|
||||||
description: Default Response
|
description: Default Response
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Agents
|
||||||
|
summary: Create Openai Response
|
||||||
|
description: Create a model response.
|
||||||
|
operationId: create_openai_response_v1_responses_post
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/_responses_Request'
|
||||||
|
x-llama-stack-extra-body-params:
|
||||||
|
guardrails:
|
||||||
|
$defs:
|
||||||
|
ResponseGuardrailSpec:
|
||||||
|
description: |-
|
||||||
|
Specification for a guardrail to apply during response generation.
|
||||||
|
|
||||||
|
:param type: The type/identifier of the guardrail.
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
title: Type
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- type
|
||||||
|
title: ResponseGuardrailSpec
|
||||||
|
type: object
|
||||||
|
anyOf:
|
||||||
|
- items:
|
||||||
|
anyOf:
|
||||||
|
- type: string
|
||||||
|
- $ref: '#/components/schemas/ResponseGuardrailSpec'
|
||||||
|
type: array
|
||||||
|
- type: 'null'
|
||||||
|
description: List of guardrails to apply during response generation. Guardrails provide safety and content moderation.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: An OpenAIResponseObject.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OpenAIResponseObject'
|
||||||
|
text/event-stream:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OpenAIResponseObjectStream'
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/responses/BadRequest400'
|
||||||
|
description: Bad Request
|
||||||
|
'429':
|
||||||
|
$ref: '#/components/responses/TooManyRequests429'
|
||||||
|
description: Too Many Requests
|
||||||
|
'500':
|
||||||
|
$ref: '#/components/responses/InternalServerError500'
|
||||||
|
description: Internal Server Error
|
||||||
|
default:
|
||||||
|
$ref: '#/components/responses/DefaultError'
|
||||||
|
description: Default Response
|
||||||
/v1/responses/{response_id}:
|
/v1/responses/{response_id}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
|
|
@ -2335,44 +2335,6 @@ paths:
|
||||||
$ref: '#/components/responses/DefaultError'
|
$ref: '#/components/responses/DefaultError'
|
||||||
description: Default Response
|
description: Default Response
|
||||||
/v1/vector_stores/{vector_store_id}/files:
|
/v1/vector_stores/{vector_store_id}/files:
|
||||||
post:
|
|
||||||
tags:
|
|
||||||
- Vector Io
|
|
||||||
summary: Openai Attach File To Vector Store
|
|
||||||
description: Attach a file to a vector store.
|
|
||||||
operationId: openai_attach_file_to_vector_store_v1_vector_stores__vector_store_id__files_post
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/_vector_stores_vector_store_id_files_Request'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: A VectorStoreFileObject representing the attached file.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/VectorStoreFileObject'
|
|
||||||
'400':
|
|
||||||
$ref: '#/components/responses/BadRequest400'
|
|
||||||
description: Bad Request
|
|
||||||
'429':
|
|
||||||
$ref: '#/components/responses/TooManyRequests429'
|
|
||||||
description: Too Many Requests
|
|
||||||
'500':
|
|
||||||
$ref: '#/components/responses/InternalServerError500'
|
|
||||||
description: Internal Server Error
|
|
||||||
default:
|
|
||||||
$ref: '#/components/responses/DefaultError'
|
|
||||||
description: Default Response
|
|
||||||
parameters:
|
|
||||||
- name: vector_store_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: 'Path parameter: vector_store_id'
|
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Vector Io
|
- Vector Io
|
||||||
|
|
@ -2454,6 +2416,44 @@ paths:
|
||||||
default:
|
default:
|
||||||
$ref: '#/components/responses/DefaultError'
|
$ref: '#/components/responses/DefaultError'
|
||||||
description: Default Response
|
description: Default Response
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Vector Io
|
||||||
|
summary: Openai Attach File To Vector Store
|
||||||
|
description: Attach a file to a vector store.
|
||||||
|
operationId: openai_attach_file_to_vector_store_v1_vector_stores__vector_store_id__files_post
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/_vector_stores_vector_store_id_files_Request'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A VectorStoreFileObject representing the attached file.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VectorStoreFileObject'
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/responses/BadRequest400'
|
||||||
|
description: Bad Request
|
||||||
|
'429':
|
||||||
|
$ref: '#/components/responses/TooManyRequests429'
|
||||||
|
description: Too Many Requests
|
||||||
|
'500':
|
||||||
|
$ref: '#/components/responses/InternalServerError500'
|
||||||
|
description: Internal Server Error
|
||||||
|
default:
|
||||||
|
$ref: '#/components/responses/DefaultError'
|
||||||
|
description: Default Response
|
||||||
|
parameters:
|
||||||
|
- name: vector_store_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: 'Path parameter: vector_store_id'
|
||||||
/v1/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel:
|
/v1/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
|
|
@ -2494,40 +2494,6 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
description: 'Path parameter: batch_id'
|
description: 'Path parameter: batch_id'
|
||||||
/v1/vector_stores:
|
/v1/vector_stores:
|
||||||
post:
|
|
||||||
tags:
|
|
||||||
- Vector Io
|
|
||||||
summary: Openai Create Vector Store
|
|
||||||
description: |-
|
|
||||||
Creates a vector store.
|
|
||||||
|
|
||||||
Generate an OpenAI-compatible vector store with the given parameters.
|
|
||||||
operationId: openai_create_vector_store_v1_vector_stores_post
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/OpenAICreateVectorStoreRequestWithExtraBody'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: A VectorStoreObject representing the created vector store.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/VectorStoreObject'
|
|
||||||
'400':
|
|
||||||
$ref: '#/components/responses/BadRequest400'
|
|
||||||
description: Bad Request
|
|
||||||
'429':
|
|
||||||
$ref: '#/components/responses/TooManyRequests429'
|
|
||||||
description: Too Many Requests
|
|
||||||
'500':
|
|
||||||
$ref: '#/components/responses/InternalServerError500'
|
|
||||||
description: Internal Server Error
|
|
||||||
default:
|
|
||||||
$ref: '#/components/responses/DefaultError'
|
|
||||||
description: Default Response
|
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Vector Io
|
- Vector Io
|
||||||
|
|
@ -2588,6 +2554,40 @@ paths:
|
||||||
default:
|
default:
|
||||||
$ref: '#/components/responses/DefaultError'
|
$ref: '#/components/responses/DefaultError'
|
||||||
description: Default Response
|
description: Default Response
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Vector Io
|
||||||
|
summary: Openai Create Vector Store
|
||||||
|
description: |-
|
||||||
|
Creates a vector store.
|
||||||
|
|
||||||
|
Generate an OpenAI-compatible vector store with the given parameters.
|
||||||
|
operationId: openai_create_vector_store_v1_vector_stores_post
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OpenAICreateVectorStoreRequestWithExtraBody'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A VectorStoreObject representing the created vector store.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VectorStoreObject'
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/responses/BadRequest400'
|
||||||
|
description: Bad Request
|
||||||
|
'429':
|
||||||
|
$ref: '#/components/responses/TooManyRequests429'
|
||||||
|
description: Too Many Requests
|
||||||
|
'500':
|
||||||
|
$ref: '#/components/responses/InternalServerError500'
|
||||||
|
description: Internal Server Error
|
||||||
|
default:
|
||||||
|
$ref: '#/components/responses/DefaultError'
|
||||||
|
description: Default Response
|
||||||
/v1/vector_stores/{vector_store_id}/file_batches:
|
/v1/vector_stores/{vector_store_id}/file_batches:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
|
|
@ -4708,40 +4708,6 @@ paths:
|
||||||
description: Default Response
|
description: Default Response
|
||||||
$ref: '#/components/responses/DefaultError'
|
$ref: '#/components/responses/DefaultError'
|
||||||
/v1/prompts/{prompt_id}:
|
/v1/prompts/{prompt_id}:
|
||||||
delete:
|
|
||||||
tags:
|
|
||||||
- Prompts
|
|
||||||
summary: Delete Prompt
|
|
||||||
description: |-
|
|
||||||
Delete prompt.
|
|
||||||
|
|
||||||
Delete a prompt.
|
|
||||||
operationId: delete_prompt_v1_prompts__prompt_id__delete
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful Response
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: {}
|
|
||||||
'400':
|
|
||||||
$ref: '#/components/responses/BadRequest400'
|
|
||||||
description: Bad Request
|
|
||||||
'429':
|
|
||||||
$ref: '#/components/responses/TooManyRequests429'
|
|
||||||
description: Too Many Requests
|
|
||||||
'500':
|
|
||||||
$ref: '#/components/responses/InternalServerError500'
|
|
||||||
description: Internal Server Error
|
|
||||||
default:
|
|
||||||
$ref: '#/components/responses/DefaultError'
|
|
||||||
description: Default Response
|
|
||||||
parameters:
|
|
||||||
- name: prompt_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: 'Path parameter: prompt_id'
|
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Prompts
|
- Prompts
|
||||||
|
|
@ -4826,6 +4792,40 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
description: 'Path parameter: prompt_id'
|
description: 'Path parameter: prompt_id'
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- Prompts
|
||||||
|
summary: Delete Prompt
|
||||||
|
description: |-
|
||||||
|
Delete prompt.
|
||||||
|
|
||||||
|
Delete a prompt.
|
||||||
|
operationId: delete_prompt_v1_prompts__prompt_id__delete
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Successful Response
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: {}
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/responses/BadRequest400'
|
||||||
|
description: Bad Request
|
||||||
|
'429':
|
||||||
|
$ref: '#/components/responses/TooManyRequests429'
|
||||||
|
description: Too Many Requests
|
||||||
|
'500':
|
||||||
|
$ref: '#/components/responses/InternalServerError500'
|
||||||
|
description: Internal Server Error
|
||||||
|
default:
|
||||||
|
$ref: '#/components/responses/DefaultError'
|
||||||
|
description: Default Response
|
||||||
|
parameters:
|
||||||
|
- name: prompt_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: 'Path parameter: prompt_id'
|
||||||
/v1/prompts/{prompt_id}/versions:
|
/v1/prompts/{prompt_id}/versions:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
|
|
@ -4905,47 +4905,6 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
description: 'Path parameter: prompt_id'
|
description: 'Path parameter: prompt_id'
|
||||||
/v1/conversations/{conversation_id}/items:
|
/v1/conversations/{conversation_id}/items:
|
||||||
post:
|
|
||||||
tags:
|
|
||||||
- Conversations
|
|
||||||
summary: Add Items
|
|
||||||
description: |-
|
|
||||||
Create items.
|
|
||||||
|
|
||||||
Create items in the conversation.
|
|
||||||
operationId: add_items_v1_conversations__conversation_id__items_post
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/_conversations_conversation_id_items_Request'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: List of created items.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ConversationItemList'
|
|
||||||
'400':
|
|
||||||
$ref: '#/components/responses/BadRequest400'
|
|
||||||
description: Bad Request
|
|
||||||
'429':
|
|
||||||
$ref: '#/components/responses/TooManyRequests429'
|
|
||||||
description: Too Many Requests
|
|
||||||
'500':
|
|
||||||
$ref: '#/components/responses/InternalServerError500'
|
|
||||||
description: Internal Server Error
|
|
||||||
default:
|
|
||||||
$ref: '#/components/responses/DefaultError'
|
|
||||||
description: Default Response
|
|
||||||
parameters:
|
|
||||||
- name: conversation_id
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: 'Path parameter: conversation_id'
|
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Conversations
|
- Conversations
|
||||||
|
|
@ -5018,6 +4977,47 @@ paths:
|
||||||
default:
|
default:
|
||||||
$ref: '#/components/responses/DefaultError'
|
$ref: '#/components/responses/DefaultError'
|
||||||
description: Default Response
|
description: Default Response
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Conversations
|
||||||
|
summary: Add Items
|
||||||
|
description: |-
|
||||||
|
Create items.
|
||||||
|
|
||||||
|
Create items in the conversation.
|
||||||
|
operationId: add_items_v1_conversations__conversation_id__items_post
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/_conversations_conversation_id_items_Request'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: List of created items.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ConversationItemList'
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/responses/BadRequest400'
|
||||||
|
description: Bad Request
|
||||||
|
'429':
|
||||||
|
$ref: '#/components/responses/TooManyRequests429'
|
||||||
|
description: Too Many Requests
|
||||||
|
'500':
|
||||||
|
$ref: '#/components/responses/InternalServerError500'
|
||||||
|
description: Internal Server Error
|
||||||
|
default:
|
||||||
|
$ref: '#/components/responses/DefaultError'
|
||||||
|
description: Default Response
|
||||||
|
parameters:
|
||||||
|
- name: conversation_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: 'Path parameter: conversation_id'
|
||||||
/v1/conversations:
|
/v1/conversations:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
|
|
|
||||||
|
|
@ -1647,19 +1647,30 @@ def _filter_combined_schema(openapi_schema: dict[str, Any]) -> dict[str, Any]:
|
||||||
# Filter paths to include stable (v1) and experimental (v1alpha, v1beta), excluding deprecated
|
# Filter paths to include stable (v1) and experimental (v1alpha, v1beta), excluding deprecated
|
||||||
filtered_paths = {}
|
filtered_paths = {}
|
||||||
for path, path_item in filtered_schema["paths"].items():
|
for path, path_item in filtered_schema["paths"].items():
|
||||||
# Check if path has any deprecated operations
|
if not isinstance(path_item, dict):
|
||||||
is_deprecated = _is_path_deprecated(path_item)
|
|
||||||
|
|
||||||
# Skip deprecated endpoints
|
|
||||||
if is_deprecated:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Include stable v1 paths
|
# Filter at operation level, not path level
|
||||||
if _is_stable_path(path):
|
# This allows paths with both deprecated and non-deprecated operations
|
||||||
filtered_paths[path] = path_item
|
filtered_path_item = {}
|
||||||
# Include experimental paths (v1alpha or v1beta)
|
for method in ["get", "post", "put", "delete", "patch", "head", "options"]:
|
||||||
elif _is_experimental_path(path):
|
if method not in path_item:
|
||||||
filtered_paths[path] = path_item
|
continue
|
||||||
|
operation = path_item[method]
|
||||||
|
if not isinstance(operation, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip deprecated operations
|
||||||
|
if operation.get("deprecated", False):
|
||||||
|
continue
|
||||||
|
|
||||||
|
filtered_path_item[method] = operation
|
||||||
|
|
||||||
|
# Only include path if it has at least one operation after filtering
|
||||||
|
if filtered_path_item:
|
||||||
|
# Check if path matches version filter (stable or experimental)
|
||||||
|
if _is_stable_path(path) or _is_experimental_path(path):
|
||||||
|
filtered_paths[path] = filtered_path_item
|
||||||
|
|
||||||
filtered_schema["paths"] = filtered_paths
|
filtered_schema["paths"] = filtered_paths
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue