"""Arcade Starter Tools for Weaviate

DO NOT EDIT THIS MODULE DIRECTLY.

THIS MODULE WAS AUTO-GENERATED BY TRANSPILING THE API STARTER TOOL JSON DEFINITIONS
IN THE ../wrapper_tools DIRECTORY INTO PYTHON CODE. ANY CHANGES TO THIS MODULE WILL
BE OVERWRITTEN BY THE TRANSPILER.
"""

import asyncio
import json
from enum import Enum
from typing import Annotated, Any

import httpx
import jsonschema
from arcade_tdk import ToolContext, tool
from arcade_tdk.errors import RetryableToolError

from .request_body_schemas import REQUEST_BODY_SCHEMAS

# Retry configuration
INITIAL_RETRY_DELAY = 0.5  # seconds

HTTP_CLIENT = httpx.AsyncClient(
    timeout=httpx.Timeout(60.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=20, max_connections=100),
    transport=httpx.AsyncHTTPTransport(retries=3),
    http2=True,
    follow_redirects=True,
)


class ToolMode(str, Enum):
    """Mode for tools with complex request bodies."""

    GET_REQUEST_SCHEMA = "get_request_schema"
    EXECUTE = "execute"


def remove_none_values(data: dict[str, Any]) -> dict[str, Any]:
    return {k: v for k, v in data.items() if v is not None}


async def make_request(
    url: str,
    method: str,
    params: dict[str, Any] | None = None,
    headers: dict[str, Any] | None = None,
    content: str | None = None,
    data: dict[str, Any] | None = None,
    auth: tuple[str, str] | None = None,
    max_retries: int = 3,
) -> httpx.Response:
    """Make an HTTP request with retry logic for 5xx server errors."""
    for attempt in range(max_retries):
        try:
            response = await HTTP_CLIENT.request(
                url=url,
                auth=auth,
                method=method,
                params=params,
                headers=headers,
                content=content,
            )
            response.raise_for_status()
        except httpx.HTTPStatusError as e:
            # Only retry on 5xx server errors
            if e.response.status_code >= 500 and attempt < max_retries - 1:
                # Exponential backoff: 0.5s, 1s, 2s
                await asyncio.sleep(INITIAL_RETRY_DELAY * (2**attempt))
                continue
            # Re-raise for 4xx errors or if max retries reached
            raise
        except httpx.RequestError:
            # Don't retry request errors (network issues are handled by transport)
            raise
        else:
            return response

    # This should never be reached, but satisfies type checker
    raise httpx.RequestError("Max retries exceeded")  # noqa: TRY003


async def make_request_with_schema_validation(
    url: str,
    method: str,
    request_data: dict[str, Any],
    schema: dict[str, Any],
    params: dict[str, Any] | None = None,
    headers: dict[str, Any] | None = None,
    max_retries: int = 3,
) -> httpx.Response:
    """Make an HTTP request with schema validation on format errors."""
    try:
        response = await make_request(
            url=url,
            method=method,
            params=params,
            headers=headers,
            content=json.dumps(request_data),
            max_retries=max_retries,
        )
    except httpx.HTTPStatusError as e:
        # Only provide schema validation for format-related errors
        if e.response.status_code in (400, 422):
            # Run validation to provide additional context
            is_valid, validation_error = validate_json_against_schema(request_data, schema)

            api_error_details = f"API returned {e.response.status_code}: {e.response.text}"

            if not is_valid:
                # Schema validation found issues - additional context
                additional_context = (
                    f"{api_error_details}\n\n"
                    f"Schema validation found the following issues:\n"
                    f"{validation_error}"
                )
            else:
                # Schema validation passed - just show API error
                additional_context = api_error_details

            raise RetryableToolError(
                message=(f"API request failed with validation error: {e.response.status_code}"),
                developer_message=api_error_details,
                additional_prompt_content=additional_context,
            ) from e
        else:
            # For non-validation errors, re-raise as-is
            raise
    else:
        return response


