diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index dc42b6b29a..0516704b1c 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -2588,6 +2588,517 @@ components: $ref: "#/components/schemas/JSONAPIErrorResponse" description: The server cannot process the request because it contains invalid data. schemas: + AIGuardAction: + description: The recommended action for the evaluated conversation. + enum: + - ALLOW + - DENY + - ABORT + example: ALLOW + type: string + x-enum-varnames: + - ALLOW + - DENY + - ABORT + AIGuardCodingAgent: + description: |- + The coding agent that produced the conversation. By default, a non-empty + value selects the coding-agent evaluator profile, while an omitted or empty + value selects the default profile. Known identifiers include `claude-code`, + `codex`, `cursor`, and `github-copilot`; other identifiers are accepted. + The coding-agent profile assumes the caller can ask the user to confirm + Supply Chain Firewall warnings; the default profile assumes it cannot. + example: claude-code + type: string + AIGuardConfidenceThreshold: + additionalProperties: + description: The confidence threshold override for the threat tag named by the property. + example: 0.9 + format: double + type: number + description: |- + Request-level confidence threshold override. `flagging_global` is the global + threshold applied to every threat category. Any other property is a + per-category override keyed by an AI Guard threat tag; per-category values + take precedence over `flagging_global`, and categories without an entry fall + back to it. When this object omits flagging_global, the fallback is `0.5`. + Recognized category keys include policy-evasion, indirect-prompt-injection, + system-and-capability-discovery, credential-access, sensitive-data-collection, + privilege-escalation, malicious-execution, private-network-target, + destructive-or-resource-abuse, tool-abuse, poisoning-or-persistence, and + `data-exfiltration`. Unknown category keys are ignored. + example: + flagging_global: 0.6 + policy-evasion: 0.9 + properties: + flagging_global: + description: The global confidence threshold applied to categories without a specific override. + example: 0.6 + format: double + type: number + type: object + AIGuardConfidenceThresholdOverride: + description: |- + Request-only confidence threshold override. A number sets the threshold + for every threat category; an object supplies a global threshold and + optional per-category overrides. Omit this field to use the stored + sensitivity policy. This field is not returned in evaluation responses. + oneOf: + - $ref: "#/components/schemas/AIGuardGlobalConfidenceThreshold" + - $ref: "#/components/schemas/AIGuardConfidenceThreshold" + AIGuardContentPart: + description: |- + One part of multipart message content, following the OpenAI content part + format. For user, system, developer, and tool messages, a text part must + include non-empty `text`, and an image part must include `image_url` with + a non-empty `url`. These content-part checks do not apply to assistant messages. + properties: + image_url: + $ref: "#/components/schemas/AIGuardImageURL" + text: + description: The text content, required and non-empty when `type` is `text` in a user, system, developer, or tool message. + example: What is the weather like today? + type: string + type: + $ref: "#/components/schemas/AIGuardContentPartType" + required: + - type + type: object + AIGuardContentPartType: + description: The type of the content part. + enum: + - text + - image_url + example: text + type: string + x-enum-varnames: + - TEXT + - IMAGE_URL + AIGuardEvaluateAttributesResponse: + description: The evaluation result with the recommended action and detected threat information. + properties: + action: + $ref: "#/components/schemas/AIGuardAction" + global_prob: + description: The global threat probability, between `0` and `1`. Included only when computed. + example: 0.005218505859375 + format: double + type: number + is_blocking_enabled: + description: Whether blocking is enabled for the organization. + example: false + type: boolean + reason: + description: The explanation for the recommended action, for logging rather than user-facing display. + example: No rule match. + type: string + redaction_replacements: + description: The redacted replacements computed for the request. Included only when redaction applies. + items: + $ref: "#/components/schemas/AIGuardRedactionReplacement" + type: array + scfw_findings: + $ref: "#/components/schemas/AIGuardScfwFindings" + sds_findings: + description: The sensitive data findings from the Sensitive Data Scanner. Included only when findings exist. + items: + $ref: "#/components/schemas/AIGuardSdsFinding" + type: array + tag_probs: + additionalProperties: + description: The detection confidence for the threat tag named by the property. + example: 0.97 + format: double + type: number + description: The detection confidence for each threat tag, between `0` and `1`. May be null when the evaluation does not produce per-category scores. + example: + nullable: true + type: object + tags: + description: The threat tags detected in the conversation. + example: [] + items: + description: A detected threat tag. + example: destructive-or-resource-abuse + type: string + type: array + required: + - action + - reason + - tags + - tag_probs + - is_blocking_enabled + type: object + AIGuardEvaluateDataResponse: + description: The JSON:API evaluation resource. + properties: + attributes: + $ref: "#/components/schemas/AIGuardEvaluateAttributesResponse" + id: + description: The unique identifier of the evaluation. + example: a76a703c-324f-42ed-af33-9b17d65e28af + type: string + type: + $ref: "#/components/schemas/AIGuardEvaluationType" + required: + - id + - type + - attributes + type: object + AIGuardEvaluateRequest: + description: The request to evaluate a conversation for security threats. + example: + data: + attributes: + messages: + - content: Hello world! + role: user + meta: + confidence_threshold: 0.8 + type: evaluations + properties: + data: + $ref: "#/components/schemas/AIGuardEvaluateRequestData" + required: + - data + type: object + AIGuardEvaluateRequestData: + description: The JSON:API request envelope for an AI Guard evaluation. + properties: + attributes: + $ref: "#/components/schemas/AIGuardEvaluateRequestDataAttributes" + type: + $ref: "#/components/schemas/AIGuardEvaluationType" + required: + - type + - attributes + type: object + AIGuardEvaluateRequestDataAttributes: + description: The conversation to evaluate and its metadata. + properties: + messages: + description: The messages of the conversation to evaluate, in order. Must contain at least one message. + items: + $ref: "#/components/schemas/AIGuardMessage" + minItems: 1 + type: array + meta: + $ref: "#/components/schemas/AIGuardMeta" + required: + - messages + type: object + AIGuardEvaluateResponse: + description: The response containing the evaluation result. + properties: + data: + $ref: "#/components/schemas/AIGuardEvaluateDataResponse" + required: + - data + type: object + AIGuardEvaluationType: + description: |- + The JSON:API resource type for AI Guard evaluation requests and responses. + Its value is `evaluations` for the action endpoint `/api/v2/ai-guard/evaluate`. + enum: + - evaluations + example: evaluations + type: string + x-enum-varnames: + - EVALUATIONS + AIGuardGlobalConfidenceThreshold: + description: The global confidence threshold applied to every threat category. + example: 0.8 + format: double + type: number + AIGuardImageURL: + description: An image referenced by URL in a message content part. + properties: + url: + description: The image URL or a data URI with an `image/png`, `image/jpeg`, `image/gif`, or `image/webp` MIME type. + example: https://example.com/image.png + type: string + required: + - url + type: object + AIGuardMessage: + description: |- + A message in the conversation. Content is required for user, system, + developer, and tool messages, but may be omitted for assistant messages. + For those non-assistant roles, a plain content string may be empty, but a + multipart content array must contain at least one valid content part. + Only assistant messages may contain tool calls, and tool call entries must + not be null. Tool messages must include a non-empty `tool_call_id`; other + message roles must not include a non-empty `tool_call_id`. + properties: + content: + $ref: "#/components/schemas/AIGuardMessageContent" + role: + $ref: "#/components/schemas/AIGuardMessageRole" + tool_call_id: + description: The identifier of the tool call this message responds to, present on tool result messages. + example: call_1 + type: string + tool_calls: + description: The tool calls emitted by the assistant, present on assistant messages that call tools. + items: + $ref: "#/components/schemas/AIGuardToolCall" + type: array + required: + - role + type: object + AIGuardMessageContent: + description: The content of a message, either a plain string or a list of OpenAI-style content parts. + oneOf: + - $ref: "#/components/schemas/AIGuardMessageContentText" + - $ref: "#/components/schemas/AIGuardMessageContentParts" + AIGuardMessageContentParts: + description: The message content as a list of content parts. The list must be non-empty for user, system, developer, and tool messages. + items: + $ref: "#/components/schemas/AIGuardContentPart" + type: array + AIGuardMessageContentText: + description: The message content as a plain string. + example: What is the weather like today? + type: string + AIGuardMessageRole: + description: The role of the message author in the conversation. + enum: + - user + - assistant + - system + - developer + - tool + example: user + type: string + x-enum-varnames: + - USER + - ASSISTANT + - SYSTEM + - DEVELOPER + - TOOL + AIGuardMeta: + description: Metadata about the evaluated conversation, used to select the policy and evaluator behavior. + properties: + coding_agent: + $ref: "#/components/schemas/AIGuardCodingAgent" + confidence_threshold: + $ref: "#/components/schemas/AIGuardConfidenceThresholdOverride" + env: + description: The environment the conversation belongs to. + example: production + type: string + is_sds_enabled_override: + description: Whether to run the Sensitive Data Scanner for this request, overriding the stored policy. + example: true + type: boolean + service: + description: The name of the service the conversation belongs to. + example: my-web-app + type: string + tool_policies: + additionalProperties: + $ref: "#/components/schemas/AIGuardToolPolicyAction" + description: |- + A non-empty map replaces stored tool policies for this request only, + keyed by tool name. Any called tool not present resolves to EVALUATE. + Only takes effect when tool policies are enabled for the organization. + example: + shell: BLOCK + type: object + type: object + AIGuardRedactionReplacement: + description: The fully redacted content for one path in the request. + properties: + path: + description: The path of the redacted content relative to the request attributes. + example: messages[0].content + type: string + replacement: + description: The redacted replacement content. + example: "[REDACTED]" + type: string + required: + - path + - replacement + type: object + AIGuardScfwFindings: + description: |- + The Supply Chain Firewall verdict for the version-pinned install packages + found in the request. Evaluation requires Supply Chain Firewall to be enabled + for the organization and the caller to have the `appsec_vm_read` permission. + Without that permission, Supply Chain Firewall evaluation is skipped; the + AI Guard evaluation request itself is not rejected. Findings are omitted + when Supply Chain Firewall does not produce a result. + The recommended action combines the package outcomes. + Confirmation support is inferred from the evaluator profile, not from a + separate confirmation-capability flag. By default, a non-empty + `meta.coding_agent` selects the coding-agent profile, which assumes the caller + can ask for confirmation and retains WARN in `recommended_action`. + Omitting `meta.coding_agent` or setting it to an empty string selects the + default profile, which assumes the caller cannot ask for confirmation and + returns ALLOW instead of WARN in `recommended_action`. Package outcomes + still retain WARN, and BLOCK is never changed to ALLOW by this rule. + properties: + packages: + description: The packages the firewall evaluated, `ALLOW` ones included. + items: + $ref: "#/components/schemas/AIGuardScfwPackageFinding" + type: array + recommended_action: + $ref: "#/components/schemas/AIGuardScfwOutcome" + required: + - recommended_action + type: object + AIGuardScfwMatchedRule: + description: One rule the Supply Chain Firewall matched on a package, with the outcome the rule reached. + properties: + outcome: + $ref: "#/components/schemas/AIGuardScfwOutcome" + rule: + description: The rule text the firewall reported, or the policy type when no text was reported. + example: "Disallow install commands" + type: string + required: + - rule + - outcome + type: object + AIGuardScfwOutcome: + description: The Supply Chain Firewall outcome for a package, a matched rule, or the whole request. + enum: + - ALLOW + - WARN + - BLOCK + example: BLOCK + type: string + x-enum-descriptions: + - Proceed with the install. + - Confirm with the user before installing. + - Do not install. + x-enum-varnames: + - ALLOW + - WARN + - BLOCK + AIGuardScfwPackageFinding: + description: One package the Supply Chain Firewall evaluated, with the outcome and every matched rule. + properties: + ecosystem: + description: The package ecosystem the firewall evaluated, such as `PyPI`, `npm`, or `crates.io`. + example: npm + type: string + matched_rules: + description: Every rule the firewall matched on the package. + items: + $ref: "#/components/schemas/AIGuardScfwMatchedRule" + type: array + name: + description: The name of the package. + example: left-pad + type: string + outcome: + $ref: "#/components/schemas/AIGuardScfwOutcome" + version: + description: The version of the package. + example: 1.3.0 + type: string + required: + - ecosystem + - name + - version + - outcome + type: object + AIGuardSdsFinding: + description: A sensitive data finding from the Sensitive Data Scanner. + properties: + category: + description: The category of the sensitive data found. + example: financial + type: string + location: + $ref: "#/components/schemas/AIGuardSdsFindingLocation" + rule_display_name: + description: The display name of the SDS rule that matched. + example: Credit Card Number + type: string + rule_tag: + description: The tag of the SDS rule that matched. + example: credit-card + type: string + required: + - rule_display_name + - rule_tag + - category + - location + type: object + AIGuardSdsFindingLocation: + description: The location of a sensitive data match within the request. + properties: + end_index_exclusive: + description: The exclusive end index of the match within the located content. + example: 42 + format: int64 + type: integer + path: + description: The path of the match relative to the request attributes. + example: messages[0].content + type: string + start_index: + description: The start index of the match within the located content. + example: 12 + format: int64 + type: integer + required: + - path + - start_index + - end_index_exclusive + type: object + AIGuardToolCall: + description: A tool call emitted by the assistant. + properties: + function: + $ref: "#/components/schemas/AIGuardToolCallFunction" + id: + description: The identifier of the tool call. + example: call_1 + type: string + required: + - id + - function + type: object + AIGuardToolCallFunction: + description: The function invoked by a tool call. + properties: + arguments: + description: The arguments of the function call, serialized as a JSON string. + example: '{"command":"shutdown"}' + type: string + name: + description: The name of the function to call. + example: shell + type: string + required: + - name + - arguments + type: object + AIGuardToolPolicyAction: + description: |- + The policy action for a tool named by the property. Tool policies override + the recommendation after evaluation; they do not skip evaluation. + Any BLOCK takes precedence over PASS and EVALUATE. The Supply Chain Firewall + is applied afterward and can still return DENY. + enum: + - PASS + - BLOCK + - EVALUATE + example: EVALUATE + type: string + x-enum-descriptions: + - Recommend ALLOW when all called tools use PASS, overriding the evaluation result. + - Recommend DENY if any called tool uses BLOCK, overriding the evaluation result. + - Use the evaluation result unless another called tool uses BLOCK. + x-enum-varnames: + - PASS + - BLOCK + - EVALUATE AIImpactUserActivityAttributes: additionalProperties: false description: |- @@ -145100,6 +145611,7 @@ components: ai_gateway_config_read: View AI Gateway configuration. ai_gateway_config_write: Create, update, and delete AI Gateway configuration. ai_gateway_usage: Use AI Gateway and view configuration applicable to the authenticated user. + ai_guard_evaluate: Evaluate conversations for security threats with AI Guard. apm_api_catalog_read: View API catalog and API definitions. apm_api_catalog_write: Add, modify, and delete API catalog definitions. apm_read: Read and query APM and Trace Analytics. @@ -148247,6 +148759,125 @@ paths: operator: OR permissions: - security_monitoring_findings_read + /api/v2/ai-guard/evaluate: + post: + description: |- + Analyzes a conversation for security threats such as prompt injection, + jailbreak attempts, and other AI-specific attacks. Returns an action + recommendation (`ALLOW`, `DENY`, or `ABORT`) along with the detected threat + tags and their confidence scores. + AI Guard must be activated for the organization, and the calling user or + application key requires the `ai_guard_evaluate` permission. The action is a + recommendation for the caller to enforce: ALLOW permits the interaction, + DENY blocks the interaction, and ABORT terminates the workflow. + This endpoint is rate limited to `700` requests per minute per organization. + Organization token limits can also result in a 429 response. + Malformed messages return a 400 response. Requests exceeding the raw-input + limit of 1,000,000 tokens return a 413 response; this limit applies to the + input context, not only the size of the HTTP request body. + operationId: EvaluateAIGuardRequest + requestBody: + content: + application/json: + examples: + default: + summary: Evaluate a user message + value: + data: + attributes: + messages: + - content: Hello world! + role: user + meta: + confidence_threshold: 0.8 + type: evaluations + per_category_threshold: + summary: Evaluate with per-category confidence thresholds + value: + data: + attributes: + messages: + - content: Hello world! + role: user + meta: + confidence_threshold: + flagging_global: 0.6 + indirect-prompt-injection: 0.4 + policy-evasion: 0.9 + type: evaluations + schema: + $ref: "#/components/schemas/AIGuardEvaluateRequest" + description: The conversation to evaluate for security threats. + required: true + responses: + "200": + content: + application/json: + examples: + default: + summary: Evaluation of a benign user message + value: + data: + attributes: + action: ALLOW + global_prob: 0.005218505859375 + is_blocking_enabled: false + reason: No rule match. + tag_probs: + tags: [] + id: a76a703c-324f-42ed-af33-9b17d65e28af + type: evaluations + schema: + $ref: "#/components/schemas/AIGuardEvaluateResponse" + description: OK + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Bad Request + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Forbidden + "408": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Request Timeout + "413": + content: + application/json: + schema: + $ref: "#/components/schemas/JSONAPIErrorResponse" + description: Payload Too Large + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + security: + - apiKeyAuth: [] + appKeyAuth: [] + - AuthZ: + - ai_guard_evaluate + summary: Evaluate an AI Guard request + tags: + - AI Guard + x-codegen-request-body-name: body + x-permission: + operator: AND + permissions: + - ai_guard_evaluate + x-unstable: |- + **Note**: This endpoint is in preview and is subject to change. + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/). /api/v2/ai_impact/user_activity: post: description: |- @@ -256357,6 +256988,8 @@ servers: default: api description: The subdomain where the API is deployed. tags: + - description: Evaluate LLM interactions for security threats such as prompt injection and jailbreak attempts. + name: AI Guard - description: |- Configure your API endpoints through the Datadog API. name: API Management diff --git a/examples/v2/ai-guard/EvaluateAIGuardRequest.py b/examples/v2/ai-guard/EvaluateAIGuardRequest.py new file mode 100644 index 0000000000..9d57f25da4 --- /dev/null +++ b/examples/v2/ai-guard/EvaluateAIGuardRequest.py @@ -0,0 +1,40 @@ +""" +Evaluate an AI Guard request returns "OK" response +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.ai_guard_api import AIGuardApi +from datadog_api_client.v2.model.ai_guard_evaluate_request import AIGuardEvaluateRequest +from datadog_api_client.v2.model.ai_guard_evaluate_request_data import AIGuardEvaluateRequestData +from datadog_api_client.v2.model.ai_guard_evaluate_request_data_attributes import AIGuardEvaluateRequestDataAttributes +from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType +from datadog_api_client.v2.model.ai_guard_message import AIGuardMessage +from datadog_api_client.v2.model.ai_guard_message_role import AIGuardMessageRole +from datadog_api_client.v2.model.ai_guard_meta import AIGuardMeta + +body = AIGuardEvaluateRequest( + data=AIGuardEvaluateRequestData( + type=AIGuardEvaluationType.EVALUATIONS, + attributes=AIGuardEvaluateRequestDataAttributes( + messages=[ + AIGuardMessage( + content="Hello world!", + role=AIGuardMessageRole.USER, + ), + ], + meta=AIGuardMeta( + confidence_threshold=0.8, + ), + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +configuration.unstable_operations["evaluate_ai_guard_request"] = True +with ApiClient(configuration) as api_client: + api_instance = AIGuardApi(api_client) + response = api_instance.evaluate_ai_guard_request(body=body) + + print(response) diff --git a/examples/v2/ai-guard/EvaluateAIGuardRequest_1377048937.py b/examples/v2/ai-guard/EvaluateAIGuardRequest_1377048937.py new file mode 100644 index 0000000000..c2b39d486b --- /dev/null +++ b/examples/v2/ai-guard/EvaluateAIGuardRequest_1377048937.py @@ -0,0 +1,45 @@ +""" +Evaluate an AI Guard request with per-category confidence thresholds +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.ai_guard_api import AIGuardApi +from datadog_api_client.v2.model.ai_guard_confidence_threshold import AIGuardConfidenceThreshold +from datadog_api_client.v2.model.ai_guard_evaluate_request import AIGuardEvaluateRequest +from datadog_api_client.v2.model.ai_guard_evaluate_request_data import AIGuardEvaluateRequestData +from datadog_api_client.v2.model.ai_guard_evaluate_request_data_attributes import AIGuardEvaluateRequestDataAttributes +from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType +from datadog_api_client.v2.model.ai_guard_message import AIGuardMessage +from datadog_api_client.v2.model.ai_guard_message_role import AIGuardMessageRole +from datadog_api_client.v2.model.ai_guard_meta import AIGuardMeta + +body = AIGuardEvaluateRequest( + data=AIGuardEvaluateRequestData( + type=AIGuardEvaluationType.EVALUATIONS, + attributes=AIGuardEvaluateRequestDataAttributes( + messages=[ + AIGuardMessage( + role=AIGuardMessageRole.USER, + content="Hello world!", + ), + ], + meta=AIGuardMeta( + confidence_threshold=AIGuardConfidenceThreshold( + flagging_global=0.6, + policy_evasion=0.9, + indirect_prompt_injection=0.4, + ), + ), + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +configuration.unstable_operations["evaluate_ai_guard_request"] = True +with ApiClient(configuration) as api_client: + api_instance = AIGuardApi(api_client) + response = api_instance.evaluate_ai_guard_request(body=body) + + print(response) diff --git a/examples/v2/ai-guard/EvaluateAIGuardRequest_3923646360.py b/examples/v2/ai-guard/EvaluateAIGuardRequest_3923646360.py new file mode 100644 index 0000000000..4080b2327d --- /dev/null +++ b/examples/v2/ai-guard/EvaluateAIGuardRequest_3923646360.py @@ -0,0 +1,40 @@ +""" +Evaluate an AI Guard request with a numeric confidence threshold +""" + +from os import environ +from datadog_api_client import ApiClient, Configuration +from datadog_api_client.v2.api.ai_guard_api import AIGuardApi +from datadog_api_client.v2.model.ai_guard_evaluate_request import AIGuardEvaluateRequest +from datadog_api_client.v2.model.ai_guard_evaluate_request_data import AIGuardEvaluateRequestData +from datadog_api_client.v2.model.ai_guard_evaluate_request_data_attributes import AIGuardEvaluateRequestDataAttributes +from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType +from datadog_api_client.v2.model.ai_guard_message import AIGuardMessage +from datadog_api_client.v2.model.ai_guard_message_role import AIGuardMessageRole +from datadog_api_client.v2.model.ai_guard_meta import AIGuardMeta + +body = AIGuardEvaluateRequest( + data=AIGuardEvaluateRequestData( + type=AIGuardEvaluationType.EVALUATIONS, + attributes=AIGuardEvaluateRequestDataAttributes( + messages=[ + AIGuardMessage( + role=AIGuardMessageRole.USER, + content="Hello world!", + ), + ], + meta=AIGuardMeta( + confidence_threshold=0.8, + ), + ), + ), +) + +configuration = Configuration() +configuration.access_token = environ["DD_BEARER_TOKEN"] +configuration.unstable_operations["evaluate_ai_guard_request"] = True +with ApiClient(configuration) as api_client: + api_instance = AIGuardApi(api_client) + response = api_instance.evaluate_ai_guard_request(body=body) + + print(response) diff --git a/src/datadog_api_client/configuration.py b/src/datadog_api_client/configuration.py index 29f69e0e9d..6e72e56290 100644 --- a/src/datadog_api_client/configuration.py +++ b/src/datadog_api_client/configuration.py @@ -341,6 +341,7 @@ def __init__( "v2.get_execution_policy": False, "v2.list_execution_policies": False, "v2.update_execution_policy": False, + "v2.evaluate_ai_guard_request": False, "v2.create_annotation": False, "v2.delete_annotation": False, "v2.get_page_annotations": False, diff --git a/src/datadog_api_client/v2/api/ai_guard_api.py b/src/datadog_api_client/v2/api/ai_guard_api.py new file mode 100644 index 0000000000..6b68778a3e --- /dev/null +++ b/src/datadog_api_client/v2/api/ai_guard_api.py @@ -0,0 +1,71 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Any, Dict + +from datadog_api_client.api_client import ApiClient, Endpoint as _Endpoint +from datadog_api_client.configuration import Configuration +from datadog_api_client.v2.model.ai_guard_evaluate_response import AIGuardEvaluateResponse +from datadog_api_client.v2.model.ai_guard_evaluate_request import AIGuardEvaluateRequest + + +class AIGuardApi: + """ + Evaluate LLM interactions for security threats such as prompt injection and jailbreak attempts. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient(Configuration()) + self.api_client = api_client + + self._evaluate_ai_guard_request_endpoint = _Endpoint( + settings={ + "response_type": (AIGuardEvaluateResponse,), + "auth": ["apiKeyAuth", "appKeyAuth", "AuthZ"], + "endpoint_path": "/api/v2/ai-guard/evaluate", + "operation_id": "evaluate_ai_guard_request", + "http_method": "POST", + "version": "v2", + }, + params_map={ + "body": { + "required": True, + "openapi_types": (AIGuardEvaluateRequest,), + "location": "body", + }, + }, + headers_map={"accept": ["application/json"], "content_type": ["application/json"]}, + api_client=api_client, + ) + + def evaluate_ai_guard_request( + self, + body: AIGuardEvaluateRequest, + ) -> AIGuardEvaluateResponse: + """Evaluate an AI Guard request. + + Analyzes a conversation for security threats such as prompt injection, + jailbreak attempts, and other AI-specific attacks. Returns an action + recommendation ( ``ALLOW`` , ``DENY`` , or ``ABORT`` ) along with the detected threat + tags and their confidence scores. + AI Guard must be activated for the organization, and the calling user or + application key requires the ``ai_guard_evaluate`` permission. The action is a + recommendation for the caller to enforce: ALLOW permits the interaction, + DENY blocks the interaction, and ABORT terminates the workflow. + This endpoint is rate limited to ``700`` requests per minute per organization. + Organization token limits can also result in a 429 response. + Malformed messages return a 400 response. Requests exceeding the raw-input + limit of 1,000,000 tokens return a 413 response; this limit applies to the + input context, not only the size of the HTTP request body. + + :param body: The conversation to evaluate for security threats. + :type body: AIGuardEvaluateRequest + :rtype: AIGuardEvaluateResponse + """ + kwargs: Dict[str, Any] = {} + kwargs["body"] = body + + return self._evaluate_ai_guard_request_endpoint.call_with_http_info(**kwargs) diff --git a/src/datadog_api_client/v2/apis/__init__.py b/src/datadog_api_client/v2/apis/__init__.py index 830facf196..5eb33da03a 100644 --- a/src/datadog_api_client/v2/apis/__init__.py +++ b/src/datadog_api_client/v2/apis/__init__.py @@ -1,3 +1,4 @@ +from datadog_api_client.v2.api.ai_guard_api import AIGuardApi from datadog_api_client.v2.api.api_management_api import APIManagementApi from datadog_api_client.v2.api.apm_api import APMApi from datadog_api_client.v2.api.apm_retention_filters_api import APMRetentionFiltersApi @@ -154,6 +155,7 @@ __all__ = [ + "AIGuardApi", "APIManagementApi", "APMApi", "APMRetentionFiltersApi", diff --git a/src/datadog_api_client/v2/model/ai_guard_action.py b/src/datadog_api_client/v2/model/ai_guard_action.py new file mode 100644 index 0000000000..4e6e841b48 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_action.py @@ -0,0 +1,41 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelSimple, + cached_property, +) + +from typing import ClassVar + + +class AIGuardAction(ModelSimple): + """ + The recommended action for the evaluated conversation. + + :param value: Must be one of ["ALLOW", "DENY", "ABORT"]. + :type value: str + """ + + allowed_values = { + "ALLOW", + "DENY", + "ABORT", + } + ALLOW: ClassVar["AIGuardAction"] + DENY: ClassVar["AIGuardAction"] + ABORT: ClassVar["AIGuardAction"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +AIGuardAction.ALLOW = AIGuardAction("ALLOW") +AIGuardAction.DENY = AIGuardAction("DENY") +AIGuardAction.ABORT = AIGuardAction("ABORT") diff --git a/src/datadog_api_client/v2/model/ai_guard_confidence_threshold.py b/src/datadog_api_client/v2/model/ai_guard_confidence_threshold.py new file mode 100644 index 0000000000..305873f7db --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_confidence_threshold.py @@ -0,0 +1,49 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Union + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +class AIGuardConfidenceThreshold(ModelNormal): + @cached_property + def additional_properties_type(_): + return (float,) + + @cached_property + def openapi_types(_): + return { + "flagging_global": (float,), + } + + attribute_map = { + "flagging_global": "flagging_global", + } + + def __init__(self_, flagging_global: Union[float, UnsetType] = unset, **kwargs): + """ + Request-level confidence threshold override. ``flagging_global`` is the global + threshold applied to every threat category. Any other property is a + per-category override keyed by an AI Guard threat tag; per-category values + take precedence over ``flagging_global`` , and categories without an entry fall + back to it. When this object omits flagging_global, the fallback is ``0.5``. + Recognized category keys include policy-evasion, indirect-prompt-injection, + system-and-capability-discovery, credential-access, sensitive-data-collection, + privilege-escalation, malicious-execution, private-network-target, + destructive-or-resource-abuse, tool-abuse, poisoning-or-persistence, and + ``data-exfiltration``. Unknown category keys are ignored. + + :param flagging_global: The global confidence threshold applied to categories without a specific override. + :type flagging_global: float, optional + """ + if flagging_global is not unset: + kwargs["flagging_global"] = flagging_global + super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/ai_guard_confidence_threshold_override.py b/src/datadog_api_client/v2/model/ai_guard_confidence_threshold_override.py new file mode 100644 index 0000000000..129c3a32e4 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_confidence_threshold_override.py @@ -0,0 +1,42 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelComposed, + cached_property, +) + + +class AIGuardConfidenceThresholdOverride(ModelComposed): + def __init__(self, **kwargs): + """ + Request-only confidence threshold override. A number sets the threshold + for every threat category; an object supplies a global threshold and + optional per-category overrides. Omit this field to use the stored + sensitivity policy. This field is not returned in evaluation responses. + + :param flagging_global: The global confidence threshold applied to categories without a specific override. + :type flagging_global: float, optional + """ + super().__init__(kwargs) + + @cached_property + def _composed_schemas(_): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + from datadog_api_client.v2.model.ai_guard_confidence_threshold import AIGuardConfidenceThreshold + + return { + "oneOf": [ + float, + AIGuardConfidenceThreshold, + ], + } diff --git a/src/datadog_api_client/v2/model/ai_guard_content_part.py b/src/datadog_api_client/v2/model/ai_guard_content_part.py new file mode 100644 index 0000000000..cdbba00b5d --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_content_part.py @@ -0,0 +1,67 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_image_url import AIGuardImageURL + from datadog_api_client.v2.model.ai_guard_content_part_type import AIGuardContentPartType + + +class AIGuardContentPart(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_image_url import AIGuardImageURL + from datadog_api_client.v2.model.ai_guard_content_part_type import AIGuardContentPartType + + return { + "image_url": (AIGuardImageURL,), + "text": (str,), + "type": (AIGuardContentPartType,), + } + + attribute_map = { + "image_url": "image_url", + "text": "text", + "type": "type", + } + + def __init__( + self_, + type: AIGuardContentPartType, + image_url: Union[AIGuardImageURL, UnsetType] = unset, + text: Union[str, UnsetType] = unset, + **kwargs, + ): + """ + One part of multipart message content, following the OpenAI content part + format. For user, system, developer, and tool messages, a text part must + include non-empty ``text`` , and an image part must include ``image_url`` with + a non-empty ``url``. These content-part checks do not apply to assistant messages. + + :param image_url: An image referenced by URL in a message content part. + :type image_url: AIGuardImageURL, optional + + :param text: The text content, required and non-empty when ``type`` is ``text`` in a user, system, developer, or tool message. + :type text: str, optional + + :param type: The type of the content part. + :type type: AIGuardContentPartType + """ + if image_url is not unset: + kwargs["image_url"] = image_url + if text is not unset: + kwargs["text"] = text + super().__init__(kwargs) + + self_.type = type diff --git a/src/datadog_api_client/v2/model/ai_guard_content_part_type.py b/src/datadog_api_client/v2/model/ai_guard_content_part_type.py new file mode 100644 index 0000000000..f2c9fede57 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_content_part_type.py @@ -0,0 +1,38 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelSimple, + cached_property, +) + +from typing import ClassVar + + +class AIGuardContentPartType(ModelSimple): + """ + The type of the content part. + + :param value: Must be one of ["text", "image_url"]. + :type value: str + """ + + allowed_values = { + "text", + "image_url", + } + TEXT: ClassVar["AIGuardContentPartType"] + IMAGE_URL: ClassVar["AIGuardContentPartType"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +AIGuardContentPartType.TEXT = AIGuardContentPartType("text") +AIGuardContentPartType.IMAGE_URL = AIGuardContentPartType("image_url") diff --git a/src/datadog_api_client/v2/model/ai_guard_evaluate_attributes_response.py b/src/datadog_api_client/v2/model/ai_guard_evaluate_attributes_response.py new file mode 100644 index 0000000000..106e6075f6 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_evaluate_attributes_response.py @@ -0,0 +1,127 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Dict, List, Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + none_type, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_action import AIGuardAction + from datadog_api_client.v2.model.ai_guard_redaction_replacement import AIGuardRedactionReplacement + from datadog_api_client.v2.model.ai_guard_scfw_findings import AIGuardScfwFindings + from datadog_api_client.v2.model.ai_guard_sds_finding import AIGuardSdsFinding + + +class AIGuardEvaluateAttributesResponse(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_action import AIGuardAction + from datadog_api_client.v2.model.ai_guard_redaction_replacement import AIGuardRedactionReplacement + from datadog_api_client.v2.model.ai_guard_scfw_findings import AIGuardScfwFindings + from datadog_api_client.v2.model.ai_guard_sds_finding import AIGuardSdsFinding + + return { + "action": (AIGuardAction,), + "global_prob": (float,), + "is_blocking_enabled": (bool,), + "reason": (str,), + "redaction_replacements": ([AIGuardRedactionReplacement],), + "scfw_findings": (AIGuardScfwFindings,), + "sds_findings": ([AIGuardSdsFinding],), + "tag_probs": ({str: (float,)}, none_type), + "tags": ([str],), + } + + attribute_map = { + "action": "action", + "global_prob": "global_prob", + "is_blocking_enabled": "is_blocking_enabled", + "reason": "reason", + "redaction_replacements": "redaction_replacements", + "scfw_findings": "scfw_findings", + "sds_findings": "sds_findings", + "tag_probs": "tag_probs", + "tags": "tags", + } + + def __init__( + self_, + action: AIGuardAction, + is_blocking_enabled: bool, + reason: str, + tag_probs: Union[Dict[str, float], none_type], + tags: List[str], + global_prob: Union[float, UnsetType] = unset, + redaction_replacements: Union[List[AIGuardRedactionReplacement], UnsetType] = unset, + scfw_findings: Union[AIGuardScfwFindings, UnsetType] = unset, + sds_findings: Union[List[AIGuardSdsFinding], UnsetType] = unset, + **kwargs, + ): + """ + The evaluation result with the recommended action and detected threat information. + + :param action: The recommended action for the evaluated conversation. + :type action: AIGuardAction + + :param global_prob: The global threat probability, between ``0`` and ``1``. Included only when computed. + :type global_prob: float, optional + + :param is_blocking_enabled: Whether blocking is enabled for the organization. + :type is_blocking_enabled: bool + + :param reason: The explanation for the recommended action, for logging rather than user-facing display. + :type reason: str + + :param redaction_replacements: The redacted replacements computed for the request. Included only when redaction applies. + :type redaction_replacements: [AIGuardRedactionReplacement], optional + + :param scfw_findings: The Supply Chain Firewall verdict for the version-pinned install packages + found in the request. Evaluation requires Supply Chain Firewall to be enabled + for the organization and the caller to have the ``appsec_vm_read`` permission. + Without that permission, Supply Chain Firewall evaluation is skipped; the + AI Guard evaluation request itself is not rejected. Findings are omitted + when Supply Chain Firewall does not produce a result. + The recommended action combines the package outcomes. + Confirmation support is inferred from the evaluator profile, not from a + separate confirmation-capability flag. By default, a non-empty + ``meta.coding_agent`` selects the coding-agent profile, which assumes the caller + can ask for confirmation and retains WARN in ``recommended_action``. + Omitting ``meta.coding_agent`` or setting it to an empty string selects the + default profile, which assumes the caller cannot ask for confirmation and + returns ALLOW instead of WARN in ``recommended_action``. Package outcomes + still retain WARN, and BLOCK is never changed to ALLOW by this rule. + :type scfw_findings: AIGuardScfwFindings, optional + + :param sds_findings: The sensitive data findings from the Sensitive Data Scanner. Included only when findings exist. + :type sds_findings: [AIGuardSdsFinding], optional + + :param tag_probs: The detection confidence for each threat tag, between ``0`` and ``1``. May be null when the evaluation does not produce per-category scores. + :type tag_probs: {str: (float,)}, none_type + + :param tags: The threat tags detected in the conversation. + :type tags: [str] + """ + if global_prob is not unset: + kwargs["global_prob"] = global_prob + if redaction_replacements is not unset: + kwargs["redaction_replacements"] = redaction_replacements + if scfw_findings is not unset: + kwargs["scfw_findings"] = scfw_findings + if sds_findings is not unset: + kwargs["sds_findings"] = sds_findings + super().__init__(kwargs) + + self_.action = action + self_.is_blocking_enabled = is_blocking_enabled + self_.reason = reason + self_.tag_probs = tag_probs + self_.tags = tags diff --git a/src/datadog_api_client/v2/model/ai_guard_evaluate_data_response.py b/src/datadog_api_client/v2/model/ai_guard_evaluate_data_response.py new file mode 100644 index 0000000000..0c6a07cfd7 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_evaluate_data_response.py @@ -0,0 +1,55 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_evaluate_attributes_response import AIGuardEvaluateAttributesResponse + from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType + + +class AIGuardEvaluateDataResponse(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_evaluate_attributes_response import AIGuardEvaluateAttributesResponse + from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType + + return { + "attributes": (AIGuardEvaluateAttributesResponse,), + "id": (str,), + "type": (AIGuardEvaluationType,), + } + + attribute_map = { + "attributes": "attributes", + "id": "id", + "type": "type", + } + + def __init__(self_, attributes: AIGuardEvaluateAttributesResponse, id: str, type: AIGuardEvaluationType, **kwargs): + """ + The JSON:API evaluation resource. + + :param attributes: The evaluation result with the recommended action and detected threat information. + :type attributes: AIGuardEvaluateAttributesResponse + + :param id: The unique identifier of the evaluation. + :type id: str + + :param type: The JSON:API resource type for AI Guard evaluation requests and responses. + Its value is ``evaluations`` for the action endpoint ``/api/v2/ai-guard/evaluate``. + :type type: AIGuardEvaluationType + """ + super().__init__(kwargs) + + self_.attributes = attributes + self_.id = id + self_.type = type diff --git a/src/datadog_api_client/v2/model/ai_guard_evaluate_request.py b/src/datadog_api_client/v2/model/ai_guard_evaluate_request.py new file mode 100644 index 0000000000..fc3b55d235 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_evaluate_request.py @@ -0,0 +1,40 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_evaluate_request_data import AIGuardEvaluateRequestData + + +class AIGuardEvaluateRequest(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_evaluate_request_data import AIGuardEvaluateRequestData + + return { + "data": (AIGuardEvaluateRequestData,), + } + + attribute_map = { + "data": "data", + } + + def __init__(self_, data: AIGuardEvaluateRequestData, **kwargs): + """ + The request to evaluate a conversation for security threats. + + :param data: The JSON:API request envelope for an AI Guard evaluation. + :type data: AIGuardEvaluateRequestData + """ + super().__init__(kwargs) + + self_.data = data diff --git a/src/datadog_api_client/v2/model/ai_guard_evaluate_request_data.py b/src/datadog_api_client/v2/model/ai_guard_evaluate_request_data.py new file mode 100644 index 0000000000..0007eb4f49 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_evaluate_request_data.py @@ -0,0 +1,53 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_evaluate_request_data_attributes import ( + AIGuardEvaluateRequestDataAttributes, + ) + from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType + + +class AIGuardEvaluateRequestData(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_evaluate_request_data_attributes import ( + AIGuardEvaluateRequestDataAttributes, + ) + from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType + + return { + "attributes": (AIGuardEvaluateRequestDataAttributes,), + "type": (AIGuardEvaluationType,), + } + + attribute_map = { + "attributes": "attributes", + "type": "type", + } + + def __init__(self_, attributes: AIGuardEvaluateRequestDataAttributes, type: AIGuardEvaluationType, **kwargs): + """ + The JSON:API request envelope for an AI Guard evaluation. + + :param attributes: The conversation to evaluate and its metadata. + :type attributes: AIGuardEvaluateRequestDataAttributes + + :param type: The JSON:API resource type for AI Guard evaluation requests and responses. + Its value is ``evaluations`` for the action endpoint ``/api/v2/ai-guard/evaluate``. + :type type: AIGuardEvaluationType + """ + super().__init__(kwargs) + + self_.attributes = attributes + self_.type = type diff --git a/src/datadog_api_client/v2/model/ai_guard_evaluate_request_data_attributes.py b/src/datadog_api_client/v2/model/ai_guard_evaluate_request_data_attributes.py new file mode 100644 index 0000000000..b9ad1c0fac --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_evaluate_request_data_attributes.py @@ -0,0 +1,57 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import List, Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_message import AIGuardMessage + from datadog_api_client.v2.model.ai_guard_meta import AIGuardMeta + + +class AIGuardEvaluateRequestDataAttributes(ModelNormal): + validations = { + "messages": { + "min_items": 1, + }, + } + + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_message import AIGuardMessage + from datadog_api_client.v2.model.ai_guard_meta import AIGuardMeta + + return { + "messages": ([AIGuardMessage],), + "meta": (AIGuardMeta,), + } + + attribute_map = { + "messages": "messages", + "meta": "meta", + } + + def __init__(self_, messages: List[AIGuardMessage], meta: Union[AIGuardMeta, UnsetType] = unset, **kwargs): + """ + The conversation to evaluate and its metadata. + + :param messages: The messages of the conversation to evaluate, in order. Must contain at least one message. + :type messages: [AIGuardMessage] + + :param meta: Metadata about the evaluated conversation, used to select the policy and evaluator behavior. + :type meta: AIGuardMeta, optional + """ + if meta is not unset: + kwargs["meta"] = meta + super().__init__(kwargs) + + self_.messages = messages diff --git a/src/datadog_api_client/v2/model/ai_guard_evaluate_response.py b/src/datadog_api_client/v2/model/ai_guard_evaluate_response.py new file mode 100644 index 0000000000..17a2dcc6da --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_evaluate_response.py @@ -0,0 +1,40 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_evaluate_data_response import AIGuardEvaluateDataResponse + + +class AIGuardEvaluateResponse(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_evaluate_data_response import AIGuardEvaluateDataResponse + + return { + "data": (AIGuardEvaluateDataResponse,), + } + + attribute_map = { + "data": "data", + } + + def __init__(self_, data: AIGuardEvaluateDataResponse, **kwargs): + """ + The response containing the evaluation result. + + :param data: The JSON:API evaluation resource. + :type data: AIGuardEvaluateDataResponse + """ + super().__init__(kwargs) + + self_.data = data diff --git a/src/datadog_api_client/v2/model/ai_guard_evaluation_type.py b/src/datadog_api_client/v2/model/ai_guard_evaluation_type.py new file mode 100644 index 0000000000..23c1b1ca64 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_evaluation_type.py @@ -0,0 +1,36 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelSimple, + cached_property, +) + +from typing import ClassVar + + +class AIGuardEvaluationType(ModelSimple): + """ + The JSON:API resource type for AI Guard evaluation requests and responses. + Its value is `evaluations` for the action endpoint `/api/v2/ai-guard/evaluate`. + + :param value: If omitted defaults to "evaluations". Must be one of ["evaluations"]. + :type value: str + """ + + allowed_values = { + "evaluations", + } + EVALUATIONS: ClassVar["AIGuardEvaluationType"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +AIGuardEvaluationType.EVALUATIONS = AIGuardEvaluationType("evaluations") diff --git a/src/datadog_api_client/v2/model/ai_guard_image_url.py b/src/datadog_api_client/v2/model/ai_guard_image_url.py new file mode 100644 index 0000000000..1f3fb78bad --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_image_url.py @@ -0,0 +1,33 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +class AIGuardImageURL(ModelNormal): + @cached_property + def openapi_types(_): + return { + "url": (str,), + } + + attribute_map = { + "url": "url", + } + + def __init__(self_, url: str, **kwargs): + """ + An image referenced by URL in a message content part. + + :param url: The image URL or a data URI with an ``image/png`` , ``image/jpeg`` , ``image/gif`` , or ``image/webp`` MIME type. + :type url: str + """ + super().__init__(kwargs) + + self_.url = url diff --git a/src/datadog_api_client/v2/model/ai_guard_message.py b/src/datadog_api_client/v2/model/ai_guard_message.py new file mode 100644 index 0000000000..b09ef33a0b --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_message.py @@ -0,0 +1,81 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import List, Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_message_content import AIGuardMessageContent + from datadog_api_client.v2.model.ai_guard_message_role import AIGuardMessageRole + from datadog_api_client.v2.model.ai_guard_tool_call import AIGuardToolCall + from datadog_api_client.v2.model.ai_guard_content_part import AIGuardContentPart + + +class AIGuardMessage(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_message_content import AIGuardMessageContent + from datadog_api_client.v2.model.ai_guard_message_role import AIGuardMessageRole + from datadog_api_client.v2.model.ai_guard_tool_call import AIGuardToolCall + + return { + "content": (AIGuardMessageContent,), + "role": (AIGuardMessageRole,), + "tool_call_id": (str,), + "tool_calls": ([AIGuardToolCall],), + } + + attribute_map = { + "content": "content", + "role": "role", + "tool_call_id": "tool_call_id", + "tool_calls": "tool_calls", + } + + def __init__( + self_, + role: AIGuardMessageRole, + content: Union[AIGuardMessageContent, str, List[AIGuardContentPart], UnsetType] = unset, + tool_call_id: Union[str, UnsetType] = unset, + tool_calls: Union[List[AIGuardToolCall], UnsetType] = unset, + **kwargs, + ): + """ + A message in the conversation. Content is required for user, system, + developer, and tool messages, but may be omitted for assistant messages. + For those non-assistant roles, a plain content string may be empty, but a + multipart content array must contain at least one valid content part. + Only assistant messages may contain tool calls, and tool call entries must + not be null. Tool messages must include a non-empty ``tool_call_id`` ; other + message roles must not include a non-empty ``tool_call_id``. + + :param content: The content of a message, either a plain string or a list of OpenAI-style content parts. + :type content: AIGuardMessageContent, optional + + :param role: The role of the message author in the conversation. + :type role: AIGuardMessageRole + + :param tool_call_id: The identifier of the tool call this message responds to, present on tool result messages. + :type tool_call_id: str, optional + + :param tool_calls: The tool calls emitted by the assistant, present on assistant messages that call tools. + :type tool_calls: [AIGuardToolCall], optional + """ + if content is not unset: + kwargs["content"] = content + if tool_call_id is not unset: + kwargs["tool_call_id"] = tool_call_id + if tool_calls is not unset: + kwargs["tool_calls"] = tool_calls + super().__init__(kwargs) + + self_.role = role diff --git a/src/datadog_api_client/v2/model/ai_guard_message_content.py b/src/datadog_api_client/v2/model/ai_guard_message_content.py new file mode 100644 index 0000000000..dbe84ab390 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_message_content.py @@ -0,0 +1,36 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelComposed, + cached_property, +) + + +class AIGuardMessageContent(ModelComposed): + def __init__(self, **kwargs): + """ + The content of a message, either a plain string or a list of OpenAI-style content parts. + """ + super().__init__(kwargs) + + @cached_property + def _composed_schemas(_): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + from datadog_api_client.v2.model.ai_guard_content_part import AIGuardContentPart + + return { + "oneOf": [ + str, + [AIGuardContentPart], + ], + } diff --git a/src/datadog_api_client/v2/model/ai_guard_message_role.py b/src/datadog_api_client/v2/model/ai_guard_message_role.py new file mode 100644 index 0000000000..bc890cfd8f --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_message_role.py @@ -0,0 +1,47 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelSimple, + cached_property, +) + +from typing import ClassVar + + +class AIGuardMessageRole(ModelSimple): + """ + The role of the message author in the conversation. + + :param value: Must be one of ["user", "assistant", "system", "developer", "tool"]. + :type value: str + """ + + allowed_values = { + "user", + "assistant", + "system", + "developer", + "tool", + } + USER: ClassVar["AIGuardMessageRole"] + ASSISTANT: ClassVar["AIGuardMessageRole"] + SYSTEM: ClassVar["AIGuardMessageRole"] + DEVELOPER: ClassVar["AIGuardMessageRole"] + TOOL: ClassVar["AIGuardMessageRole"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +AIGuardMessageRole.USER = AIGuardMessageRole("user") +AIGuardMessageRole.ASSISTANT = AIGuardMessageRole("assistant") +AIGuardMessageRole.SYSTEM = AIGuardMessageRole("system") +AIGuardMessageRole.DEVELOPER = AIGuardMessageRole("developer") +AIGuardMessageRole.TOOL = AIGuardMessageRole("tool") diff --git a/src/datadog_api_client/v2/model/ai_guard_meta.py b/src/datadog_api_client/v2/model/ai_guard_meta.py new file mode 100644 index 0000000000..fb6110065e --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_meta.py @@ -0,0 +1,103 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import Dict, Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_confidence_threshold_override import AIGuardConfidenceThresholdOverride + from datadog_api_client.v2.model.ai_guard_tool_policy_action import AIGuardToolPolicyAction + from datadog_api_client.v2.model.ai_guard_confidence_threshold import AIGuardConfidenceThreshold + + +class AIGuardMeta(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_confidence_threshold_override import ( + AIGuardConfidenceThresholdOverride, + ) + from datadog_api_client.v2.model.ai_guard_tool_policy_action import AIGuardToolPolicyAction + + return { + "coding_agent": (str,), + "confidence_threshold": (AIGuardConfidenceThresholdOverride,), + "env": (str,), + "is_sds_enabled_override": (bool,), + "service": (str,), + "tool_policies": ({str: (AIGuardToolPolicyAction,)},), + } + + attribute_map = { + "coding_agent": "coding_agent", + "confidence_threshold": "confidence_threshold", + "env": "env", + "is_sds_enabled_override": "is_sds_enabled_override", + "service": "service", + "tool_policies": "tool_policies", + } + + def __init__( + self_, + coding_agent: Union[str, UnsetType] = unset, + confidence_threshold: Union[ + AIGuardConfidenceThresholdOverride, float, AIGuardConfidenceThreshold, UnsetType + ] = unset, + env: Union[str, UnsetType] = unset, + is_sds_enabled_override: Union[bool, UnsetType] = unset, + service: Union[str, UnsetType] = unset, + tool_policies: Union[Dict[str, AIGuardToolPolicyAction], UnsetType] = unset, + **kwargs, + ): + """ + Metadata about the evaluated conversation, used to select the policy and evaluator behavior. + + :param coding_agent: The coding agent that produced the conversation. By default, a non-empty + value selects the coding-agent evaluator profile, while an omitted or empty + value selects the default profile. Known identifiers include ``claude-code`` , + ``codex`` , ``cursor`` , and ``github-copilot`` ; other identifiers are accepted. + The coding-agent profile assumes the caller can ask the user to confirm + Supply Chain Firewall warnings; the default profile assumes it cannot. + :type coding_agent: str, optional + + :param confidence_threshold: Request-only confidence threshold override. A number sets the threshold + for every threat category; an object supplies a global threshold and + optional per-category overrides. Omit this field to use the stored + sensitivity policy. This field is not returned in evaluation responses. + :type confidence_threshold: AIGuardConfidenceThresholdOverride, optional + + :param env: The environment the conversation belongs to. + :type env: str, optional + + :param is_sds_enabled_override: Whether to run the Sensitive Data Scanner for this request, overriding the stored policy. + :type is_sds_enabled_override: bool, optional + + :param service: The name of the service the conversation belongs to. + :type service: str, optional + + :param tool_policies: A non-empty map replaces stored tool policies for this request only, + keyed by tool name. Any called tool not present resolves to EVALUATE. + Only takes effect when tool policies are enabled for the organization. + :type tool_policies: {str: (AIGuardToolPolicyAction,)}, optional + """ + if coding_agent is not unset: + kwargs["coding_agent"] = coding_agent + if confidence_threshold is not unset: + kwargs["confidence_threshold"] = confidence_threshold + if env is not unset: + kwargs["env"] = env + if is_sds_enabled_override is not unset: + kwargs["is_sds_enabled_override"] = is_sds_enabled_override + if service is not unset: + kwargs["service"] = service + if tool_policies is not unset: + kwargs["tool_policies"] = tool_policies + super().__init__(kwargs) diff --git a/src/datadog_api_client/v2/model/ai_guard_redaction_replacement.py b/src/datadog_api_client/v2/model/ai_guard_redaction_replacement.py new file mode 100644 index 0000000000..fa71525449 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_redaction_replacement.py @@ -0,0 +1,39 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +class AIGuardRedactionReplacement(ModelNormal): + @cached_property + def openapi_types(_): + return { + "path": (str,), + "replacement": (str,), + } + + attribute_map = { + "path": "path", + "replacement": "replacement", + } + + def __init__(self_, path: str, replacement: str, **kwargs): + """ + The fully redacted content for one path in the request. + + :param path: The path of the redacted content relative to the request attributes. + :type path: str + + :param replacement: The redacted replacement content. + :type replacement: str + """ + super().__init__(kwargs) + + self_.path = path + self_.replacement = replacement diff --git a/src/datadog_api_client/v2/model/ai_guard_scfw_findings.py b/src/datadog_api_client/v2/model/ai_guard_scfw_findings.py new file mode 100644 index 0000000000..5dec3fbba0 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_scfw_findings.py @@ -0,0 +1,70 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import List, Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_scfw_package_finding import AIGuardScfwPackageFinding + from datadog_api_client.v2.model.ai_guard_scfw_outcome import AIGuardScfwOutcome + + +class AIGuardScfwFindings(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_scfw_package_finding import AIGuardScfwPackageFinding + from datadog_api_client.v2.model.ai_guard_scfw_outcome import AIGuardScfwOutcome + + return { + "packages": ([AIGuardScfwPackageFinding],), + "recommended_action": (AIGuardScfwOutcome,), + } + + attribute_map = { + "packages": "packages", + "recommended_action": "recommended_action", + } + + def __init__( + self_, + recommended_action: AIGuardScfwOutcome, + packages: Union[List[AIGuardScfwPackageFinding], UnsetType] = unset, + **kwargs, + ): + """ + The Supply Chain Firewall verdict for the version-pinned install packages + found in the request. Evaluation requires Supply Chain Firewall to be enabled + for the organization and the caller to have the ``appsec_vm_read`` permission. + Without that permission, Supply Chain Firewall evaluation is skipped; the + AI Guard evaluation request itself is not rejected. Findings are omitted + when Supply Chain Firewall does not produce a result. + The recommended action combines the package outcomes. + Confirmation support is inferred from the evaluator profile, not from a + separate confirmation-capability flag. By default, a non-empty + ``meta.coding_agent`` selects the coding-agent profile, which assumes the caller + can ask for confirmation and retains WARN in ``recommended_action``. + Omitting ``meta.coding_agent`` or setting it to an empty string selects the + default profile, which assumes the caller cannot ask for confirmation and + returns ALLOW instead of WARN in ``recommended_action``. Package outcomes + still retain WARN, and BLOCK is never changed to ALLOW by this rule. + + :param packages: The packages the firewall evaluated, ``ALLOW`` ones included. + :type packages: [AIGuardScfwPackageFinding], optional + + :param recommended_action: The Supply Chain Firewall outcome for a package, a matched rule, or the whole request. + :type recommended_action: AIGuardScfwOutcome + """ + if packages is not unset: + kwargs["packages"] = packages + super().__init__(kwargs) + + self_.recommended_action = recommended_action diff --git a/src/datadog_api_client/v2/model/ai_guard_scfw_matched_rule.py b/src/datadog_api_client/v2/model/ai_guard_scfw_matched_rule.py new file mode 100644 index 0000000000..643cdca695 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_scfw_matched_rule.py @@ -0,0 +1,46 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_scfw_outcome import AIGuardScfwOutcome + + +class AIGuardScfwMatchedRule(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_scfw_outcome import AIGuardScfwOutcome + + return { + "outcome": (AIGuardScfwOutcome,), + "rule": (str,), + } + + attribute_map = { + "outcome": "outcome", + "rule": "rule", + } + + def __init__(self_, outcome: AIGuardScfwOutcome, rule: str, **kwargs): + """ + One rule the Supply Chain Firewall matched on a package, with the outcome the rule reached. + + :param outcome: The Supply Chain Firewall outcome for a package, a matched rule, or the whole request. + :type outcome: AIGuardScfwOutcome + + :param rule: The rule text the firewall reported, or the policy type when no text was reported. + :type rule: str + """ + super().__init__(kwargs) + + self_.outcome = outcome + self_.rule = rule diff --git a/src/datadog_api_client/v2/model/ai_guard_scfw_outcome.py b/src/datadog_api_client/v2/model/ai_guard_scfw_outcome.py new file mode 100644 index 0000000000..1fadec8cca --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_scfw_outcome.py @@ -0,0 +1,41 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelSimple, + cached_property, +) + +from typing import ClassVar + + +class AIGuardScfwOutcome(ModelSimple): + """ + The Supply Chain Firewall outcome for a package, a matched rule, or the whole request. + + :param value: Must be one of ["ALLOW", "WARN", "BLOCK"]. + :type value: str + """ + + allowed_values = { + "ALLOW", + "WARN", + "BLOCK", + } + ALLOW: ClassVar["AIGuardScfwOutcome"] + WARN: ClassVar["AIGuardScfwOutcome"] + BLOCK: ClassVar["AIGuardScfwOutcome"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +AIGuardScfwOutcome.ALLOW = AIGuardScfwOutcome("ALLOW") +AIGuardScfwOutcome.WARN = AIGuardScfwOutcome("WARN") +AIGuardScfwOutcome.BLOCK = AIGuardScfwOutcome("BLOCK") diff --git a/src/datadog_api_client/v2/model/ai_guard_scfw_package_finding.py b/src/datadog_api_client/v2/model/ai_guard_scfw_package_finding.py new file mode 100644 index 0000000000..596c8ac894 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_scfw_package_finding.py @@ -0,0 +1,77 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import List, Union, TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, + unset, + UnsetType, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_scfw_matched_rule import AIGuardScfwMatchedRule + from datadog_api_client.v2.model.ai_guard_scfw_outcome import AIGuardScfwOutcome + + +class AIGuardScfwPackageFinding(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_scfw_matched_rule import AIGuardScfwMatchedRule + from datadog_api_client.v2.model.ai_guard_scfw_outcome import AIGuardScfwOutcome + + return { + "ecosystem": (str,), + "matched_rules": ([AIGuardScfwMatchedRule],), + "name": (str,), + "outcome": (AIGuardScfwOutcome,), + "version": (str,), + } + + attribute_map = { + "ecosystem": "ecosystem", + "matched_rules": "matched_rules", + "name": "name", + "outcome": "outcome", + "version": "version", + } + + def __init__( + self_, + ecosystem: str, + name: str, + outcome: AIGuardScfwOutcome, + version: str, + matched_rules: Union[List[AIGuardScfwMatchedRule], UnsetType] = unset, + **kwargs, + ): + """ + One package the Supply Chain Firewall evaluated, with the outcome and every matched rule. + + :param ecosystem: The package ecosystem the firewall evaluated, such as ``PyPI`` , ``npm`` , or ``crates.io``. + :type ecosystem: str + + :param matched_rules: Every rule the firewall matched on the package. + :type matched_rules: [AIGuardScfwMatchedRule], optional + + :param name: The name of the package. + :type name: str + + :param outcome: The Supply Chain Firewall outcome for a package, a matched rule, or the whole request. + :type outcome: AIGuardScfwOutcome + + :param version: The version of the package. + :type version: str + """ + if matched_rules is not unset: + kwargs["matched_rules"] = matched_rules + super().__init__(kwargs) + + self_.ecosystem = ecosystem + self_.name = name + self_.outcome = outcome + self_.version = version diff --git a/src/datadog_api_client/v2/model/ai_guard_sds_finding.py b/src/datadog_api_client/v2/model/ai_guard_sds_finding.py new file mode 100644 index 0000000000..3a8715bd8b --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_sds_finding.py @@ -0,0 +1,60 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_sds_finding_location import AIGuardSdsFindingLocation + + +class AIGuardSdsFinding(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_sds_finding_location import AIGuardSdsFindingLocation + + return { + "category": (str,), + "location": (AIGuardSdsFindingLocation,), + "rule_display_name": (str,), + "rule_tag": (str,), + } + + attribute_map = { + "category": "category", + "location": "location", + "rule_display_name": "rule_display_name", + "rule_tag": "rule_tag", + } + + def __init__( + self_, category: str, location: AIGuardSdsFindingLocation, rule_display_name: str, rule_tag: str, **kwargs + ): + """ + A sensitive data finding from the Sensitive Data Scanner. + + :param category: The category of the sensitive data found. + :type category: str + + :param location: The location of a sensitive data match within the request. + :type location: AIGuardSdsFindingLocation + + :param rule_display_name: The display name of the SDS rule that matched. + :type rule_display_name: str + + :param rule_tag: The tag of the SDS rule that matched. + :type rule_tag: str + """ + super().__init__(kwargs) + + self_.category = category + self_.location = location + self_.rule_display_name = rule_display_name + self_.rule_tag = rule_tag diff --git a/src/datadog_api_client/v2/model/ai_guard_sds_finding_location.py b/src/datadog_api_client/v2/model/ai_guard_sds_finding_location.py new file mode 100644 index 0000000000..3cd26fc291 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_sds_finding_location.py @@ -0,0 +1,45 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +class AIGuardSdsFindingLocation(ModelNormal): + @cached_property + def openapi_types(_): + return { + "end_index_exclusive": (int,), + "path": (str,), + "start_index": (int,), + } + + attribute_map = { + "end_index_exclusive": "end_index_exclusive", + "path": "path", + "start_index": "start_index", + } + + def __init__(self_, end_index_exclusive: int, path: str, start_index: int, **kwargs): + """ + The location of a sensitive data match within the request. + + :param end_index_exclusive: The exclusive end index of the match within the located content. + :type end_index_exclusive: int + + :param path: The path of the match relative to the request attributes. + :type path: str + + :param start_index: The start index of the match within the located content. + :type start_index: int + """ + super().__init__(kwargs) + + self_.end_index_exclusive = end_index_exclusive + self_.path = path + self_.start_index = start_index diff --git a/src/datadog_api_client/v2/model/ai_guard_tool_call.py b/src/datadog_api_client/v2/model/ai_guard_tool_call.py new file mode 100644 index 0000000000..ec1dd0804e --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_tool_call.py @@ -0,0 +1,46 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + +from typing import TYPE_CHECKING + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +if TYPE_CHECKING: + from datadog_api_client.v2.model.ai_guard_tool_call_function import AIGuardToolCallFunction + + +class AIGuardToolCall(ModelNormal): + @cached_property + def openapi_types(_): + from datadog_api_client.v2.model.ai_guard_tool_call_function import AIGuardToolCallFunction + + return { + "function": (AIGuardToolCallFunction,), + "id": (str,), + } + + attribute_map = { + "function": "function", + "id": "id", + } + + def __init__(self_, function: AIGuardToolCallFunction, id: str, **kwargs): + """ + A tool call emitted by the assistant. + + :param function: The function invoked by a tool call. + :type function: AIGuardToolCallFunction + + :param id: The identifier of the tool call. + :type id: str + """ + super().__init__(kwargs) + + self_.function = function + self_.id = id diff --git a/src/datadog_api_client/v2/model/ai_guard_tool_call_function.py b/src/datadog_api_client/v2/model/ai_guard_tool_call_function.py new file mode 100644 index 0000000000..4db60bdfdb --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_tool_call_function.py @@ -0,0 +1,39 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelNormal, + cached_property, +) + + +class AIGuardToolCallFunction(ModelNormal): + @cached_property + def openapi_types(_): + return { + "arguments": (str,), + "name": (str,), + } + + attribute_map = { + "arguments": "arguments", + "name": "name", + } + + def __init__(self_, arguments: str, name: str, **kwargs): + """ + The function invoked by a tool call. + + :param arguments: The arguments of the function call, serialized as a JSON string. + :type arguments: str + + :param name: The name of the function to call. + :type name: str + """ + super().__init__(kwargs) + + self_.arguments = arguments + self_.name = name diff --git a/src/datadog_api_client/v2/model/ai_guard_tool_policy_action.py b/src/datadog_api_client/v2/model/ai_guard_tool_policy_action.py new file mode 100644 index 0000000000..30df0879c1 --- /dev/null +++ b/src/datadog_api_client/v2/model/ai_guard_tool_policy_action.py @@ -0,0 +1,44 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019-Present Datadog, Inc. +from __future__ import annotations + + +from datadog_api_client.model_utils import ( + ModelSimple, + cached_property, +) + +from typing import ClassVar + + +class AIGuardToolPolicyAction(ModelSimple): + """ + The policy action for a tool named by the property. Tool policies override + the recommendation after evaluation; they do not skip evaluation. + Any BLOCK takes precedence over PASS and EVALUATE. The Supply Chain Firewall + is applied afterward and can still return DENY. + + :param value: Must be one of ["PASS", "BLOCK", "EVALUATE"]. + :type value: str + """ + + allowed_values = { + "PASS", + "BLOCK", + "EVALUATE", + } + PASS: ClassVar["AIGuardToolPolicyAction"] + BLOCK: ClassVar["AIGuardToolPolicyAction"] + EVALUATE: ClassVar["AIGuardToolPolicyAction"] + + @cached_property + def openapi_types(_): + return { + "value": (str,), + } + + +AIGuardToolPolicyAction.PASS = AIGuardToolPolicyAction("PASS") +AIGuardToolPolicyAction.BLOCK = AIGuardToolPolicyAction("BLOCK") +AIGuardToolPolicyAction.EVALUATE = AIGuardToolPolicyAction("EVALUATE") diff --git a/src/datadog_api_client/v2/models/__init__.py b/src/datadog_api_client/v2/models/__init__.py index bb42678277..7dcc7d10a2 100644 --- a/src/datadog_api_client/v2/models/__init__.py +++ b/src/datadog_api_client/v2/models/__init__.py @@ -1,3 +1,30 @@ +from datadog_api_client.v2.model.ai_guard_action import AIGuardAction +from datadog_api_client.v2.model.ai_guard_confidence_threshold import AIGuardConfidenceThreshold +from datadog_api_client.v2.model.ai_guard_confidence_threshold_override import AIGuardConfidenceThresholdOverride +from datadog_api_client.v2.model.ai_guard_content_part import AIGuardContentPart +from datadog_api_client.v2.model.ai_guard_content_part_type import AIGuardContentPartType +from datadog_api_client.v2.model.ai_guard_evaluate_attributes_response import AIGuardEvaluateAttributesResponse +from datadog_api_client.v2.model.ai_guard_evaluate_data_response import AIGuardEvaluateDataResponse +from datadog_api_client.v2.model.ai_guard_evaluate_request import AIGuardEvaluateRequest +from datadog_api_client.v2.model.ai_guard_evaluate_request_data import AIGuardEvaluateRequestData +from datadog_api_client.v2.model.ai_guard_evaluate_request_data_attributes import AIGuardEvaluateRequestDataAttributes +from datadog_api_client.v2.model.ai_guard_evaluate_response import AIGuardEvaluateResponse +from datadog_api_client.v2.model.ai_guard_evaluation_type import AIGuardEvaluationType +from datadog_api_client.v2.model.ai_guard_image_url import AIGuardImageURL +from datadog_api_client.v2.model.ai_guard_message import AIGuardMessage +from datadog_api_client.v2.model.ai_guard_message_content import AIGuardMessageContent +from datadog_api_client.v2.model.ai_guard_message_role import AIGuardMessageRole +from datadog_api_client.v2.model.ai_guard_meta import AIGuardMeta +from datadog_api_client.v2.model.ai_guard_redaction_replacement import AIGuardRedactionReplacement +from datadog_api_client.v2.model.ai_guard_scfw_findings import AIGuardScfwFindings +from datadog_api_client.v2.model.ai_guard_scfw_matched_rule import AIGuardScfwMatchedRule +from datadog_api_client.v2.model.ai_guard_scfw_outcome import AIGuardScfwOutcome +from datadog_api_client.v2.model.ai_guard_scfw_package_finding import AIGuardScfwPackageFinding +from datadog_api_client.v2.model.ai_guard_sds_finding import AIGuardSdsFinding +from datadog_api_client.v2.model.ai_guard_sds_finding_location import AIGuardSdsFindingLocation +from datadog_api_client.v2.model.ai_guard_tool_call import AIGuardToolCall +from datadog_api_client.v2.model.ai_guard_tool_call_function import AIGuardToolCallFunction +from datadog_api_client.v2.model.ai_guard_tool_policy_action import AIGuardToolPolicyAction from datadog_api_client.v2.model.ai_impact_user_activity_attributes import AIImpactUserActivityAttributes from datadog_api_client.v2.model.ai_impact_user_activity_data import AIImpactUserActivityData from datadog_api_client.v2.model.ai_impact_user_activity_request import AIImpactUserActivityRequest @@ -13149,6 +13176,33 @@ from datadog_api_client.v2.model.zoom_configuration_reference_data import ZoomConfigurationReferenceData __all__ = [ + "AIGuardAction", + "AIGuardConfidenceThreshold", + "AIGuardConfidenceThresholdOverride", + "AIGuardContentPart", + "AIGuardContentPartType", + "AIGuardEvaluateAttributesResponse", + "AIGuardEvaluateDataResponse", + "AIGuardEvaluateRequest", + "AIGuardEvaluateRequestData", + "AIGuardEvaluateRequestDataAttributes", + "AIGuardEvaluateResponse", + "AIGuardEvaluationType", + "AIGuardImageURL", + "AIGuardMessage", + "AIGuardMessageContent", + "AIGuardMessageRole", + "AIGuardMeta", + "AIGuardRedactionReplacement", + "AIGuardScfwFindings", + "AIGuardScfwMatchedRule", + "AIGuardScfwOutcome", + "AIGuardScfwPackageFinding", + "AIGuardSdsFinding", + "AIGuardSdsFindingLocation", + "AIGuardToolCall", + "AIGuardToolCallFunction", + "AIGuardToolPolicyAction", "AIImpactUserActivityAttributes", "AIImpactUserActivityData", "AIImpactUserActivityRequest", diff --git a/tests/generated-test/test-runner-data/manifest.json b/tests/generated-test/test-runner-data/manifest.json index 16919a3dbb..65160f74c1 100644 --- a/tests/generated-test/test-runner-data/manifest.json +++ b/tests/generated-test/test-runner-data/manifest.json @@ -2793,6 +2793,55 @@ "scenario": "Update GCP scan options returns \"OK\" response", "version": "v2" }, + { + "feature": "AI Guard", + "feature_file": "../../v2/features/ai_guard.feature", + "file": "v2/ai-guard/evaluate-an-ai-guard-request-rejects-empty-multipart-user-content.json", + "scenario": "Evaluate an AI Guard request rejects empty multipart user content", + "version": "v2" + }, + { + "feature": "AI Guard", + "feature_file": "../../v2/features/ai_guard.feature", + "file": "v2/ai-guard/evaluate-an-ai-guard-request-rejects-image-parts-without-a-url.json", + "scenario": "Evaluate an AI Guard request rejects image parts without a URL", + "version": "v2" + }, + { + "feature": "AI Guard", + "feature_file": "../../v2/features/ai_guard.feature", + "file": "v2/ai-guard/evaluate-an-ai-guard-request-rejects-text-parts-without-text.json", + "scenario": "Evaluate an AI Guard request rejects text parts without text", + "version": "v2" + }, + { + "feature": "AI Guard", + "feature_file": "../../v2/features/ai_guard.feature", + "file": "v2/ai-guard/evaluate-an-ai-guard-request-returns-bad-request-response.json", + "scenario": "Evaluate an AI Guard request returns \"Bad Request\" response", + "version": "v2" + }, + { + "feature": "AI Guard", + "feature_file": "../../v2/features/ai_guard.feature", + "file": "v2/ai-guard/evaluate-an-ai-guard-request-returns-ok-response.json", + "scenario": "Evaluate an AI Guard request returns \"OK\" response", + "version": "v2" + }, + { + "feature": "AI Guard", + "feature_file": "../../v2/features/ai_guard.feature", + "file": "v2/ai-guard/evaluate-an-ai-guard-request-with-a-numeric-confidence-threshold.json", + "scenario": "Evaluate an AI Guard request with a numeric confidence threshold", + "version": "v2" + }, + { + "feature": "AI Guard", + "feature_file": "../../v2/features/ai_guard.feature", + "file": "v2/ai-guard/evaluate-an-ai-guard-request-with-per-category-confidence-thresholds.json", + "scenario": "Evaluate an AI Guard request with per-category confidence thresholds", + "version": "v2" + }, { "feature": "Annotations", "feature_file": "../../v2/features/annotations.feature", diff --git a/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-empty-multipart-user-content.json b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-empty-multipart-user-content.json new file mode 100644 index 0000000000..b9b5a8a692 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-empty-multipart-user-content.json @@ -0,0 +1,38 @@ +{ + "api": "AIGuard", + "expected_status": 400, + "feature": "AI Guard", + "id": "v2/AI Guard/Evaluate an AI Guard request rejects empty multipart user content", + "operation_id": "EvaluateAIGuardRequest", + "request": { + "body": { + "schema": { + "format": null, + "ref": "AIGuardEvaluateRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": [], + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "pagination": false, + "parameters": [], + "path": "/api/v2/ai-guard/evaluate" + }, + "scenario": "Evaluate an AI Guard request rejects empty multipart user content", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-image-parts-without-a-url.json b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-image-parts-without-a-url.json new file mode 100644 index 0000000000..b756f693b0 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-image-parts-without-a-url.json @@ -0,0 +1,45 @@ +{ + "api": "AIGuard", + "expected_status": 400, + "feature": "AI Guard", + "id": "v2/AI Guard/Evaluate an AI Guard request rejects image parts without a URL", + "operation_id": "EvaluateAIGuardRequest", + "request": { + "body": { + "schema": { + "format": null, + "ref": "AIGuardEvaluateRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": [ + { + "image_url": { + "url": "" + }, + "type": "image_url" + } + ], + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "pagination": false, + "parameters": [], + "path": "/api/v2/ai-guard/evaluate" + }, + "scenario": "Evaluate an AI Guard request rejects image parts without a URL", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-text-parts-without-text.json b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-text-parts-without-text.json new file mode 100644 index 0000000000..19f085b53e --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-rejects-text-parts-without-text.json @@ -0,0 +1,42 @@ +{ + "api": "AIGuard", + "expected_status": 400, + "feature": "AI Guard", + "id": "v2/AI Guard/Evaluate an AI Guard request rejects text parts without text", + "operation_id": "EvaluateAIGuardRequest", + "request": { + "body": { + "schema": { + "format": null, + "ref": "AIGuardEvaluateRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": [ + { + "type": "text" + } + ], + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "pagination": false, + "parameters": [], + "path": "/api/v2/ai-guard/evaluate" + }, + "scenario": "Evaluate an AI Guard request rejects text parts without text", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-returns-bad-request-response.json b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-returns-bad-request-response.json new file mode 100644 index 0000000000..120e864d70 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-returns-bad-request-response.json @@ -0,0 +1,37 @@ +{ + "api": "AIGuard", + "expected_status": 400, + "feature": "AI Guard", + "id": "v2/AI Guard/Evaluate an AI Guard request returns \"Bad Request\" response", + "operation_id": "EvaluateAIGuardRequest", + "request": { + "body": { + "schema": { + "format": null, + "ref": "AIGuardEvaluateRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "messages": [ + { + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "pagination": false, + "parameters": [], + "path": "/api/v2/ai-guard/evaluate" + }, + "scenario": "Evaluate an AI Guard request returns \"Bad Request\" response", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-returns-ok-response.json b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-returns-ok-response.json new file mode 100644 index 0000000000..b28a509acd --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-returns-ok-response.json @@ -0,0 +1,41 @@ +{ + "api": "AIGuard", + "expected_status": 200, + "feature": "AI Guard", + "id": "v2/AI Guard/Evaluate an AI Guard request returns \"OK\" response", + "operation_id": "EvaluateAIGuardRequest", + "request": { + "body": { + "schema": { + "format": null, + "ref": "AIGuardEvaluateRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": "Hello world!", + "role": "user" + } + ], + "meta": { + "confidence_threshold": 0.8 + } + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "pagination": false, + "parameters": [], + "path": "/api/v2/ai-guard/evaluate" + }, + "scenario": "Evaluate an AI Guard request returns \"OK\" response", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-with-a-numeric-confidence-threshold.json b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-with-a-numeric-confidence-threshold.json new file mode 100644 index 0000000000..e59b768e8d --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-with-a-numeric-confidence-threshold.json @@ -0,0 +1,41 @@ +{ + "api": "AIGuard", + "expected_status": 200, + "feature": "AI Guard", + "id": "v2/AI Guard/Evaluate an AI Guard request with a numeric confidence threshold", + "operation_id": "EvaluateAIGuardRequest", + "request": { + "body": { + "schema": { + "format": null, + "ref": "AIGuardEvaluateRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": "Hello world!", + "role": "user" + } + ], + "meta": { + "confidence_threshold": 0.8 + } + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "pagination": false, + "parameters": [], + "path": "/api/v2/ai-guard/evaluate" + }, + "scenario": "Evaluate an AI Guard request with a numeric confidence threshold", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-with-per-category-confidence-thresholds.json b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-with-per-category-confidence-thresholds.json new file mode 100644 index 0000000000..1c7389f696 --- /dev/null +++ b/tests/generated-test/test-runner-data/v2/ai-guard/evaluate-an-ai-guard-request-with-per-category-confidence-thresholds.json @@ -0,0 +1,45 @@ +{ + "api": "AIGuard", + "expected_status": 200, + "feature": "AI Guard", + "id": "v2/AI Guard/Evaluate an AI Guard request with per-category confidence thresholds", + "operation_id": "EvaluateAIGuardRequest", + "request": { + "body": { + "schema": { + "format": null, + "ref": "AIGuardEvaluateRequest", + "type": "object" + }, + "source": "inline", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": "Hello world!", + "role": "user" + } + ], + "meta": { + "confidence_threshold": { + "flagging_global": 0.6, + "indirect-prompt-injection": 0.4, + "policy-evasion": 0.9 + } + } + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "pagination": false, + "parameters": [], + "path": "/api/v2/ai-guard/evaluate" + }, + "scenario": "Evaluate an AI Guard request with per-category confidence thresholds", + "schema_version": 1, + "version": "v2" +} diff --git a/tests/generated-test/test-server-data/manifest.json b/tests/generated-test/test-server-data/manifest.json index b57e8c66bc..563b35eb13 100644 --- a/tests/generated-test/test-server-data/manifest.json +++ b/tests/generated-test/test-server-data/manifest.json @@ -115,6 +115,11 @@ "file": "v1/webhooks-integration.json", "version": "v1" }, + { + "feature": "AI Guard", + "file": "v2/ai-guard.json", + "version": "v2" + }, { "feature": "APM Retention Filters", "file": "v2/apm-retention-filters.json", diff --git a/tests/generated-test/test-server-data/v2/ai-guard.json b/tests/generated-test/test-server-data/v2/ai-guard.json new file mode 100644 index 0000000000..269a7e0d2b --- /dev/null +++ b/tests/generated-test/test-server-data/v2/ai-guard.json @@ -0,0 +1,331 @@ +{ + "feature": "AI Guard", + "recordings": [ + { + "feature": "AI Guard", + "frozen_at": "2026-10-26T11:41:08.575Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": [], + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "path": "/api/v2/ai-guard/evaluate", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"title\":\"Generic Error\",\"detail\":\"content parts cannot be empty array for user messages\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Evaluate an AI Guard request rejects empty multipart user content", + "version": "v2" + }, + { + "feature": "AI Guard", + "frozen_at": "2026-10-26T11:41:08.682Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": [ + { + "image_url": { + "url": "" + }, + "type": "image_url" + } + ], + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "path": "/api/v2/ai-guard/evaluate", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"title\":\"Generic Error\",\"detail\":\"content part 0: image_url is required when type is 'image_url'\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Evaluate an AI Guard request rejects image parts without a URL", + "version": "v2" + }, + { + "feature": "AI Guard", + "frozen_at": "2026-10-26T11:41:08.732Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": [ + { + "type": "text" + } + ], + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "path": "/api/v2/ai-guard/evaluate", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"title\":\"Generic Error\",\"detail\":\"content part 0: text is required when type is 'text'\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Evaluate an AI Guard request rejects text parts without text", + "version": "v2" + }, + { + "feature": "AI Guard", + "frozen_at": "2026-10-26T11:41:08.777Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "messages": [ + { + "role": "user" + } + ] + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "path": "/api/v2/ai-guard/evaluate", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"errors\":[{\"title\":\"Generic Error\",\"detail\":\"content is required for user messages\"}]}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "Bad Request", + "status": 400 + } + } + ], + "scenario": "Evaluate an AI Guard request returns \"Bad Request\" response", + "version": "v2" + }, + { + "feature": "AI Guard", + "frozen_at": "2026-10-26T11:41:08.830Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": "Hello world!", + "role": "user" + } + ], + "meta": { + "confidence_threshold": 0.8 + } + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "path": "/api/v2/ai-guard/evaluate", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"a76a703c-324f-42ed-af33-9b17d65e28af\",\"type\":\"evaluations\",\"attributes\":{\"action\":\"ALLOW\",\"global_prob\":0.005218505859375,\"is_blocking_enabled\":false,\"reason\":\"No rule match.\",\"tag_probs\":null,\"tags\":[]}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Evaluate an AI Guard request returns \"OK\" response", + "version": "v2" + }, + { + "feature": "AI Guard", + "frozen_at": "2026-10-26T11:41:08.905Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": "Hello world!", + "role": "user" + } + ], + "meta": { + "confidence_threshold": 0.8 + } + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "path": "/api/v2/ai-guard/evaluate", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"645e547e-1430-41df-a7e5-7cee63a8110e\",\"type\":\"evaluations\",\"attributes\":{\"action\":\"ALLOW\",\"global_prob\":0.005218505859375,\"is_blocking_enabled\":false,\"reason\":\"No rule match.\",\"tag_probs\":null,\"tags\":[]}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Evaluate an AI Guard request with a numeric confidence threshold", + "version": "v2" + }, + { + "feature": "AI Guard", + "frozen_at": "2026-10-26T11:41:08.982Z", + "interactions": [ + { + "request": { + "body": { + "type": "json", + "value": { + "data": { + "attributes": { + "messages": [ + { + "content": "Hello world!", + "role": "user" + } + ], + "meta": { + "confidence_threshold": { + "flagging_global": 0.6, + "indirect-prompt-injection": 0.4, + "policy-evasion": 0.9 + } + } + }, + "type": "evaluations" + } + } + }, + "content_type": "application/json", + "method": "POST", + "path": "/api/v2/ai-guard/evaluate", + "query": [] + }, + "response": { + "body": { + "encoding": "text", + "value": "{\"data\":{\"id\":\"aeeb0cb1-32e2-4efa-82d9-3d8ce357ec72\",\"type\":\"evaluations\",\"attributes\":{\"action\":\"ALLOW\",\"global_prob\":0.005218505859375,\"is_blocking_enabled\":false,\"reason\":\"No rule match.\",\"tag_probs\":null,\"tags\":[]}}}" + }, + "headers": { + "content-type": "application/vnd.api+json" + }, + "reason": "OK", + "status": 200 + } + } + ], + "scenario": "Evaluate an AI Guard request with per-category confidence thresholds", + "version": "v2" + } + ], + "schema_version": 1, + "version": "v2" +} diff --git a/tests/v2/features/ai_guard.feature b/tests/v2/features/ai_guard.feature new file mode 100644 index 0000000000..b205d90c09 --- /dev/null +++ b/tests/v2/features/ai_guard.feature @@ -0,0 +1,71 @@ +@endpoint(ai-guard) @endpoint(ai-guard-v2) +Feature: AI Guard + Evaluate LLM interactions for security threats such as prompt injection + and jailbreak attempts. + + Background: + Given a valid "apiKeyAuth" key in the system + And a valid "appKeyAuth" key in the system + And an instance of "AIGuard" API + And operation "EvaluateAIGuardRequest" enabled + And new "EvaluateAIGuardRequest" request + + @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request rejects empty multipart user content + Given body with value {"data":{"type":"evaluations","attributes":{"messages":[{"role":"user","content":[]}]}}} + When the request is sent + Then the response status is 400 Bad Request + + @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request rejects image parts without a URL + Given body with value {"data":{"type":"evaluations","attributes":{"messages":[{"role":"user","content":[{"type":"image_url","image_url":{"url":""}}]}]}}} + When the request is sent + Then the response status is 400 Bad Request + + @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request rejects text parts without text + Given body with value {"data":{"type":"evaluations","attributes":{"messages":[{"role":"user","content":[{"type":"text"}]}]}}} + When the request is sent + Then the response status is 400 Bad Request + + @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request returns "Bad Request" response + Given body with value {"data":{"type":"evaluations","attributes":{"messages":[{"role":"user"}]}}} + When the request is sent + Then the response status is 400 Bad Request + + @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request returns "OK" response + Given body with value {"data": {"type": "evaluations", "attributes": {"messages": [{"content": "Hello world!", "role": "user"}], "meta": {"confidence_threshold": 0.8}}}} + When the request is sent + Then the response status is 200 OK + + @generated @skip @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request returns "Payload Too Large" response + Given body with value {"data": {"attributes": {"messages": [{"content": "Hello world!", "role": "user"}], "meta": {"confidence_threshold": 0.8}}, "type": "evaluations"}} + When the request is sent + Then the response status is 413 Payload Too Large + + @generated @skip @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request returns "Request Timeout" response + Given body with value {"data": {"attributes": {"messages": [{"content": "Hello world!", "role": "user"}], "meta": {"confidence_threshold": 0.8}}, "type": "evaluations"}} + When the request is sent + Then the response status is 408 Request Timeout + + @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request with a numeric confidence threshold + Given body with value {"data":{"type":"evaluations","attributes":{"messages":[{"role":"user","content":"Hello world!"}],"meta":{"confidence_threshold":0.8}}}} + When the request is sent + Then the response status is 200 OK + And the response "data.type" is equal to "evaluations" + And the response "data.attributes" has field "action" + And the response "data.attributes" has field "is_blocking_enabled" + + @team:ddoghq/k9-ai-guard + Scenario: Evaluate an AI Guard request with per-category confidence thresholds + Given body with value {"data":{"type":"evaluations","attributes":{"messages":[{"role":"user","content":"Hello world!"}],"meta":{"confidence_threshold":{"flagging_global":0.6,"policy-evasion":0.9,"indirect-prompt-injection":0.4}}}}} + When the request is sent + Then the response status is 200 OK + And the response "data.type" is equal to "evaluations" + And the response "data.attributes" has field "action" + And the response "data.attributes" has field "is_blocking_enabled" diff --git a/tests/v2/features/undo.json b/tests/v2/features/undo.json index 45b82eb3cd..c644e09b57 100644 --- a/tests/v2/features/undo.json +++ b/tests/v2/features/undo.json @@ -374,6 +374,12 @@ "type": "safe" } }, + "EvaluateAIGuardRequest": { + "tag": "AI Guard", + "undo": { + "type": "safe" + } + }, "CreateAIImpactUserActivity": { "tag": "Delivery Performance", "undo": {