def validate_json_against_schema(
    json_data: dict[str, Any], schema: dict[str, Any]
) -> tuple[bool, str | None]:
    """Validate JSON data against an OpenAPI/JSON Schema.

    This provides full JSON Schema Draft 7 validation including:
    - Required fields, types, enums
    - Pattern validation (regex)
    - Format validation (email, uuid, date-time, etc.)
    - Min/max length and values
    - oneOf, anyOf, allOf
    - And all other JSON Schema features

    Args:
        json_data: The JSON data to validate
        schema: The JSON Schema to validate against

    Returns:
        Tuple of (is_valid, error_messages). If valid, error_messages is None.
        If invalid, error_messages contains all validation errors.
    """
    try:
        validator = jsonschema.Draft7Validator(
            schema, format_checker=jsonschema.Draft7Validator.FORMAT_CHECKER
        )
        # Collect ALL validation errors
        errors = list(validator.iter_errors(json_data))
        if errors:
            # Format all errors with their paths
            error_messages = []
            for error in errors:
                error_path = ".".join(str(p) for p in error.path) if error.path else "root"
                error_messages.append(f"{error.message} at {error_path}")
            # Join all errors with newlines
            return False, "\n".join(error_messages)
        else:
            return True, None
    except jsonschema.SchemaError as e:
        return False, f"Invalid schema: {e.message}"
    except Exception as e:
        return False, f"Validation error: {e!s}"


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def discover_api_endpoints(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'weaviate.root'."]:
    """Retrieve links to available REST API endpoints.

    This tool fetches links to other endpoints within the REST API, aiding in the discovery and navigation of the available API options."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def check_weaviate_liveness(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'weaviate.wellknown.liveness'."]:
    """Check if the Weaviate instance is running properly.

    Use this tool to perform a basic health check on a Weaviate instance, ensuring it is running and responding to HTTP requests. Useful for monitoring and maintenance tasks."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/.well-known/live".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def check_weaviate_readiness(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'weaviate.wellknown.readiness'."]:
    """Check if the Weaviate instance is ready to accept traffic.

    Use this tool to determine if the Weaviate instance has completed its startup routines and is ready for operations such as data import and queries. Ideal for readiness checks in container orchestration contexts like Kubernetes."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/.well-known/ready".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_oidc_discovery(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint ''."]:
    """Fetches OIDC discovery details for Weaviate authentication.

    This tool retrieves OpenID Connect (OIDC) discovery information for Weaviate if OIDC authentication is configured. It provides essential details like the token issuer URL, client ID, and required scopes."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/.well-known/openid-configuration".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def replicate_shard_replica(
    context: ToolContext,
    shard_name: Annotated[
        str,
        "The name of the shard whose replica is to be moved or copied. Specify the shard to initiate the operation.",  # noqa: E501
    ],
    source_node: Annotated[
        str,
        "The name of the Weaviate node currently hosting the shard replica to be moved or copied.",
    ],
    target_collection_name: Annotated[
        str,
        "The name of the collection to which the target shard belongs in the Weaviate database.",
    ],
    target_weaviate_node: Annotated[
        str, "Name of the Weaviate node for creating the new shard replica during the operation."
    ],
    replication_operation_type: Annotated[
        str | None,
        "Specifies whether to 'COPY' or 'MOVE' the shard replica. Defaults to 'COPY' if not provided.",  # noqa: E501
    ] = "COPY",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'replicate'."]:
    """Initiates replication of a shard replica to a target node.

    This tool starts an asynchronous process to move or copy a specific shard replica from its current node to a specified target node. It handles data copying, synchronization, and may decommission the source replica."""  # noqa: E501
    request_data: Any = {
        "sourceNode": source_node,
        "targetNode": target_weaviate_node,
        "collection": target_collection_name,
        "shard": shard_name,
        "type": replication_operation_type,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/replicate".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_all_replications(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteAllReplications'."]:
    """Schedule deletion of all replication operations across the system.

    This tool is used to schedule the deletion of all replication operations across all collections, shards, and nodes. It should be called when you need to clear all replication tasks in the system."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/replicate".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def force_delete_replications(
    context: ToolContext,
    collection_name: Annotated[
        str | None, "The name of the collection associated with the shard being replicated."
    ] = None,
    dry_run: Annotated[
        bool | None,
        "When set to true, the operation simulates the deletion and returns the expected result without executing it.",  # noqa: E501
    ] = False,
    replication_operation_id: Annotated[
        str | None,
        "The unique identifier (ID) of the replication operation to be forcefully deleted.",
    ] = None,
    shard_identifier: Annotated[
        str | None, "The unique identifier of the shard involved in the replication operations."
    ] = None,
    target_node_name: Annotated[
        str | None, "The name of the target node where replication operations are registered."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'forceDeleteReplications'."]:
    """Forcefully delete replication operations with caution.

    This tool is used to forcefully delete operations from the FSM in Weaviate. It should be called when there is a need to remove replication operations without performing state checks, which may lead to data corruption or loss. Ensure replication engine workers are scaled to 0 before using this to prevent in-flight operations."""  # noqa: E501
    request_data: Any = {
        "id": replication_operation_id,
        "collection": collection_name,
        "shard": shard_identifier,
        "node": target_node_name,
        "dryRun": dry_run,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/replicate/force-delete".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def fetch_replication_status(
    context: ToolContext,
    replication_operation_id: Annotated[
        str, "The unique identifier for the replication operation to fetch details for."
    ],
    include_history: Annotated[
        bool | None, "Set to true to include the history of the replication operation."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'replicationDetails'."]:
    """Retrieve the status of a specific replication operation.

    Use this tool to get current status and detailed information about a replication operation by its unique ID. It can optionally include historical progress data of the operation."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/replicate/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            id=replication_operation_id,
        ),
        method="GET",
        params=remove_none_values({"includeHistory": include_history}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def cancel_replication_operation(
    context: ToolContext,
    replication_operation_id: Annotated[
        str, "The unique identifier of the replication operation to be canceled and deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteReplication'."]:
    """Cancel an active replication operation.

    Use this tool to remove and cancel a specific replication operation. It ensures that active processes are halted and resources are cleaned up before deletion."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/replicate/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            id=replication_operation_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def list_replication_status(
    context: ToolContext,
    collection_name: Annotated[
        str | None, "Specify the name of the collection for which to retrieve replication details."
    ] = None,
    include_replication_history: Annotated[
        bool | None,
        "Set to true to include the history of the replication operation, false to exclude it.",
    ] = None,
    shard_name: Annotated[
        str | None, "The specific shard for which to retrieve replication details."
    ] = None,
    target_node_name: Annotated[
        str | None, "The name of the target node to retrieve replication operation details for."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listReplication'."]:
    """Retrieve registered replication operations and details.

    Call this tool to get information about current replication operations in Weaviate, filtered by collection, shard, or node ID if needed."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/replicate/list".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({
            "targetNode": target_node_name,
            "collection": collection_name,
            "shard": shard_name,
            "includeHistory": include_replication_history,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def cancel_replication(
    context: ToolContext,
    replication_operation_id: Annotated[
        str,
        "The ID of the replication operation you wish to cancel. This is a string identifier for the specific operation.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'cancelReplication'."]:
    """Cancel an active replication operation by ID.

    Use this tool to request the cancellation of an ongoing replication operation specified by its ID. The operation is stopped and marked as 'CANCELLED' but is not automatically deleted."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/replicate/{id}/cancel".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            id=replication_operation_id,
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def fetch_sharding_state(
    context: ToolContext,
    collection_name: Annotated[
        str | None, "The name of the collection to retrieve the sharding state for."
    ] = None,
    target_shard: Annotated[
        str | None, "Specify the shard name to retrieve its sharding state in a collection."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getCollectionShardingState'."]:
    """Fetch the current sharding state and replica details for collections.

    This tool retrieves the current sharding state for all collections or a specified collection, including replica locations and statuses. If needed, it can also provide the state for a specific shard within a collection."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/replication/sharding-state".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({"collection": collection_name, "shard": target_shard}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_authenticated_user_info(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getOwnInfo'."]:
    """Retrieve details about the authenticated user and their roles.

    This tool retrieves information about the current user, including their username and assigned roles, from the authenticated session."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/own-info".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def list_db_users(
    context: ToolContext,
    include_last_used_time: Annotated[
        bool | None, "Include the last time users were utilized in the response."
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listAllUsers'."]:
    """Retrieve all database users and their roles and statuses.

    Use this tool to obtain a comprehensive list of all users within the database, along with detailed information about their roles and current status. This is useful for auditing and managing user permissions in the database."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/db".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({"includeLastUsedTime": include_last_used_time}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_database_user_info(
    context: ToolContext,
    database_user_name: Annotated[
        str, "The unique identifier or name of the database user to retrieve information for."
    ],
    include_last_used_time: Annotated[
        bool | None, "Set to true to include the last used time in the user's information."
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getUserInfo'."]:
    """Retrieve information about a specific database user.

    Use this tool to get detailed information about a database user, including their roles, status, and type. Call this tool when you need to know specific details about a database user's profile or attributes."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/db/{user_id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            user_id=database_user_name,
        ),
        method="GET",
        params=remove_none_values({"includeLastUsedTime": include_last_used_time}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def create_database_user(
    context: ToolContext,
    user_name: Annotated[
        str,
        "Specify the name for the new database user. It should be a string that identifies the user.",  # noqa: E501
    ],
    disable_import_experimental: Annotated[
        bool | None,
        "Set to true to prevent importing an API key from a static user. Experimental and will be removed.",  # noqa: E501
    ] = False,
    set_creation_time_experimental: Annotated[
        str | None,
        "EXPERIMENTAL: Set the given time as creation time. This will be removed in future versions.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createUser'."]:
    """Create a new database user and obtain an API key.

    This tool is used to create a new database user by specifying a name. It returns an API key for the created user."""  # noqa: E501
    request_data: Any = {
        "import": disable_import_experimental,
        "createTime": set_creation_time_experimental,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/db/{user_id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), user_id=user_name
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_database_user(
    context: ToolContext,
    user_identifier: Annotated[
        str, "Specify the name of the user you want to delete. This cannot be the current user."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteUser'."]:
    """Delete a specific database user.

    This tool deletes a specified user from the database. It should be called when there is a need to remove a user, except the current active user."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/db/{user_id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), user_id=user_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def rotate_user_api_key(
    context: ToolContext,
    database_user_name: Annotated[
        str, "The name of the database user for which the API key will be rotated."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'rotateUserApiKey'."]:
    """Revoke and regenerate the API key for a database user.

    This tool is used to revoke the current API key for a specified database user and generate a new one. It should be called when an API key needs refreshing or if security has been compromised."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/db/{user_id}/rotate-key".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            user_id=database_user_name,
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def activate_database_user(
    context: ToolContext,
    user_name: Annotated[str, "The name of the database user to activate."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'activateUser'."]:
    """Activate a deactivated database user account.

    Use this tool to activate a user with a `db` user type in the database. It should be called when re-enabling user access is needed."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/db/{user_id}/activate".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), user_id=user_name
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def deactivate_database_user(
    context: ToolContext,
    database_user_id: Annotated[
        str, "The unique identifier for the database user to be deactivated."
    ],
    revoke_api_key: Annotated[
        bool | None, "Revoke the user's API key when deactivating. Set to true to enable."
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deactivateUser'."]:
    """Deactivate a database user account.

    Use this tool to deactivate a user with the type 'db' in the database. Provide the user ID to deactivate their account and prevent access to the database."""  # noqa: E501
    request_data: Any = {"revoke_key": revoke_api_key}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/users/db/{user_id}/deactivate".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), user_id=database_user_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_roles_and_permissions(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRoles'."]:
    """Retrieve all roles and their assigned permissions.

    Use this tool to obtain detailed information on all roles and the specific permissions associated with each role."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/roles".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def create_role_with_permissions(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createRole'."]:
    """Create a new role with specified permissions.

    Use this tool to create a new role in the system and define its permissions. It should be called when there is a need to set up access controls for new role-based functionalities.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEROLEWITHPERMISSIONS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEROLEWITHPERMISSIONS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEROLEWITHPERMISSIONS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/authz/roles".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEROLEWITHPERMISSIONS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def add_permissions_to_role(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    role_id: Annotated[
        str | None,
        "The ID of the role to which new permissions will be added.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'addPermissions'."]:
    """Add new permissions to a role without affecting existing ones.

    Use this tool to assign additional permissions to a specified role within a system, ensuring that current permissions remain unchanged.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDPERMISSIONSTOROLE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not role_id:
        missing_params.append(("role_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDPERMISSIONSTOROLE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDPERMISSIONSTOROLE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/authz/roles/{id}/add-permissions".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=role_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDPERMISSIONSTOROLE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def revoke_role_permissions(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    role_name: Annotated[
        str | None,
        "The name of the role from which permissions are being revoked. Removing all permissions will delete the role.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removePermissions'."]:
    """Revoke permissions from a specified role.

    This tool is used to revoke permissions from a specified role in the Weaviate system. If all permissions are removed, the role will be deleted.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "REVOKEROLEPERMISSIONS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not role_name:
        missing_params.append(("role_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REVOKEROLEPERMISSIONS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REVOKEROLEPERMISSIONS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/authz/roles/{id}/remove-permissions".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=role_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REVOKEROLEPERMISSIONS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def fetch_role_by_name(
    context: ToolContext,
    role_name: Annotated[str, "The name of the role to fetch details for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRole'."]:
    """Fetch role details using its name.

    This tool retrieves the details of a specific role by its name from the Weaviate service. It should be called when information about a role is required, typically for authorization or configuration purposes."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/roles/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=role_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_role(
    context: ToolContext,
    role_name: Annotated[str, "Specify the name of the role to be deleted and revoked from users."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteRole'."]:
    """Delete a role and revoke its permissions system-wide.

    Use this tool to delete a role from the system, removing it and revoking the associated permissions from all users who had it."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/roles/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=role_name
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def check_role_permission(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    role_name: Annotated[
        str | None,
        "The name of the role to check permissions for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'hasPermission'."]:
    """Check if a role has specific permissions in the system.

    Use this tool to verify whether a particular role possesses certain permissions within the system. It should be called when there's a need to confirm role access rights.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CHECKROLEPERMISSION_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not role_name:
        missing_params.append(("role_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CHECKROLEPERMISSION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CHECKROLEPERMISSION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/authz/roles/{id}/has-permission".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=role_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CHECKROLEPERMISSION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_users_by_role(
    context: ToolContext,
    role_id: Annotated[str, "The unique identifier for the role to fetch associated users."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getUsersForRole'."]:
    """Retrieve users with a specific role assignment.

    Use to obtain a list of users assigned to a particular role by role ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/roles/{id}/user-assignments".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=role_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_groups_for_role(
    context: ToolContext,
    role_name: Annotated[str, "The unique name of the role to retrieve associated groups."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getGroupsForRole'."]:
    """Retrieve groups assigned to a specific role.

    Use this tool to get a list of all groups that have been assigned a particular role, identified by the role's name."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/roles/{id}/group-assignments".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=role_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_user_roles(
    context: ToolContext,
    user_id: Annotated[
        str, "The unique name or identifier of the user for whom roles are being retrieved."
    ],
    user_type: Annotated[
        str, "Specify the user type: 'oidc' for OpenID Connect or 'db' for database."
    ],
    include_detailed_role_information: Annotated[
        bool | None, "Set to true to include detailed role information like assigned permissions."
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRolesForUser'."]:
    """Retrieve all roles assigned to a specific user.

    Use this tool to get a list of all roles for a specified user, identified by their ID and user type (`db` or `oidc`)."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/users/{id}/roles/{userType}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            id=user_id,
            userType=user_type,
        ),
        method="GET",
        params=remove_none_values({"includeFullRoles": include_detailed_role_information}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def assign_roles_to_user(
    context: ToolContext,
    user_name: Annotated[str, "The name or identifier of the user to assign roles to."],
    assigned_roles: Annotated[
        list[str] | None,
        "List of roles to assign to the specified user. Each role should be a string.",
    ] = None,
    user_type: Annotated[
        str | None,
        "Specify the user type. Choose 'db' for Weaviate managed users or 'oidc' for users managed by an external OIDC provider.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'assignRoleToUser'."]:
    """Assign roles to a user in the system.

    Use this tool to assign one or more roles to a specific user, allowing flexibility in user permissions and access control."""  # noqa: E501
    request_data: Any = {"roles": assigned_roles, "userType": user_type}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/users/{id}/assign".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=user_name
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def revoke_user_role(
    context: ToolContext,
    user_id: Annotated[
        str, "The unique identifier or name of the user from whom roles will be revoked."
    ],
    roles_to_revoke: Annotated[
        list[str] | None,
        "A list of roles to be removed from the specified user. Provide each role as a string in the array.",  # noqa: E501
    ] = None,
    user_type: Annotated[
        str | None,
        "Specify the user type: `db` for Weaviate-managed or `oidc` for external OIDC-managed users.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'revokeRoleFromUser'."]:
    """Remove roles from a specified user in the system.

    Use this tool to revoke one or more roles assigned to a user by specifying their user ID. It's useful for updating user permissions in the system."""  # noqa: E501
    request_data: Any = {"roles": roles_to_revoke, "userType": user_type}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/users/{id}/revoke".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=user_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def assign_role_to_group(
    context: ToolContext,
    group_name: Annotated[str, "The name of the group to which roles will be assigned."],
    group_type: Annotated[
        str | None,
        "Indicate if the group contains OIDC or database users. Choose 'oidc' for OIDC users.",
    ] = None,
    roles_to_assign: Annotated[
        list[str] | None, "A list of roles to assign to a specified group. Each role is a string."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'assignRoleToGroup'."]:
    """Assign roles to a specific group.

    Use this tool to assign one or more roles to a designated group, identified by its ID."""
    request_data: Any = {"roles": roles_to_assign, "groupType": group_type}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/groups/{id}/assign".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=group_name
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def revoke_role_from_group(
    context: ToolContext,
    group_name: Annotated[str, "The name of the group from which roles will be revoked."],
    group_type: Annotated[
        str | None, "Specifies whether the group contains OIDC or database users."
    ] = None,
    roles_to_revoke: Annotated[
        list[str] | None, "An array of role names to revoke from the specified group."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'revokeRoleFromGroup'."]:
    """Revoke roles from a specified group to manage permissions.

    This tool allows the revocation of roles from a specified group, which is useful for managing and updating group permissions within a system using Weaviate."""  # noqa: E501
    request_data: Any = {"roles": roles_to_revoke, "groupType": group_type}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/groups/{id}/revoke".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=group_name
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_roles_for_group(
    context: ToolContext,
    group_name: Annotated[str, "The unique name of the group to retrieve roles for."],
    group_type: Annotated[str, "Specifies the type of the group, either 'db' or 'oidc'."],
    include_full_role_definitions: Annotated[
        bool | None,
        "Include full role definitions with all permissions if true; return only role names if false.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRolesForGroup'."]:
    """Retrieve roles assigned to a specific group.

    Fetches all roles associated with a group identified by its name and type (options: `db` or `oidc`)."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/groups/{id}/roles/{groupType}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            id=group_name,
            groupType=group_type,
        ),
        method="GET",
        params=remove_none_values({"includeFullRoles": include_full_role_definitions}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def retrieve_group_names(
    context: ToolContext,
    group_type: Annotated[str, "Specifies the group type to retrieve, either 'oidc' or 'db'."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getGroups'."]:
    """Retrieve available group names for a specified type.

    This tool retrieves a list of all available group names for a specified group type, either 'oidc' or 'db'. Use it when you need to list groups of a certain type."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/authz/groups/{groupType}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), groupType=group_type
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def list_data_objects(
    context: ToolContext,
    collection_name: Annotated[
        str | None,
        "Specifies the collection name to query objects from. If not provided, no objects will be returned.",  # noqa: E501
    ] = None,
    include_additional_information: Annotated[
        str | None,
        "Include additional information types such as `classification`, `vector`, or `interpretation`.",  # noqa: E501
    ] = None,
    maximum_items_per_page: Annotated[
        int | None,
        "The maximum number of items to be returned per page. The default is 25 unless set otherwise as an environment variable.",  # noqa: E501
    ] = None,
    query_start_index: Annotated[
        int | None,
        "The starting index for the result window. Retrieves `offset+limit` results and returns `limit` results from this index onward. Cannot be used with `after`. Should be used with `limit`.",  # noqa: E501
    ] = 0,
    sort_order: Annotated[
        str | None,
        "Specify how to order the data within the sorted field(s). Use 'asc' for ascending and 'desc' for descending. Should match the order of fields used in `sort`. Multiple values should be separated by commas.",  # noqa: E501
    ] = None,
    sort_properties: Annotated[
        str | None, "Names of properties to sort by, e.g., 'city' or 'country,city'."
    ] = None,
    tenant_identifier: Annotated[
        str | None, "Specifies the tenant for requests targeting a multi-tenant collection (class)."
    ] = None,
    threshold_uuid_after: Annotated[
        str | None,
        "A UUID to retrieve objects after, excluding this object. Use with `class` and `limit`. Leave empty for the start.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.list'."]:
    """Retrieve a list of data objects from a specified collection.

    Use this tool to get a list of data objects from a particular collection by providing the collection name parameter. The objects are returned in reverse order of creation."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/objects".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({
            "after": threshold_uuid_after,
            "offset": query_start_index,
            "limit": maximum_items_per_page,
            "include": include_additional_information,
            "sort": sort_properties,
            "order": sort_order,
            "class": collection_name,
            "tenant": tenant_identifier,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def create_object_in_weaviate(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    consistency_level_replica_acknowledgement: Annotated[
        str | None,
        "Specifies the number of replicas that must confirm the request for it to be successful.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.create'."]:
    """Create a new data object in Weaviate.

    This tool creates a new data object in Weaviate, ensuring that the object's metadata and schema values are validated. It's useful for adding new data objects when there is no existing object with the same ID.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEOBJECTINWEAVIATE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEOBJECTINWEAVIATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEOBJECTINWEAVIATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/objects".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEOBJECTINWEAVIATE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"consistency_level": consistency_level_replica_acknowledgement}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_data_object(
    context: ToolContext,
    collection_name: Annotated[str, "Name of the collection (class) the object belongs to."],
    object_uuid: Annotated[
        str, "Unique UUID of the object to be retrieved from the specified collection."
    ],
    include_additional_information: Annotated[
        str | None,
        "Specify additional info to include: `classification`, `vector`, or `interpretation`.",
    ] = None,
    required_replica_acknowledgment: Annotated[
        str | None,
        "Specifies how many replicas must confirm a request for success. Relates to consistency level.",  # noqa: E501
    ] = None,
    target_node_name: Annotated[
        str | None, "Specify the target node to fulfill the request."
    ] = None,
    tenant_identifier: Annotated[
        str | None, "Specify the tenant for a multi-tenant collection request."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.class.get'."]:
    """Retrieve a data object using collection name and UUID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/objects/{className}/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            id=object_uuid,
        ),
        method="GET",
        params=remove_none_values({
            "include": include_additional_information,
            "consistency_level": required_replica_acknowledgment,
            "node_name": target_node_name,
            "tenant": tenant_identifier,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def update_object_properties(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name: Annotated[
        str | None,
        "The name of the class (collection) to which the object belongs.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    object_uuid: Annotated[
        str | None,
        "Unique UUID of the object to be replaced.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    required_replica_acknowledgement: Annotated[
        str | None,
        "Specifies how many replicas must acknowledge a request for it to be successful.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.class.put'."]:
    """Replace properties of a data object using its class and ID.

    This tool updates the properties of an existing object identified by its class name and UUID. The request must include the complete object definition with new values to replace the existing ones.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEOBJECTPROPERTIES_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name:
        missing_params.append(("collection_name", "path"))
    if not object_uuid:
        missing_params.append(("object_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEOBJECTPROPERTIES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEOBJECTPROPERTIES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/objects/{className}/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            id=object_uuid,
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEOBJECTPROPERTIES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"consistency_level": required_replica_acknowledgement}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_data_object(
    context: ToolContext,
    collection_name: Annotated[str, "Name of the collection (class) the object belongs to."],
    object_uuid: Annotated[str, "Unique UUID of the object to be deleted from the collection."],
    replica_acknowledgment_level: Annotated[
        str | None, "Specifies the number of replicas needed to confirm request success."
    ] = None,
    tenant_identifier: Annotated[
        str | None, "Specifies the tenant when targeting a multi-tenant collection (class)."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.class.delete'."]:
    """Delete a data object from a specified collection using its UUID.

    Use this tool to remove a specific data object from a Weaviate collection, identified by its `className` and `id` (UUID). Useful for managing data within a Weaviate instance."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/objects/{className}/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            id=object_uuid,
        ),
        method="DELETE",
        params=remove_none_values({
            "consistency_level": replica_acknowledgment_level,
            "tenant": tenant_identifier,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def update_data_object(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name: Annotated[
        str | None,
        "Specifies the name of the collection or class the object belongs to. This identifies where the object is stored.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    object_uuid: Annotated[
        str | None,
        "Unique UUID of the object to be patched within the specified collection.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    consistency_level: Annotated[
        str | None,
        "Specify the number of replicas that must acknowledge the request for success.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.class.patch'."]:
    """Update specific properties of a data object.

    This tool updates properties of an existing object in a specified collection using JSON merge patch semantics. The object is identified by its collection name and UUID. Use this to modify selected fields of a data object while ensuring validation and timestamp updates.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEDATAOBJECT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name:
        missing_params.append(("collection_name", "path"))
    if not object_uuid:
        missing_params.append(("object_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEDATAOBJECT_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEDATAOBJECT_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/objects/{className}/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            id=object_uuid,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEDATAOBJECT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"consistency_level": consistency_level}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def replace_object_references(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name: Annotated[
        str | None,
        "Name of the collection the source object belongs to.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    source_object_uuid: Annotated[
        str | None,
        "Unique UUID of the source object to identify it for reference replacement.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    reference_property_name: Annotated[
        str | None,
        "Unique name of the reference property for the source object to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    consistency_level: Annotated[
        str | None,
        "Defines the required number of replica acknowledgements for request success.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    tenant_identifier: Annotated[
        str | None,
        "Specifies the tenant in a request targeting a multi-tenant collection (class).  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.class.references.put'."]:
    """Replace existing references for an object in Weaviate.

    Use this tool to replace all current references of a specific property for an object in Weaviate. Specify the object's collection name, UUID, and the reference property to update. Useful for updating linked data within a dataset.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "REPLACEOBJECTREFERENCES_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name:
        missing_params.append(("collection_name", "path"))
    if not source_object_uuid:
        missing_params.append(("source_object_uuid", "path"))
    if not reference_property_name:
        missing_params.append(("reference_property_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REPLACEOBJECTREFERENCES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REPLACEOBJECTREFERENCES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/objects/{className}/{id}/references/{propertyName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            id=source_object_uuid,
            propertyName=reference_property_name,
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REPLACEOBJECTREFERENCES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "consistency_level": consistency_level,
            "tenant": tenant_identifier,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def add_reference_to_object(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    source_class_name: Annotated[
        str | None,
        "Name of the collection (class) the source object belongs to.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    source_object_uuid: Annotated[
        str | None,
        "Unique UUID identifying the source object to which the reference will be added.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    reference_property_name: Annotated[
        str | None,
        "Unique name of the reference property of the source object to which the reference will be added.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    required_consistency_level: Annotated[
        str | None,
        "Specifies the number of replicas that must acknowledge a request for it to be successful.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    tenant_identifier: Annotated[
        str | None,
        "Specifies the tenant for multi-tenant collection requests in a Weaviate class.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.class.references.create'."]:
    """Add a reference to an object's property.

    This tool adds a new reference to a specified property of a data object. The object is identified by its class and UUID. Use this to link related objects within a dataset.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDREFERENCETOOBJECT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not source_class_name:
        missing_params.append(("source_class_name", "path"))
    if not source_object_uuid:
        missing_params.append(("source_object_uuid", "path"))
    if not reference_property_name:
        missing_params.append(("reference_property_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDREFERENCETOOBJECT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDREFERENCETOOBJECT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/objects/{className}/{id}/references/{propertyName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=source_class_name,
            id=source_object_uuid,
            propertyName=reference_property_name,
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDREFERENCETOOBJECT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "consistency_level": required_consistency_level,
            "tenant": tenant_identifier,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_reference_from_object(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name: Annotated[
        str | None,
        "Name of the collection (class) the source object belongs to. This identifies where the object is located.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    source_object_uuid: Annotated[
        str | None,
        "Unique UUID of the source object from which the reference will be deleted.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    reference_property_name: Annotated[
        str | None,
        "Unique name of the reference property of the source object from which to delete the reference.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    required_consistency_level: Annotated[
        str | None,
        "Specifies how many replicas must acknowledge the request for success.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    tenant_identifier: Annotated[
        str | None,
        "Specifies the tenant in a request targeting a multi-tenant collection (class).  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.class.references.delete'."]:
    """Delete a specific reference from an object's property.

    Use this tool to remove a reference from a specific property of a data object in a collection, identified by its class name and UUID. Call this tool when you need to delete a reference from an object's property.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "DELETEREFERENCEFROMOBJECT_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name:
        missing_params.append(("collection_name", "path"))
    if not source_object_uuid:
        missing_params.append(("source_object_uuid", "path"))
    if not reference_property_name:
        missing_params.append(("reference_property_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEREFERENCEFROMOBJECT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEREFERENCEFROMOBJECT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/objects/{className}/{id}/references/{propertyName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            id=source_object_uuid,
            propertyName=reference_property_name,
        ),
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["DELETEREFERENCEFROMOBJECT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "consistency_level": required_consistency_level,
            "tenant": tenant_identifier,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def validate_data_object_structure(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'objects.validate'."]:
    """Validate a data object's structure against the schema.

    Use this tool to check if a data object conforms to the specified collection schema and metadata rules without storing it. A successful validation returns a confirmation, while errors provide detailed feedback.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "VALIDATEDATAOBJECTSTRUCTURE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEDATAOBJECTSTRUCTURE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEDATAOBJECTSTRUCTURE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/objects/validate".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["VALIDATEDATAOBJECTSTRUCTURE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def batch_register_objects(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    consistency_level: Annotated[
        str | None,
        "Specifies how many replicas must confirm a request for it to be successful.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'batch.objects.create'."]:
    """Register multiple data objects in a single request.

    This tool registers multiple data objects in one request for efficiency. It validates metadata and schema values for each object. The operation is idempotent, meaning if an object with a given UUID already exists, it will be overwritten.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BATCHREGISTEROBJECTS_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["BATCHREGISTEROBJECTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["BATCHREGISTEROBJECTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/batch/objects".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BATCHREGISTEROBJECTS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"consistency_level": consistency_level}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_multiple_objects(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    replica_acknowledgement_level: Annotated[
        str | None,
        "Specifies the number of replicas that must confirm the request for it to be deemed successful.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    target_tenant: Annotated[
        str | None,
        "Specifies the tenant when targeting a multi-tenant collection. Use the tenant's unique identifier.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'batch.objects.delete'."]:
    """Deletes multiple data objects using specified filter criteria.

    This tool removes data objects based on a filter. Use it when you need to delete multiple objects that match specific criteria within the limit. The deletion is performed in the order objects match the filter. If you need to delete beyond the limit, repeat the process until finished.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "DELETEMULTIPLEOBJECTS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEMULTIPLEOBJECTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEMULTIPLEOBJECTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request(
        url="{weaviate_server_url}/v1/batch/objects".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="DELETE",
        params=remove_none_values({
            "consistency_level": replica_acknowledgement_level,
            "tenant": target_tenant,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def batch_create_references(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    consistency_level: Annotated[
        str | None,
        "Specifies the number of replicas needed to acknowledge a request for it to be deemed successful.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'batch.references.create'."]:
    """Batch create cross-references between items in a collection.

    Use this tool to efficiently establish multiple cross-references between items in a collection at once. Ideal for handling large datasets where establishing individual links manually would be inefficient.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "BATCHCREATEREFERENCES_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["BATCHCREATEREFERENCES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["BATCHCREATEREFERENCES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/batch/references".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BATCHCREATEREFERENCES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"consistency_level": consistency_level}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def execute_graphql_query(
    context: ToolContext,
    graphql_query: Annotated[
        str | None, "A GraphQL query string to execute on Weaviate, following GraphQL syntax."
    ] = None,
    operation_name: Annotated[
        str | None, "The name of the operation if multiple operations exist in the GraphQL query."
    ] = None,
    query_variables: Annotated[
        dict[str, str] | None,
        "Additional JSON variables for the GraphQL query used to provide external values for operations.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'graphql.post'."]:
    """Executes a GraphQL query on Weaviate.

    Use this tool to perform data queries and exploration on the Weaviate platform by executing a provided GraphQL query."""  # noqa: E501
    request_data: Any = {
        "operationName": operation_name,
        "query": graphql_query,
        "variables": query_variables,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/graphql".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def execute_graphql_batch_queries(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'graphql.batch'."]:
    """Execute multiple GraphQL queries in a single request.

    Executes multiple GraphQL queries sent in a single network request for efficiency. Ideal for performing batch queries in Weaviate.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "EXECUTEGRAPHQLBATCHQUERIES_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["EXECUTEGRAPHQLBATCHQUERIES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["EXECUTEGRAPHQLBATCHQUERIES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/graphql/batch".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["EXECUTEGRAPHQLBATCHQUERIES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_weaviate_instance_meta(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'meta.get'."]:
    """Get meta-information about a Weaviate instance.

    Use this tool to obtain details such as version, modules, and network hostname of a running Weaviate instance. Useful for monitoring, compatibility checks, or communication between instances."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/meta".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def retrieve_database_schema(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.dump'."]:
    """Retrieve definitions of all classes in the database schema.

    Use this tool to obtain the definitions of all collections (classes) currently present in the database schema. It provides a comprehensive overview of the database structure."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "consistency": "True",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def create_schema_object(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.objects.create'."]:
    """Create a new collection (class) in Weaviate.

    This tool is used to define and create a new collection (class) in Weaviate. It provides explicit control over the schema definition, which is essential when `AutoSchema` is not relied upon. Use this tool to ensure accurate schema setup in a Weaviate instance.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESCHEMAOBJECT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCHEMAOBJECT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCHEMAOBJECT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request(
        url="{weaviate_server_url}/v1/schema".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def retrieve_collection_schema(
    context: ToolContext,
    collection_name: Annotated[str, "The name of the collection to retrieve the schema for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.objects.get'."]:
    """Retrieve the schema of a specified collection.

    Fetches the definition of a specific collection by `className`, detailing its properties, configuration, and vectorizer settings."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), className=collection_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "consistency": "True",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def update_collection_settings(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name_to_update: Annotated[
        str | None,
        "The name of the collection to be updated. Specify the collection's class name.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.objects.update'."]:
    """Update settings of an existing collection.

    Use this tool to update the configuration settings of an existing collection by specifying the className and the new settings. This tool is useful when you need to change mutable settings of a collection but not its name or properties.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATECOLLECTIONSETTINGS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name_to_update:
        missing_params.append(("collection_name_to_update", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECOLLECTIONSETTINGS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECOLLECTIONSETTINGS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name_to_update,
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_schema_collection(
    context: ToolContext,
    collection_name_to_delete: Annotated[
        str, "The name of the collection (class) that will be permanently deleted from the schema."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.objects.delete'."]:
    """Permanently delete a collection from the schema.

    Call this tool to remove a collection definition and all its data objects from the schema permanently."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name_to_delete,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def add_property_to_collection(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name: Annotated[
        str | None,
        "The name of the collection (class) to which the property will be added.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.objects.properties.add'."]:
    """Add a new property to an existing collection schema.

    Use this tool to add a new property definition to a specified collection (className) within the schema. It is called when a new property needs to be incorporated into an existing data structure.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "ADDPROPERTYTOCOLLECTION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name:
        missing_params.append(("collection_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDPROPERTYTOCOLLECTION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDPROPERTYTOCOLLECTION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}/properties".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), className=collection_name
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_collection_shard_status(
    context: ToolContext,
    collection_name: Annotated[str, "The name of the collection (class) whose shards to query."],
    tenant_name: Annotated[
        str | None,
        "The name of the tenant for retrieving shard statuses, applicable only for multi-tenant collections.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.objects.shards.get'."]:
    """Retrieves status of shards for a specified collection.

    Use this tool to get the status of all shards associated with a specific collection (`className`) in Weaviate. For multi-tenant collections, specify the `tenant` query parameter to retrieve shard status for a particular tenant."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}/shards".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), className=collection_name
        ),
        method="GET",
        params=remove_none_values({"tenant": tenant_name}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def update_shard_status(
    context: ToolContext,
    collection_name: Annotated[
        str, "The name of the collection or class containing the shard to be updated."
    ],
    shard_name: Annotated[str, "The specific name of the shard to update in the collection."],
    shard_status: Annotated[
        str | None, "The status to set for the shard, such as 'READY' or 'READONLY'."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'schema.objects.shards.update'."]:
    """Update the status of a specific shard in a collection.

    Use this tool to update the status of a specific shard within a Weaviate collection. It is typically used to change the shard's status to `READY` or `READONLY` after addressing issues that affected its operation."""  # noqa: E501
    request_data: Any = {"status": shard_status}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}/shards/{shardName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            shardName=shard_name,
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_collection_tenants(
    context: ToolContext,
    collection_name: Annotated[str, "The name of the collection (class) whose tenants to list."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'tenants.get'."]:
    """Retrieve tenants for a specified collection.

    This tool retrieves a list of all tenants associated with a given collection. Call this tool to obtain tenant information for a specified collection in the Weaviate service."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}/tenants".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), className=collection_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "consistency": "True",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def update_tenant_status(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name: Annotated[
        str | None,
        "The name of the collection (class) containing the tenants whose status is to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'tenants.update'."]:
    """Update the activity status of specified tenants.

    This tool updates the activity status, such as `ACTIVE` or `INACTIVE`, of one or more specified tenants within a collection (specified by `className`). Use this tool to manage tenant statuses efficiently within your system.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATETENANTSTATUS_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name:
        missing_params.append(("collection_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATETENANTSTATUS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATETENANTSTATUS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/schema/{className}/tenants".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), className=collection_name
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETENANTSTATUS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def create_tenants(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    collection_name: Annotated[
        str | None,
        "The name of the multi-tenant enabled collection for creating tenants.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'tenants.create'."]:
    """Create new tenants in a specified collection.

    Use this tool to create one or more new tenants for a specified collection when multi-tenancy is enabled. Useful for managing tenants within a Weaviate database.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETENANTS_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not collection_name:
        missing_params.append(("collection_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATETENANTS_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATETENANTS_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/schema/{className}/tenants".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), className=collection_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETENANTS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_tenants(
    context: ToolContext,
    collection_name: Annotated[str, "The name of the collection from which to delete tenants."],
    tenant_names_to_delete: Annotated[
        list[str], "An array of tenant names to permanently delete from the specified collection."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'tenants.delete'."]:
    """Permanently delete specified tenants from a collection.

    Use this tool to delete one or more tenants from a specified collection in Weaviate. This action is irreversible and will remove all data related to the specified tenants."""  # noqa: E501
    request_data: Any = tenant_names_to_delete
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}/tenants".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), className=collection_name
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_tenant_details(
    context: ToolContext,
    collection_name: Annotated[str, "The name of the collection (class) that contains the tenant."],
    tenant_name: Annotated[
        str, "The name of the tenant to retrieve details about within the specified collection."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'tenants.get.one'."]:
    """Retrieve details about a specific tenant's status.

    Call this tool to get information about a specific tenant within a specified collection, including their current activity status."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/schema/{className}/tenants/{tenantName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_name,
            tenantName=tenant_name,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "consistency": "True",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def retrieve_aliases(
    context: ToolContext,
    filter_by_collection_name: Annotated[
        str | None,
        "Optional filter to retrieve aliases for a specific collection (class) only. If not provided, returns all aliases.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'aliases.get'."]:
    """Retrieve all aliases from the system.

    Fetches a list of all aliases, with an option to filter by collection (class) name for specific aliases."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/aliases".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({"class": filter_by_collection_name}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def create_alias_mapping(
    context: ToolContext,
    alias_name: Annotated[
        str | None,
        "The unique name of the alias, serving as an alternative identifier for the specified collection.",  # noqa: E501
    ] = None,
    collection_class_name: Annotated[
        str | None, "The name of the collection (class) to which the alias is mapped."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'aliases.create'."]:
    """Create a new alias for a collection in Weaviate.

    This tool creates a new alias mapping between an alias name and a collection (class) in Weaviate. Use it to set up alternative names for accessing collections."""  # noqa: E501
    request_data: Any = {"alias": alias_name, "class": collection_class_name}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/aliases".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def retrieve_alias_details(
    context: ToolContext,
    alias_name: Annotated[
        str, "The name of the alias to retrieve details for, including its associated collection."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'aliases.get.alias'."]:
    """Retrieve details about a specific alias by its name.

    Use this tool to obtain information about an alias, such as the collection (class) it points to, by specifying the alias name."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/aliases/{aliasName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), aliasName=alias_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def update_collection_alias(
    context: ToolContext,
    alias_name: Annotated[
        str, "The name of the existing alias that you want to update to point to a new collection."
    ],
    new_collection_name: Annotated[
        str | None, "Specify the new collection (class) for the alias to point to."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'aliases.update'."]:
    """Redirect an alias to a different collection.

    Use this tool to update an existing alias so it points to a new collection. This allows alias redirection without changing its name."""  # noqa: E501
    request_data: Any = {"class": new_collection_name}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/aliases/{aliasName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), aliasName=alias_name
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def delete_alias(
    context: ToolContext,
    alias_name: Annotated[
        str,
        "The name of the alias to be deleted. This identifier specifies which alias mapping to remove from the system.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'aliases.delete'."]:
    """Delete an existing alias from the system.

    This tool removes an alias from the system without affecting the underlying collection. It should be called when an alias is no longer needed."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/aliases/{aliasName}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), aliasName=alias_name
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def list_backups(
    context: ToolContext,
    backend_storage_system: Annotated[
        str,
        "Specifies the backend storage system to list backups from (e.g., `filesystem`, `gcs`, `s3`, `azure`).",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'backups.list'."]:
    """Retrieve all backup IDs and their statuses.

    Use this tool to get a list of all created backup IDs along with their current statuses. Useful for monitoring or managing backup data."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/backups/{backend}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            backend=backend_storage_system,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def create_backup(
    context: ToolContext,
    backend_storage_system: Annotated[
        str,
        "Specifies the backend storage system where the backup will be stored, such as `filesystem`, `gcs`, `s3`, or `azure`.",  # noqa: E501
    ],
    backup_chunk_size: Annotated[
        int | None,
        "Set the chunk size for the backup with a minimum of 2MB, default of 128MB, and a maximum of 512MB.",  # noqa: E501
    ] = None,
    backup_id: Annotated[
        str | None,
        "The ID of the backup. Must be URL-safe, using only lowercase, numbers, underscore, and minus characters.",  # noqa: E501
    ] = None,
    bucket_name: Annotated[
        str | None, "Name of the bucket, container, or volume where the backup will be stored."
    ] = None,
    bucket_path: Annotated[
        str | None,
        "Specifies the path or key within the storage bucket. This helps in locating where the backup will be stored within the bucket.",  # noqa: E501
    ] = None,
    collections_to_include_in_backup: Annotated[
        list[str] | None,
        "List of collections to include in the backup. If not set, all collections are included. Cannot be used with `collections_to_exclude_in_backup`.",  # noqa: E501
    ] = None,
    compression_level: Annotated[
        str | None,
        "Specifies the compression level for the backup: DefaultCompression, BestSpeed, or BestCompression.",  # noqa: E501
    ] = "DefaultCompression",
    cpu_utilization_percentage: Annotated[
        int | None,
        "Sets desired CPU core utilization, ranging from 1% to 80%, during backup creation.",
    ] = None,
    exclude_collections: Annotated[
        list[str] | None,
        "List of collections to exclude from the backup. Overrides include if both are set.",
    ] = None,
    storage_endpoint_name: Annotated[
        str | None,
        "Name of the storage endpoint, such as s3.amazonaws.com, where the backup will be stored.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'backups.create'."]:
    """Initiates backup creation for specified collections.

    This tool starts the process of creating a backup for the specified collections on a designated backend storage using Weaviate. The backup is compressed using gzip by default, and the system remains operational during the backup process. Use this tool when you need to ensure data is safely backed up without interrupting service."""  # noqa: E501
    request_data: Any = {
        "id": backup_id,
        "config": {
            "Endpoint": storage_endpoint_name,
            "Bucket": bucket_name,
            "Path": bucket_path,
            "CPUPercentage": cpu_utilization_percentage,
            "ChunkSize": backup_chunk_size,
            "CompressionLevel": compression_level,
        },
        "include": collections_to_include_in_backup,
        "exclude": exclude_collections,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/backups/{backend}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            backend=backend_storage_system,
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def check_backup_status(
    context: ToolContext,
    backup_backend_system: Annotated[
        str,
        "Specifies the backend storage system where the backup resides, such as 'filesystem', 'gcs', 's3', or 'azure'.",  # noqa: E501
    ],
    backup_identifier: Annotated[
        str,
        "The unique identifier of the backup. Use only lowercase, numbers, underscores, and minus characters.",  # noqa: E501
    ],
    backup_storage_path: Annotated[
        str | None,
        "Specifies the path within the bucket/container/volume if the backup is not at the root. Optional.",  # noqa: E501
    ] = None,
    bucket_name: Annotated[
        str | None, "Specifies the bucket, container, or volume name if required by the backend."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'backups.create.status'."]:
    """Get the current status of a backup creation process.

    Use this tool to manually check the status of a backup creation process by its ID on the specified backend. Ideal for when waiting for completion is disabled or not suitable."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/backups/{backend}/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            backend=backup_backend_system,
            id=backup_identifier,
        ),
        method="GET",
        params=remove_none_values({"bucket": bucket_name, "path": backup_storage_path}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def cancel_backup(
    context: ToolContext,
    backup_id: Annotated[
        str,
        "The unique ID of the backup to delete. Must be URL-safe, using lowercase, numbers, underscores, and minus characters.",  # noqa: E501
    ],
    storage_backend_system: Annotated[
        str,
        "Specifies the backend storage system where the backup resides (e.g., 'filesystem', 'gcs', 's3', 'azure').",  # noqa: E501
    ],
    backup_subpath: Annotated[
        str | None,
        "Specifies the optional path within the storage if the backup is not at the root.",
    ] = None,
    specified_bucket_name: Annotated[
        str | None,
        "Specifies the bucket, container, or volume name if required by the backend storage system. Optional parameter.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'backups.cancel'."]:
    """Cancel a backup by its ID from a specified backend storage.

    Use this tool to delete a backup using its unique ID from a specified backend storage location, ensuring the backup is no longer retrievable."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/backups/{backend}/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            backend=storage_backend_system,
            id=backup_id,
        ),
        method="DELETE",
        params=remove_none_values({"bucket": specified_bucket_name, "path": backup_subpath}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def check_backup_restore_status(
    context: ToolContext,
    backend_storage_system: Annotated[
        str,
        "Specifies the backend storage system where the backup resides, such as 'filesystem', 'gcs', 's3', or 'azure'.",  # noqa: E501
    ],
    backup_id: Annotated[
        str,
        "The URL-safe unique identifier of the backup being restored. Use lowercase, numbers, underscores, and minus characters only.",  # noqa: E501
    ],
    backup_bucket_name: Annotated[
        str | None, "Specifies the bucket, container, or volume name if required by the backend."
    ] = None,
    bucket_path: Annotated[
        str | None, "Specifies the path within the bucket, optional based on backend requirements."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'backups.restore.status'."]:
    """Check the status of a backup restoration process.

    Use this tool to manually check the status of a specific backup restoration on a given backend, using the backup ID. Ideal when automatic polling is disabled."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/backups/{backend}/{id}/restore".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            backend=backend_storage_system,
            id=backup_id,
        ),
        method="GET",
        params=remove_none_values({"bucket": backup_bucket_name, "path": bucket_path}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def restore_backup(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    backup_backend_storage: Annotated[
        str | None,
        "Specifies the backend storage system where the backup resides, such as `filesystem`, `gcs`, `s3`, or `azure`.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    backup_identifier: Annotated[
        str | None,
        "The unique identifier for the backup to restore. It must be URL-safe and compatible with filesystem paths, using only lowercase letters, numbers, underscores, and minus characters.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'backups.restore'."]:
    """Restore collections from a specified backup.

    Initiates the restoration of collections from a backup stored on a designated backend. Ensure the target cluster has identical node configurations as the source cluster, and that the collections do not already exist on the target.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["RESTOREBACKUP_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not backup_backend_storage:
        missing_params.append(("backup_backend_storage", "path"))
    if not backup_identifier:
        missing_params.append(("backup_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["RESTOREBACKUP_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["RESTOREBACKUP_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="{weaviate_server_url}/v1/backups/{backend}/{id}/restore".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            backend=backup_backend_storage,
            id=backup_identifier,
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["RESTOREBACKUP_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_weaviate_cluster_statistics(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'cluster.get.statistics'."]:
    """Get Weaviate cluster Raft protocol statistics.

    Use this tool to retrieve detailed statistics about the internal Raft consensus protocol state for a Weaviate cluster. This can help monitor the cluster's status and performance."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/cluster/statistics".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_cluster_node_status(
    context: ToolContext,
    output_verbosity: Annotated[
        str | None,
        "Controls the verbosity of the output for node status information. Accepted values: `minimal`, `verbose`. Defaults to `minimal`.",  # noqa: E501
    ] = "minimal",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'nodes.get'."]:
    """Retrieve status information about all nodes in a cluster.

    Fetches status details for all nodes in the cluster, with adjustable detail level using the output parameter."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/nodes".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({"output": output_verbosity}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_shard_host_nodes_status(
    context: ToolContext,
    collection_class_name: Annotated[
        str, "The name of the collection (class) for which to retrieve node status."
    ],
    output_verbosity: Annotated[
        str | None,
        "Set the verbosity of the output. Possible values: 'minimal', 'verbose'. Defaults to 'minimal'.",  # noqa: E501
    ] = "minimal",
    shard_name: Annotated[
        str | None,
        "Specifies the name of the shard for which to retrieve node status information. This helps to focus the request on specific shards within a collection.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'nodes.get.class'."]:
    """Retrieve status of nodes hosting shards for a collection.

    This tool fetches status information for nodes that host shards of a specified collection. Use it to monitor or diagnose the nodes in a distributed database cluster, customizing the level of detail with the `output` query parameter."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/nodes/{className}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"),
            className=collection_class_name,
        ),
        method="GET",
        params=remove_none_values({"shardName": shard_name, "output": output_verbosity}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def list_distributed_tasks(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'distributedTasks.get'."]:
    """Retrieve all distributed tasks in the cluster.

    Use this tool to get an overview of all distributed tasks currently active or available within the cluster."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/tasks".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def initiate_classification_task(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'classifications.post'."]:
    """Initiate a classification task in the background.

    Use this tool to start a background classification task with specific parameters. This will initiate the process, and you can monitor its status and retrieve results using the GET /classifications/{id} endpoint.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "INITIATECLASSIFICATIONTASK_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["INITIATECLASSIFICATIONTASK_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["INITIATECLASSIFICATIONTASK_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request(
        url="{weaviate_server_url}/v1/classifications/".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            ),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["WEAVIATE_API_KEY", "WEAVIATE_SERVER_URL"])
async def get_classification_status(
    context: ToolContext,
    classification_task_id: Annotated[
        str,
        "The unique identifier (UUID) of the classification task to retrieve its status and results.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'classifications.get'."]:
    """Retrieve status and results of a classification task.

    Use this tool to obtain the status, metadata, and results of an ongoing or completed classification task by providing its unique ID."""  # noqa: E501
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="{weaviate_server_url}/v1/classifications/{id}".format(
            weaviate_server_url=context.get_secret("WEAVIATE_SERVER_URL"), id=classification_task_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("WEAVIATE_API_KEY")
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}
