diff --git a/README.md b/README.md index 4b3ecd7..6b0b9d2 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,24 @@ client.update_agent_definition("agent_id", { "change_id": definition["change_id"], "steps": [{"type": "llm", "config": {}}], }) + +# Export / import an agent +exported = client.export_agent("agent_id") + +# Validate the payload first to surface unresolved entity refs in this account +preview = client.preview_import_agent({"agent_definition": exported}) +entity_remap = { + ref["ref_id"]: "" # pick a target uuid from ref["alternatives"] + for ref in preview.get("unresolved_refs", []) +} + +# Commit — `entity_remap` substitutes workflow refs before save +imported = client.create_agent({ + "name": "Imported", + "agent_definition": exported, + "entity_remap": entity_remap, +}) +# `imported["import_warnings"]` lists any items that couldn't be applied. ``` ### Agent runs diff --git a/openapi/seclai.openapi.json b/openapi/seclai.openapi.json index ecc90b5..16b3434 100644 --- a/openapi/seclai.openapi.json +++ b/openapi/seclai.openapi.json @@ -52,6 +52,39 @@ "title": "AddConversationTurnRequest", "type": "object" }, + "AgentDefinitionImportErrorResponse": { + "description": "422 body for invalid `agent_definition` payloads.\n\nMirrors :py:meth:`AgentDefinitionImportError.to_response_dict`.", + "properties": { + "error": { + "default": "invalid_agent_definition", + "title": "Error", + "type": "string" + }, + "errors": { + "items": { + "$ref": "#/components/schemas/ImportFieldErrorModel" + }, + "title": "Errors", + "type": "array" + }, + "message": { + "title": "Message", + "type": "string" + }, + "source": { + "description": "Canonical pretty-printed echo of the supplied payload \u2014 error line/column refer to this string.", + "title": "Source", + "type": "string" + } + }, + "required": [ + "message", + "errors", + "source" + ], + "title": "AgentDefinitionImportErrorResponse", + "type": "object" + }, "AgentDefinitionResponse": { "properties": { "change_id": { @@ -61,7 +94,7 @@ }, "definition": { "additionalProperties": true, - "description": "The agent definition containing name, description, tags, and step workflow tree. Step types include prompt_call, retrieval, transform, gate, retry, evaluate_step, insight, extract_content, streaming_result, send_email, webhook_call, call_agent, write_metadata, write_content_attachment, load_content_attachment, load_content, display_result, and others.", + "description": "The agent definition containing name, description, tags, and step workflow tree. Step types include prompt_call, retrieval, regex_replace, gate, retry, evaluate_step, extract_data, extract_content, add_chat_turn, load_chat_history, add_memory, search_memory, load_memory, streaming_result, send_email, webhook_call, call_agent, write_metadata, write_content_attachment, load_content_attachment, load_content, display_result, merge, for_each, and others.", "title": "Definition", "type": "object" }, @@ -342,6 +375,14 @@ "title": "Attempts", "type": "array" }, + "blocked_policies": { + "description": "Governance policies that produced at least one BLOCK verdict during this run. Deduplicated by policy id.", + "items": { + "$ref": "#/components/schemas/routers__api__agents__GovernancePolicyRefResponse" + }, + "title": "Blocked Policies", + "type": "array" + }, "credits": { "anyOf": [ { @@ -359,6 +400,38 @@ "title": "Error Count", "type": "integer" }, + "flagged_policies": { + "description": "Governance policies that produced at least one FLAG verdict during this run. Deduplicated by policy id.", + "items": { + "$ref": "#/components/schemas/routers__api__agents__GovernancePolicyRefResponse" + }, + "title": "Flagged Policies", + "type": "array" + }, + "governance_input_status": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Result of the governance input evaluation: safe, blocked, skipped, or timed_out.", + "title": "Governance Input Status" + }, + "governance_input_wait_ms": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Milliseconds spent waiting for governance input evaluation.", + "title": "Governance Input Wait Ms" + }, "input": { "anyOf": [ { @@ -371,6 +444,18 @@ "description": "Input provided to the agent for this run.", "title": "Input" }, + "input_scan_status": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Result of the prompt injection scan: safe, unsafe, skipped, timed_out, or error.", + "title": "Input Scan Status" + }, "output": { "anyOf": [ { @@ -393,6 +478,18 @@ "title": "Run Id", "type": "string" }, + "scan_wait_ms": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Milliseconds spent waiting for prompt injection scan.", + "title": "Scan Wait Ms" + }, "status": { "$ref": "#/components/schemas/PendingProcessingCompletedFailedStatus", "description": "Current status of the agent run." @@ -623,6 +720,21 @@ "title": "Id", "type": "string" }, + "import_warnings": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ImportSkipResponse" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "One entry per item dropped or substituted during import. Present only on endpoints that accept agent_definition; null on non-import calls; [] when the import had no skips.", + "title": "Import Warnings" + }, "max_retries": { "default": 3, "description": "Max retries for eval_and_retry mode.", @@ -2964,7 +3076,7 @@ "title": "Step Id" }, "step_type": { - "description": "The step type to generate config for (e.g. 'transform', 'gate', 'text', 'prompt_call', 'retrieval').", + "description": "The step type to generate config for (e.g. 'regex_replace', 'gate', 'text', 'prompt_call', 'retrieval').", "title": "Step Type", "type": "string" }, @@ -3145,6 +3257,66 @@ "title": "HTTPValidationError", "type": "object" }, + "ImportFieldErrorModel": { + "description": "Single agent_definition validation error with source position.", + "properties": { + "column": { + "description": "1-indexed column in `source`.", + "title": "Column", + "type": "integer" + }, + "line": { + "description": "1-indexed line in `source`.", + "title": "Line", + "type": "integer" + }, + "message": { + "description": "Human-readable description of the problem.", + "title": "Message", + "type": "string" + }, + "path": { + "description": "Dotted path of the offending field, e.g. `agent.definition.child_steps[0].step_type`.", + "title": "Path", + "type": "string" + } + }, + "required": [ + "line", + "column", + "path", + "message" + ], + "title": "ImportFieldErrorModel", + "type": "object" + }, + "ImportSkipResponse": { + "description": "One item that was not applied during an agent import.\n\nUsed as the element type for ``import_warnings`` on every\nresponse model that accepts an ``agent_definition`` payload.\nSee :py:class:`services.agent_definition_import.AgentImportSkip`\nfor the full category list.\n\nLives here (not on each router) so the authenticated and public\nAPI responses share one definition \u2014 keeping the shape that\nclients (UI modal, MCP, OpenAPI consumers) depend on aligned.", + "properties": { + "category": { + "description": "The kind of item that was skipped or substituted: 'schedule', 'evaluation_criteria', 'alert_config', 'alert_recipient', 'governance_policy', 'governance_kb_link', 'solution_link'.", + "title": "Category", + "type": "string" + }, + "details": { + "additionalProperties": true, + "description": "Category-specific identifiers for the skipped item (step_id, alert_type, kb_name, etc.). Stable keys per category; absent keys are simply not applicable.", + "title": "Details", + "type": "object" + }, + "message": { + "description": "Human-readable explanation of what was skipped and why.", + "title": "Message", + "type": "string" + } + }, + "required": [ + "category", + "message" + ], + "title": "ImportSkipResponse", + "type": "object" + }, "InlineTextReplaceRequest": { "description": "Request model for inline text content replacement.", "properties": { @@ -4162,6 +4334,19 @@ "description": "Parameters for the action.", "title": "Params", "type": "object" + }, + "preview": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Planning-time dry-run preview attached by the solution AI assistant for create_agent / update_agent actions. Contains ``steps`` (the generated step tree), ``step_count``, ``warnings`` (a mix of heuristic structural issues \u2014 e.g. brittle JSONPath, pass-through ``regex_replace``, ``prompt_call`` missing a model \u2014 and deterministic resource-usage issues: every pre-bound knowledge base / memory bank must be referenced by at least one step, and no step may reference an unknown id), and ``skipped`` / ``skipped_reason`` when preview couldn't run (e.g. the action depends on resources created earlier in the same plan). ``None`` for non-agent actions or when generation failed.", + "title": "Preview" } }, "required": [ @@ -5853,6 +6038,126 @@ "title": "VariantOptionResponse", "type": "object" }, + "routers__api__agents__AgentImportPreviewRequest": { + "description": "Dry-run import request \u2014 same payload shape as the export endpoint.", + "properties": { + "agent_definition": { + "additionalProperties": true, + "description": "Payload in the same shape as GET /api/agents/{agent_id}/export.", + "title": "Agent Definition", + "type": "object" + } + }, + "required": [ + "agent_definition" + ], + "title": "AgentImportPreviewRequest", + "type": "object" + }, + "routers__api__agents__AgentImportPreviewResponse": { + "description": "Summary of a successfully validated import payload (no DB writes).\n\nCounts are derived from the validated payload as supplied \u2014 they\nreflect what was requested, not what would eventually be persisted\n(cross-account skips for recipients and KB names happen later, only\non commit).", + "properties": { + "agent_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Imported agent name, if any.", + "title": "Agent Name" + }, + "alert_configs": { + "description": "Number of alert configs in the payload.", + "title": "Alert Configs", + "type": "integer" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Imported agent description, if any.", + "title": "Description" + }, + "evaluation_criteria": { + "description": "Number of evaluation criteria in the payload.", + "title": "Evaluation Criteria", + "type": "integer" + }, + "governance_policies": { + "description": "Number of agent-scoped governance policies in the payload.", + "title": "Governance Policies", + "type": "integer" + }, + "ok": { + "description": "Always true on a 200 response; failures use HTTP 422.", + "title": "Ok", + "type": "boolean" + }, + "payload_export_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Export-format version the payload claims (or ``null`` for legacy payloads). When this differs from ``supported_export_version``, fields may have been silently dropped or defaulted on import.", + "title": "Payload Export Version" + }, + "schedules": { + "description": "Number of trigger schedules in the payload.", + "title": "Schedules", + "type": "integer" + }, + "solutions": { + "default": 0, + "description": "Number of solutions the source agent belonged to. On import these are matched by name in the target account; unmatched names are silently skipped.", + "title": "Solutions", + "type": "integer" + }, + "step_count": { + "description": "Total number of steps in the workflow tree (recursive).", + "title": "Step Count", + "type": "integer" + }, + "supported_export_version": { + "default": "2", + "description": "Export-format version this server understands. Compare against ``payload_export_version`` to detect cross-version imports.", + "title": "Supported Export Version", + "type": "string" + }, + "unresolved_refs": { + "description": "Entity references in the imported workflow that don't exist in the target account. Each entry: {category, ref_id, ref_name?, locations:[step:], alternatives:[{id, name, description?}]}. Pass {source_uuid: target_uuid} as ``entity_remap`` on the create/update call to substitute these references before save.", + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Unresolved Refs", + "type": "array" + } + }, + "required": [ + "ok", + "agent_name", + "description", + "step_count", + "schedules", + "alert_configs", + "evaluation_criteria", + "governance_policies" + ], + "title": "AgentImportPreviewResponse", + "type": "object" + }, "routers__api__agents__AgentListResponse": { "properties": { "data": { @@ -5955,6 +6260,19 @@ }, "routers__api__agents__CreateAgentRequest": { "properties": { + "agent_definition": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Optional payload in the same format produced by GET /agents/{id}/export. When provided, replaces any template-derived workflow and pre-fills metadata/trigger fields the request does not specify explicitly. Validation errors include line/column references against a canonical pretty-printed echo of the supplied payload.", + "title": "Agent Definition" + }, "agent_template": { "anyOf": [ { @@ -5979,6 +6297,21 @@ "description": "Optional description.", "title": "Description" }, + "entity_remap": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Optional UUID-substitution map applied to the imported workflow before save. Each key is a source-account UUID (as returned by /agents/preview-import's ``unresolved_refs``); each value is the target-account UUID to substitute. Used to relink knowledge bases, memory banks, source connections, and sub-agents on cross-account imports.", + "title": "Entity Remap" + }, "name": { "description": "Name for the new agent.", "title": "Name", @@ -5997,8 +6330,48 @@ "title": "CreateAgentRequest", "type": "object" }, + "routers__api__agents__GovernancePolicyRefResponse": { + "description": "Reference to a governance policy by id and name.", + "properties": { + "policy_id": { + "description": "Governance policy identifier.", + "title": "Policy Id", + "type": "string" + }, + "policy_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Display name of the policy at evaluation time. May be null when the policy has been deleted.", + "title": "Policy Name" + } + }, + "required": [ + "policy_id" + ], + "title": "GovernancePolicyRefResponse", + "type": "object" + }, "routers__api__agents__UpdateAgentRequest": { "properties": { + "agent_definition": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Optional payload in the same format produced by GET /agents/{id}/export. When provided, agent metadata fields the request does not set explicitly are taken from the payload, and the agent's workflow is replaced from `agent.definition`. The previous version is preserved in history. Validation errors include line/column references against a canonical pretty-printed echo of the supplied payload.", + "title": "Agent Definition" + }, "default_evaluation_tier": { "anyOf": [ { @@ -6023,6 +6396,21 @@ "description": "New description for the agent.", "title": "Description" }, + "entity_remap": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Optional UUID-substitution map applied to the imported workflow before save (same shape as on POST /agents).", + "title": "Entity Remap" + }, "evaluation_mode": { "anyOf": [ { @@ -8079,7 +8467,7 @@ ] }, "post": { - "description": "Create a new agent.\n\nTrigger types:\n- `dynamic_input`: triggered via API with user-provided input\n- `template_input`: triggered via API with a predefined template\n- `schedule`: triggered on a schedule\n- `new_content`: triggered when new content arrives\n\nTemplates: `blank`, `retrieval_example`, `simple_qa`, `summarizer`, `json_extractor`, `content_change_notifier`, `scheduled_report`, `webhook_pipeline`\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. Agent is created in the caller's account.", + "description": "Create a new agent.\n\nTrigger types:\n- `dynamic_input`: triggered via API with user-provided input\n- `template_input`: triggered via API with a predefined template\n- `schedule`: triggered on a schedule\n- `new_content`: triggered when new content arrives\n\nTemplates: `blank`, `retrieval_example`, `simple_qa`, `summarizer`, `json_extractor`, `content_change_notifier`, `scheduled_report`, `webhook_pipeline`\n\nImporting an existing agent:\n- Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. The full extras suite (alert_configs, evaluation_criteria, governance_policies, schedules, solutions) is applied; items that don't resolve in this account are reported in the response's `import_warnings` array.\n- Use `POST /agents/preview-import` first to surface `unresolved_refs` (workflow refs to KBs, memory banks, source connections, sub-agents that don't exist here). Then pass `entity_remap: {source_uuid: target_uuid}` on this call to substitute them before save.\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. Agent is created in the caller's account.", "operationId": "create_agent_api_agents_post", "parameters": [ { @@ -8114,11 +8502,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HTTPValidationError" + "$ref": "#/components/schemas/AgentDefinitionImportErrorResponse" } } }, - "description": "Validation Error" + "description": "The supplied `agent_definition` payload failed validation. The body lists each error with a 1-indexed line/column pointing into the canonical pretty-printed echo of the payload (also returned in `source`)." } }, "summary": "Create an agent", @@ -8658,6 +9046,53 @@ ] } }, + "/agents/preview-import": { + "post": { + "description": "Validate an `agent_definition` payload (the same shape produced by `GET /api/agents/{agent_id}/export`) without creating or modifying any agent. On success returns a summary the client can show before commit (counts of steps, schedules, alert configs, evaluation criteria, governance policies). On failure returns the same 422 body shape used by `POST /api/agents` and `PUT /api/agents/{id}` so callers can render line/column-anchored errors.\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. No DB writes.", + "operationId": "preview_import_agent_api_agents_preview_import_post", + "parameters": [ + { + "$ref": "#/components/parameters/X-Account-Id" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/routers__api__agents__AgentImportPreviewRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/routers__api__agents__AgentImportPreviewResponse" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AgentDefinitionImportErrorResponse" + } + } + }, + "description": "The supplied `agent_definition` payload failed validation. The body lists each error with a 1-indexed line/column pointing into the canonical pretty-printed echo of the payload (also returned in `source`)." + } + }, + "summary": "Preview an agent_definition import", + "tags": [ + "agents" + ] + } + }, "/agents/runs/search": { "post": { "description": "Search agent traces using semantic similarity.\n\nFinds step-run outputs that are most semantically similar to the query.\nResults include the matching text, agent/step metadata, and a similarity score.\n\nAgent traces are automatically indexed when runs complete. The first 7 days of storage are free; extended retention is billed.\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. Searches only within your account's traces.", @@ -8890,7 +9325,7 @@ ] }, "put": { - "description": "Update an agent's name, description, evaluation settings, and model lifecycle settings.\n\nEvaluation settings: `evaluation_mode` ('output_expectation', 'eval_and_retry', 'sample_and_flag'), `default_evaluation_tier` ('fast', 'balanced', 'thorough'), `max_retries`, `retry_on_failure`, `sampling_config`.\n\nModel lifecycle settings: `prompt_model_auto_upgrade_strategy` ('none', 'early_adopter', 'middle_of_road', 'cautious_adopter'), `prompt_model_auto_rollback_enabled`, `prompt_model_auto_rollback_triggers` (list of 'agent_eval_fail', 'governance_flag', 'governance_block', 'agent_run_failed').\n\nAt least one field must be provided.\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. You can only update agents belonging to your account.", + "description": "Update an agent's name, description, evaluation settings, and model lifecycle settings.\n\nEvaluation settings: `evaluation_mode` ('output_expectation', 'eval_and_retry', 'sample_and_flag'), `default_evaluation_tier` ('fast', 'balanced', 'thorough'), `max_retries`, `retry_on_failure`, `sampling_config`.\n\nModel lifecycle settings: `prompt_model_auto_upgrade_strategy` ('none', 'early_adopter', 'middle_of_road', 'cautious_adopter'), `prompt_model_auto_rollback_enabled`, `prompt_model_auto_rollback_triggers` (list of 'agent_eval_fail', 'governance_flag', 'governance_block', 'agent_run_failed').\n\nReplacing the workflow from an export:\n- Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. Update only touches the workflow + agent metadata \u2014 alert_configs, evaluation_criteria, governance_policies, schedules, and solution links from the imported file are NOT applied (use the dedicated endpoints for those, or `POST /agents` to import as a new agent).\n- `entity_remap: {source_uuid: target_uuid}` substitutes workflow entity refs before save (same shape as `POST /agents`).\n\nAt least one field must be provided.\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. You can only update agents belonging to your account.", "operationId": "update_agent_api_agents__agent_id__put", "parameters": [ { @@ -8931,11 +9366,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HTTPValidationError" + "$ref": "#/components/schemas/AgentDefinitionImportErrorResponse" } } }, - "description": "Validation Error" + "description": "The supplied `agent_definition` payload failed validation. The body lists each error with a 1-indexed line/column pointing into the canonical pretty-printed echo of the payload (also returned in `source`)." } }, "summary": "Update agent metadata", @@ -9229,7 +9664,7 @@ }, "/agents/{agent_id}/definition": { "get": { - "description": "Fetch the current agent definition from the main branch.\n\nThe response includes `change_id` which must be provided when updating the definition (optimistic locking).\n\nThe definition contains the agent's step workflow. Available step types:\n- `prompt_call`: Call an LLM with a prompt template\n- `retrieval`: Search a knowledge base\n- `transform`: Reshape data with a Liquid template\n- `gate`: Evaluate conditions, stop or continue child execution\n- `retry`: Re-execute from a target ancestor step (for quality-control loops; pair with a `gate` step for conditional retrying. Fields: `target_step_id` (ancestor step ID), `max_retries` (1\u201310))\n- `evaluate_step`: Score a selected previous step output and emit JSON with `score`, `passed`, and `pass_threshold` (fields: `target_step_id`, `evaluation_prompt`, `pass_threshold`, optional `evaluation_tier`, optional `expectation_config`)\n- `insight`: Progressively read and analyze large input\n- `extract_content`: Extract structured data (JSON, HTML, XML)\n- `send_email`: Send email with step output\n- `webhook_call`: POST data to an external URL\n- `write_aws_s3_object`: Write output to S3\n- `call_agent`: Invoke another agent\n- `write_metadata`: Write a value to content metadata (for filtering/gates; content-triggered agents only. Fields: `metadata_key`, `content`)\n- `write_content_attachment`: Write a file-backed attachment to content (optionally indexed for retrieval; content-triggered agents only. Fields: `attachment_key`, `content`, `content_type`, `indexed`)\n- `load_content_attachment`: Load a previously written attachment (content-triggered agents only. Fields: `attachment_key`)\n- `load_content`: Load the full text body of a source document (typically used with content-triggered agents; can also load by explicit `content_version_id`. Fields: `content_version_id` optional)\n- `streaming_result`: Stream LLM tokens in real-time via SSE (must be a direct child of `prompt_call`; requires `dynamic_input` trigger; `priority: true` enables real-time streaming)\n- `display_result`: Show output to the user\n- `join`: Merge parallel branches\n- `combinator`: Combine multiple inputs\n- `text`: Static text literal\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. You can only access agents belonging to your account.", + "description": "Fetch the current agent definition from the main branch.\n\nThe response includes `change_id` which must be provided when updating the definition (optimistic locking).\n\nThe definition contains the agent's step workflow. Available step types:\n- `prompt_call`: Call an LLM with a prompt template\n- `retrieval`: Search a knowledge base\n- `regex_replace`: Reshape text via ordered regex find/replace rules\n- `gate`: Evaluate conditions, stop or continue child execution\n- `retry`: Re-execute from a target ancestor step (for quality-control loops; pair with a `gate` step for conditional retrying. Fields: `target_step_id` (ancestor step ID), `max_retries` (1\u201310))\n- `evaluate_step`: Score a selected previous step output and emit JSON with `score`, `passed`, and `pass_threshold` (fields: `target_step_id`, `evaluation_prompt`, `pass_threshold`, optional `evaluation_tier`, optional `expectation_config`)\n- `extract_data`: Progressively read and analyze large input\n- `extract_content`: Extract structured data (JSON, HTML, XML)\n- `add_chat_turn` / `load_chat_history`: Record a turn or load running history from a conversation memory bank\n- `add_memory` / `search_memory` / `load_memory`: Write, semantic-search, or load entries on a general memory bank\n- `send_email`: Send email with step output\n- `webhook_call`: POST data to an external URL\n- `write_aws_s3_object`: Write output to S3\n- `call_agent`: Invoke another agent\n- `write_metadata`: Write a value to content metadata (for filtering/gates; content-triggered agents only. Fields: `metadata_key`, `content`)\n- `write_content_attachment`: Write a file-backed attachment to content (optionally indexed for retrieval; content-triggered agents only. Fields: `attachment_key`, `content`, `content_type`, `indexed`)\n- `load_content_attachment`: Load a previously written attachment (content-triggered agents only. Fields: `attachment_key`)\n- `load_content`: Load the full text body of a source document (typically used with content-triggered agents; can also load by explicit `content_version_id`. Fields: `content_version_id` optional)\n- `streaming_result`: Stream LLM tokens in real-time via SSE (must be a direct child of `prompt_call`; requires `dynamic_input` or `template_input` trigger; `priority: true` enables real-time streaming)\n- `display_result`: Show output to the user\n- `join`: Merge parallel branches\n- `merge`: Combine multiple inputs into a single templated output\n- `text`: Static text literal\n- `for_each`: Iterate a body over a list of items (body lives in `body[]`)\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. You can only access agents belonging to your account.", "operationId": "get_agent_definition_api_agents__agent_id__definition_get", "parameters": [ { @@ -9273,7 +9708,7 @@ ] }, "put": { - "description": "Update the agent's definition on the main branch.\n\nUses **optimistic locking**: provide `expected_change_id` from the last `GET /api/agents/{agent_id}/definition`. Returns `409 Conflict` if the definition was modified since your last read.\n\nThe definition contains the agent's step workflow. Step types include `prompt_call`, `retrieval`, `transform`, `gate`, `retry`, `evaluate_step`, `insight`, `extract_content`, `streaming_result`, `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, `write_metadata`, `write_content_attachment`, `load_content_attachment`, `load_content`, `display_result`, `join`, `combinator`, and `text`. Non-composite step types (`display_result`, `join`, `retry`, `evaluate_step`, `streaming_result`) cannot contain child steps.\n\n**Retry steps** re-execute from a target ancestor step for quality-control loops. Configure with `target_step_id` (ancestor step ID) and `max_retries` (1\u201310). Best practice: place a `gate` step before the retry to make retries conditional.\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. You can only update agents belonging to your account.", + "description": "Update the agent's definition on the main branch.\n\nUses **optimistic locking**: provide `expected_change_id` from the last `GET /api/agents/{agent_id}/definition`. Returns `409 Conflict` if the definition was modified since your last read.\n\nThe definition contains the agent's step workflow. Step types include `prompt_call`, `retrieval`, `regex_replace`, `gate`, `retry`, `evaluate_step`, `extract_data`, `extract_content`, `add_chat_turn`, `load_chat_history`, `add_memory`, `search_memory`, `load_memory`, `streaming_result`, `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, `write_metadata`, `write_content_attachment`, `load_content_attachment`, `load_content`, `display_result`, `join`, `merge`, `text`, and `for_each`. Non-composite step types (`display_result`, `join`, `retry`, `streaming_result`) cannot contain child steps.\n\n**Retry steps** re-execute from a target ancestor step for quality-control loops. Configure with `target_step_id` (ancestor step ID) and `max_retries` (1\u201310). Best practice: place a `gate` step before the retry to make retries conditional.\n\nAuth & scoping:\n- Requires `X-API-Key` header or OAuth Bearer token. You can only update agents belonging to your account.", "operationId": "update_agent_definition_api_agents__agent_id__definition_put", "parameters": [ { diff --git a/seclai/_generated/api/agents/create_agent_api_agents_post.py b/seclai/_generated/api/agents/create_agent_api_agents_post.py index cb5a220..8d83197 100644 --- a/seclai/_generated/api/agents/create_agent_api_agents_post.py +++ b/seclai/_generated/api/agents/create_agent_api_agents_post.py @@ -6,9 +6,11 @@ from ... import errors from ...client import AuthenticatedClient, Client +from ...models.agent_definition_import_error_response import ( + AgentDefinitionImportErrorResponse, +) from ...models.agent_summary_response import AgentSummaryResponse from ...models.create_agent_request import CreateAgentRequest -from ...models.http_validation_error import HTTPValidationError from ...types import UNSET, Response, Unset @@ -36,7 +38,7 @@ def _get_kwargs( def _parse_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> AgentSummaryResponse | Any | HTTPValidationError | None: +) -> AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any | None: if response.status_code == 201: response_201 = AgentSummaryResponse.from_dict(response.json()) @@ -47,7 +49,7 @@ def _parse_response( return response_402 if response.status_code == 422: - response_422 = HTTPValidationError.from_dict(response.json()) + response_422 = AgentDefinitionImportErrorResponse.from_dict(response.json()) return response_422 @@ -59,7 +61,7 @@ def _parse_response( def _build_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> Response[AgentSummaryResponse | Any | HTTPValidationError]: +) -> Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -73,7 +75,7 @@ def sync_detailed( client: AuthenticatedClient | Client, body: CreateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> Response[AgentSummaryResponse | Any | HTTPValidationError]: +) -> Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any]: """Create an agent Create a new agent. @@ -87,6 +89,14 @@ def sync_detailed( Templates: `blank`, `retrieval_example`, `simple_qa`, `summarizer`, `json_extractor`, `content_change_notifier`, `scheduled_report`, `webhook_pipeline` + Importing an existing agent: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. The full extras + suite (alert_configs, evaluation_criteria, governance_policies, schedules, solutions) is applied; + items that don't resolve in this account are reported in the response's `import_warnings` array. + - Use `POST /agents/preview-import` first to surface `unresolved_refs` (workflow refs to KBs, memory + banks, source connections, sub-agents that don't exist here). Then pass `entity_remap: {source_uuid: + target_uuid}` on this call to substitute them before save. + Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. Agent is created in the caller's account. @@ -99,7 +109,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[AgentSummaryResponse | Any | HTTPValidationError] + Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any] """ kwargs = _get_kwargs( @@ -119,7 +129,7 @@ def sync( client: AuthenticatedClient | Client, body: CreateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> AgentSummaryResponse | Any | HTTPValidationError | None: +) -> AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any | None: """Create an agent Create a new agent. @@ -133,6 +143,14 @@ def sync( Templates: `blank`, `retrieval_example`, `simple_qa`, `summarizer`, `json_extractor`, `content_change_notifier`, `scheduled_report`, `webhook_pipeline` + Importing an existing agent: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. The full extras + suite (alert_configs, evaluation_criteria, governance_policies, schedules, solutions) is applied; + items that don't resolve in this account are reported in the response's `import_warnings` array. + - Use `POST /agents/preview-import` first to surface `unresolved_refs` (workflow refs to KBs, memory + banks, source connections, sub-agents that don't exist here). Then pass `entity_remap: {source_uuid: + target_uuid}` on this call to substitute them before save. + Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. Agent is created in the caller's account. @@ -145,7 +163,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - AgentSummaryResponse | Any | HTTPValidationError + AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any """ return sync_detailed( @@ -160,7 +178,7 @@ async def asyncio_detailed( client: AuthenticatedClient | Client, body: CreateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> Response[AgentSummaryResponse | Any | HTTPValidationError]: +) -> Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any]: """Create an agent Create a new agent. @@ -174,6 +192,14 @@ async def asyncio_detailed( Templates: `blank`, `retrieval_example`, `simple_qa`, `summarizer`, `json_extractor`, `content_change_notifier`, `scheduled_report`, `webhook_pipeline` + Importing an existing agent: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. The full extras + suite (alert_configs, evaluation_criteria, governance_policies, schedules, solutions) is applied; + items that don't resolve in this account are reported in the response's `import_warnings` array. + - Use `POST /agents/preview-import` first to surface `unresolved_refs` (workflow refs to KBs, memory + banks, source connections, sub-agents that don't exist here). Then pass `entity_remap: {source_uuid: + target_uuid}` on this call to substitute them before save. + Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. Agent is created in the caller's account. @@ -186,7 +212,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[AgentSummaryResponse | Any | HTTPValidationError] + Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any] """ kwargs = _get_kwargs( @@ -204,7 +230,7 @@ async def asyncio( client: AuthenticatedClient | Client, body: CreateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> AgentSummaryResponse | Any | HTTPValidationError | None: +) -> AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any | None: """Create an agent Create a new agent. @@ -218,6 +244,14 @@ async def asyncio( Templates: `blank`, `retrieval_example`, `simple_qa`, `summarizer`, `json_extractor`, `content_change_notifier`, `scheduled_report`, `webhook_pipeline` + Importing an existing agent: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. The full extras + suite (alert_configs, evaluation_criteria, governance_policies, schedules, solutions) is applied; + items that don't resolve in this account are reported in the response's `import_warnings` array. + - Use `POST /agents/preview-import` first to surface `unresolved_refs` (workflow refs to KBs, memory + banks, source connections, sub-agents that don't exist here). Then pass `entity_remap: {source_uuid: + target_uuid}` on this call to substitute them before save. + Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. Agent is created in the caller's account. @@ -230,7 +264,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - AgentSummaryResponse | Any | HTTPValidationError + AgentDefinitionImportErrorResponse | AgentSummaryResponse | Any """ return ( diff --git a/seclai/_generated/api/agents/get_agent_definition_api_agents_agent_id_definition_get.py b/seclai/_generated/api/agents/get_agent_definition_api_agents_agent_id_definition_get.py index 9eb2075..3220c16 100644 --- a/seclai/_generated/api/agents/get_agent_definition_api_agents_agent_id_definition_get.py +++ b/seclai/_generated/api/agents/get_agent_definition_api_agents_agent_id_definition_get.py @@ -78,15 +78,19 @@ def sync_detailed( The definition contains the agent's step workflow. Available step types: - `prompt_call`: Call an LLM with a prompt template - `retrieval`: Search a knowledge base - - `transform`: Reshape data with a Liquid template + - `regex_replace`: Reshape text via ordered regex find/replace rules - `gate`: Evaluate conditions, stop or continue child execution - `retry`: Re-execute from a target ancestor step (for quality-control loops; pair with a `gate` step for conditional retrying. Fields: `target_step_id` (ancestor step ID), `max_retries` (1–10)) - `evaluate_step`: Score a selected previous step output and emit JSON with `score`, `passed`, and `pass_threshold` (fields: `target_step_id`, `evaluation_prompt`, `pass_threshold`, optional `evaluation_tier`, optional `expectation_config`) - - `insight`: Progressively read and analyze large input + - `extract_data`: Progressively read and analyze large input - `extract_content`: Extract structured data (JSON, HTML, XML) + - `add_chat_turn` / `load_chat_history`: Record a turn or load running history from a conversation + memory bank + - `add_memory` / `search_memory` / `load_memory`: Write, semantic-search, or load entries on a + general memory bank - `send_email`: Send email with step output - `webhook_call`: POST data to an external URL - `write_aws_s3_object`: Write output to S3 @@ -102,11 +106,13 @@ def sync_detailed( triggered agents; can also load by explicit `content_version_id`. Fields: `content_version_id` optional) - `streaming_result`: Stream LLM tokens in real-time via SSE (must be a direct child of - `prompt_call`; requires `dynamic_input` trigger; `priority: true` enables real-time streaming) + `prompt_call`; requires `dynamic_input` or `template_input` trigger; `priority: true` enables real- + time streaming) - `display_result`: Show output to the user - `join`: Merge parallel branches - - `combinator`: Combine multiple inputs + - `merge`: Combine multiple inputs into a single templated output - `text`: Static text literal + - `for_each`: Iterate a body over a list of items (body lives in `body[]`) Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. You can only access agents belonging to your @@ -152,15 +158,19 @@ def sync( The definition contains the agent's step workflow. Available step types: - `prompt_call`: Call an LLM with a prompt template - `retrieval`: Search a knowledge base - - `transform`: Reshape data with a Liquid template + - `regex_replace`: Reshape text via ordered regex find/replace rules - `gate`: Evaluate conditions, stop or continue child execution - `retry`: Re-execute from a target ancestor step (for quality-control loops; pair with a `gate` step for conditional retrying. Fields: `target_step_id` (ancestor step ID), `max_retries` (1–10)) - `evaluate_step`: Score a selected previous step output and emit JSON with `score`, `passed`, and `pass_threshold` (fields: `target_step_id`, `evaluation_prompt`, `pass_threshold`, optional `evaluation_tier`, optional `expectation_config`) - - `insight`: Progressively read and analyze large input + - `extract_data`: Progressively read and analyze large input - `extract_content`: Extract structured data (JSON, HTML, XML) + - `add_chat_turn` / `load_chat_history`: Record a turn or load running history from a conversation + memory bank + - `add_memory` / `search_memory` / `load_memory`: Write, semantic-search, or load entries on a + general memory bank - `send_email`: Send email with step output - `webhook_call`: POST data to an external URL - `write_aws_s3_object`: Write output to S3 @@ -176,11 +186,13 @@ def sync( triggered agents; can also load by explicit `content_version_id`. Fields: `content_version_id` optional) - `streaming_result`: Stream LLM tokens in real-time via SSE (must be a direct child of - `prompt_call`; requires `dynamic_input` trigger; `priority: true` enables real-time streaming) + `prompt_call`; requires `dynamic_input` or `template_input` trigger; `priority: true` enables real- + time streaming) - `display_result`: Show output to the user - `join`: Merge parallel branches - - `combinator`: Combine multiple inputs + - `merge`: Combine multiple inputs into a single templated output - `text`: Static text literal + - `for_each`: Iterate a body over a list of items (body lives in `body[]`) Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. You can only access agents belonging to your @@ -221,15 +233,19 @@ async def asyncio_detailed( The definition contains the agent's step workflow. Available step types: - `prompt_call`: Call an LLM with a prompt template - `retrieval`: Search a knowledge base - - `transform`: Reshape data with a Liquid template + - `regex_replace`: Reshape text via ordered regex find/replace rules - `gate`: Evaluate conditions, stop or continue child execution - `retry`: Re-execute from a target ancestor step (for quality-control loops; pair with a `gate` step for conditional retrying. Fields: `target_step_id` (ancestor step ID), `max_retries` (1–10)) - `evaluate_step`: Score a selected previous step output and emit JSON with `score`, `passed`, and `pass_threshold` (fields: `target_step_id`, `evaluation_prompt`, `pass_threshold`, optional `evaluation_tier`, optional `expectation_config`) - - `insight`: Progressively read and analyze large input + - `extract_data`: Progressively read and analyze large input - `extract_content`: Extract structured data (JSON, HTML, XML) + - `add_chat_turn` / `load_chat_history`: Record a turn or load running history from a conversation + memory bank + - `add_memory` / `search_memory` / `load_memory`: Write, semantic-search, or load entries on a + general memory bank - `send_email`: Send email with step output - `webhook_call`: POST data to an external URL - `write_aws_s3_object`: Write output to S3 @@ -245,11 +261,13 @@ async def asyncio_detailed( triggered agents; can also load by explicit `content_version_id`. Fields: `content_version_id` optional) - `streaming_result`: Stream LLM tokens in real-time via SSE (must be a direct child of - `prompt_call`; requires `dynamic_input` trigger; `priority: true` enables real-time streaming) + `prompt_call`; requires `dynamic_input` or `template_input` trigger; `priority: true` enables real- + time streaming) - `display_result`: Show output to the user - `join`: Merge parallel branches - - `combinator`: Combine multiple inputs + - `merge`: Combine multiple inputs into a single templated output - `text`: Static text literal + - `for_each`: Iterate a body over a list of items (body lives in `body[]`) Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. You can only access agents belonging to your @@ -293,15 +311,19 @@ async def asyncio( The definition contains the agent's step workflow. Available step types: - `prompt_call`: Call an LLM with a prompt template - `retrieval`: Search a knowledge base - - `transform`: Reshape data with a Liquid template + - `regex_replace`: Reshape text via ordered regex find/replace rules - `gate`: Evaluate conditions, stop or continue child execution - `retry`: Re-execute from a target ancestor step (for quality-control loops; pair with a `gate` step for conditional retrying. Fields: `target_step_id` (ancestor step ID), `max_retries` (1–10)) - `evaluate_step`: Score a selected previous step output and emit JSON with `score`, `passed`, and `pass_threshold` (fields: `target_step_id`, `evaluation_prompt`, `pass_threshold`, optional `evaluation_tier`, optional `expectation_config`) - - `insight`: Progressively read and analyze large input + - `extract_data`: Progressively read and analyze large input - `extract_content`: Extract structured data (JSON, HTML, XML) + - `add_chat_turn` / `load_chat_history`: Record a turn or load running history from a conversation + memory bank + - `add_memory` / `search_memory` / `load_memory`: Write, semantic-search, or load entries on a + general memory bank - `send_email`: Send email with step output - `webhook_call`: POST data to an external URL - `write_aws_s3_object`: Write output to S3 @@ -317,11 +339,13 @@ async def asyncio( triggered agents; can also load by explicit `content_version_id`. Fields: `content_version_id` optional) - `streaming_result`: Stream LLM tokens in real-time via SSE (must be a direct child of - `prompt_call`; requires `dynamic_input` trigger; `priority: true` enables real-time streaming) + `prompt_call`; requires `dynamic_input` or `template_input` trigger; `priority: true` enables real- + time streaming) - `display_result`: Show output to the user - `join`: Merge parallel branches - - `combinator`: Combine multiple inputs + - `merge`: Combine multiple inputs into a single templated output - `text`: Static text literal + - `for_each`: Iterate a body over a list of items (body lives in `body[]`) Auth & scoping: - Requires `X-API-Key` header or OAuth Bearer token. You can only access agents belonging to your diff --git a/seclai/_generated/api/agents/preview_import_agent_api_agents_preview_import_post.py b/seclai/_generated/api/agents/preview_import_agent_api_agents_preview_import_post.py new file mode 100644 index 0000000..e819d56 --- /dev/null +++ b/seclai/_generated/api/agents/preview_import_agent_api_agents_preview_import_post.py @@ -0,0 +1,224 @@ +from http import HTTPStatus +from typing import Any +from uuid import UUID + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.agent_definition_import_error_response import ( + AgentDefinitionImportErrorResponse, +) +from ...models.agent_import_preview_request import AgentImportPreviewRequest +from ...models.agent_import_preview_response import AgentImportPreviewResponse +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + *, + body: AgentImportPreviewRequest, + x_account_id: UUID | Unset = UNSET, +) -> dict[str, Any]: + headers: dict[str, Any] = {} + if not isinstance(x_account_id, Unset): + headers["X-Account-Id"] = x_account_id + + _kwargs: dict[str, Any] = { + "method": "post", + "url": "/agents/preview-import", + } + + _kwargs["json"] = body.to_dict() + + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> AgentDefinitionImportErrorResponse | AgentImportPreviewResponse | None: + if response.status_code == 200: + response_200 = AgentImportPreviewResponse.from_dict(response.json()) + + return response_200 + + if response.status_code == 422: + response_422 = AgentDefinitionImportErrorResponse.from_dict(response.json()) + + return response_422 + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: AuthenticatedClient | Client, response: httpx.Response +) -> Response[AgentDefinitionImportErrorResponse | AgentImportPreviewResponse]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: AuthenticatedClient | Client, + body: AgentImportPreviewRequest, + x_account_id: UUID | Unset = UNSET, +) -> Response[AgentDefinitionImportErrorResponse | AgentImportPreviewResponse]: + """Preview an agent_definition import + + Validate an `agent_definition` payload (the same shape produced by `GET + /api/agents/{agent_id}/export`) without creating or modifying any agent. On success returns a + summary the client can show before commit (counts of steps, schedules, alert configs, evaluation + criteria, governance policies). On failure returns the same 422 body shape used by `POST + /api/agents` and `PUT /api/agents/{id}` so callers can render line/column-anchored errors. + + Auth & scoping: + - Requires `X-API-Key` header or OAuth Bearer token. No DB writes. + + Args: + x_account_id (UUID | Unset): + body (AgentImportPreviewRequest): Dry-run import request — same payload shape as the + export endpoint. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AgentDefinitionImportErrorResponse | AgentImportPreviewResponse] + """ + + kwargs = _get_kwargs( + body=body, + x_account_id=x_account_id, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: AuthenticatedClient | Client, + body: AgentImportPreviewRequest, + x_account_id: UUID | Unset = UNSET, +) -> AgentDefinitionImportErrorResponse | AgentImportPreviewResponse | None: + """Preview an agent_definition import + + Validate an `agent_definition` payload (the same shape produced by `GET + /api/agents/{agent_id}/export`) without creating or modifying any agent. On success returns a + summary the client can show before commit (counts of steps, schedules, alert configs, evaluation + criteria, governance policies). On failure returns the same 422 body shape used by `POST + /api/agents` and `PUT /api/agents/{id}` so callers can render line/column-anchored errors. + + Auth & scoping: + - Requires `X-API-Key` header or OAuth Bearer token. No DB writes. + + Args: + x_account_id (UUID | Unset): + body (AgentImportPreviewRequest): Dry-run import request — same payload shape as the + export endpoint. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AgentDefinitionImportErrorResponse | AgentImportPreviewResponse + """ + + return sync_detailed( + client=client, + body=body, + x_account_id=x_account_id, + ).parsed + + +async def asyncio_detailed( + *, + client: AuthenticatedClient | Client, + body: AgentImportPreviewRequest, + x_account_id: UUID | Unset = UNSET, +) -> Response[AgentDefinitionImportErrorResponse | AgentImportPreviewResponse]: + """Preview an agent_definition import + + Validate an `agent_definition` payload (the same shape produced by `GET + /api/agents/{agent_id}/export`) without creating or modifying any agent. On success returns a + summary the client can show before commit (counts of steps, schedules, alert configs, evaluation + criteria, governance policies). On failure returns the same 422 body shape used by `POST + /api/agents` and `PUT /api/agents/{id}` so callers can render line/column-anchored errors. + + Auth & scoping: + - Requires `X-API-Key` header or OAuth Bearer token. No DB writes. + + Args: + x_account_id (UUID | Unset): + body (AgentImportPreviewRequest): Dry-run import request — same payload shape as the + export endpoint. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AgentDefinitionImportErrorResponse | AgentImportPreviewResponse] + """ + + kwargs = _get_kwargs( + body=body, + x_account_id=x_account_id, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: AuthenticatedClient | Client, + body: AgentImportPreviewRequest, + x_account_id: UUID | Unset = UNSET, +) -> AgentDefinitionImportErrorResponse | AgentImportPreviewResponse | None: + """Preview an agent_definition import + + Validate an `agent_definition` payload (the same shape produced by `GET + /api/agents/{agent_id}/export`) without creating or modifying any agent. On success returns a + summary the client can show before commit (counts of steps, schedules, alert configs, evaluation + criteria, governance policies). On failure returns the same 422 body shape used by `POST + /api/agents` and `PUT /api/agents/{id}` so callers can render line/column-anchored errors. + + Auth & scoping: + - Requires `X-API-Key` header or OAuth Bearer token. No DB writes. + + Args: + x_account_id (UUID | Unset): + body (AgentImportPreviewRequest): Dry-run import request — same payload shape as the + export endpoint. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AgentDefinitionImportErrorResponse | AgentImportPreviewResponse + """ + + return ( + await asyncio_detailed( + client=client, + body=body, + x_account_id=x_account_id, + ) + ).parsed diff --git a/seclai/_generated/api/agents/update_agent_api_agents_agent_id_put.py b/seclai/_generated/api/agents/update_agent_api_agents_agent_id_put.py index ab510c4..dbfea14 100644 --- a/seclai/_generated/api/agents/update_agent_api_agents_agent_id_put.py +++ b/seclai/_generated/api/agents/update_agent_api_agents_agent_id_put.py @@ -7,8 +7,10 @@ from ... import errors from ...client import AuthenticatedClient, Client +from ...models.agent_definition_import_error_response import ( + AgentDefinitionImportErrorResponse, +) from ...models.agent_summary_response import AgentSummaryResponse -from ...models.http_validation_error import HTTPValidationError from ...models.update_agent_request import UpdateAgentRequest from ...types import UNSET, Response, Unset @@ -40,14 +42,14 @@ def _get_kwargs( def _parse_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> AgentSummaryResponse | HTTPValidationError | None: +) -> AgentDefinitionImportErrorResponse | AgentSummaryResponse | None: if response.status_code == 200: response_200 = AgentSummaryResponse.from_dict(response.json()) return response_200 if response.status_code == 422: - response_422 = HTTPValidationError.from_dict(response.json()) + response_422 = AgentDefinitionImportErrorResponse.from_dict(response.json()) return response_422 @@ -59,7 +61,7 @@ def _parse_response( def _build_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> Response[AgentSummaryResponse | HTTPValidationError]: +) -> Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -74,7 +76,7 @@ def sync_detailed( client: AuthenticatedClient | Client, body: UpdateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> Response[AgentSummaryResponse | HTTPValidationError]: +) -> Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse]: """Update agent metadata Update an agent's name, description, evaluation settings, and model lifecycle settings. @@ -88,6 +90,14 @@ def sync_detailed( `prompt_model_auto_rollback_triggers` (list of 'agent_eval_fail', 'governance_flag', 'governance_block', 'agent_run_failed'). + Replacing the workflow from an export: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. Update only + touches the workflow + agent metadata — alert_configs, evaluation_criteria, governance_policies, + schedules, and solution links from the imported file are NOT applied (use the dedicated endpoints + for those, or `POST /agents` to import as a new agent). + - `entity_remap: {source_uuid: target_uuid}` substitutes workflow entity refs before save (same + shape as `POST /agents`). + At least one field must be provided. Auth & scoping: @@ -104,7 +114,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[AgentSummaryResponse | HTTPValidationError] + Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse] """ kwargs = _get_kwargs( @@ -126,7 +136,7 @@ def sync( client: AuthenticatedClient | Client, body: UpdateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> AgentSummaryResponse | HTTPValidationError | None: +) -> AgentDefinitionImportErrorResponse | AgentSummaryResponse | None: """Update agent metadata Update an agent's name, description, evaluation settings, and model lifecycle settings. @@ -140,6 +150,14 @@ def sync( `prompt_model_auto_rollback_triggers` (list of 'agent_eval_fail', 'governance_flag', 'governance_block', 'agent_run_failed'). + Replacing the workflow from an export: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. Update only + touches the workflow + agent metadata — alert_configs, evaluation_criteria, governance_policies, + schedules, and solution links from the imported file are NOT applied (use the dedicated endpoints + for those, or `POST /agents` to import as a new agent). + - `entity_remap: {source_uuid: target_uuid}` substitutes workflow entity refs before save (same + shape as `POST /agents`). + At least one field must be provided. Auth & scoping: @@ -156,7 +174,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - AgentSummaryResponse | HTTPValidationError + AgentDefinitionImportErrorResponse | AgentSummaryResponse """ return sync_detailed( @@ -173,7 +191,7 @@ async def asyncio_detailed( client: AuthenticatedClient | Client, body: UpdateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> Response[AgentSummaryResponse | HTTPValidationError]: +) -> Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse]: """Update agent metadata Update an agent's name, description, evaluation settings, and model lifecycle settings. @@ -187,6 +205,14 @@ async def asyncio_detailed( `prompt_model_auto_rollback_triggers` (list of 'agent_eval_fail', 'governance_flag', 'governance_block', 'agent_run_failed'). + Replacing the workflow from an export: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. Update only + touches the workflow + agent metadata — alert_configs, evaluation_criteria, governance_policies, + schedules, and solution links from the imported file are NOT applied (use the dedicated endpoints + for those, or `POST /agents` to import as a new agent). + - `entity_remap: {source_uuid: target_uuid}` substitutes workflow entity refs before save (same + shape as `POST /agents`). + At least one field must be provided. Auth & scoping: @@ -203,7 +229,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[AgentSummaryResponse | HTTPValidationError] + Response[AgentDefinitionImportErrorResponse | AgentSummaryResponse] """ kwargs = _get_kwargs( @@ -223,7 +249,7 @@ async def asyncio( client: AuthenticatedClient | Client, body: UpdateAgentRequest, x_account_id: UUID | Unset = UNSET, -) -> AgentSummaryResponse | HTTPValidationError | None: +) -> AgentDefinitionImportErrorResponse | AgentSummaryResponse | None: """Update agent metadata Update an agent's name, description, evaluation settings, and model lifecycle settings. @@ -237,6 +263,14 @@ async def asyncio( `prompt_model_auto_rollback_triggers` (list of 'agent_eval_fail', 'governance_flag', 'governance_block', 'agent_run_failed'). + Replacing the workflow from an export: + - Pass `agent_definition` with the JSON shape produced by `GET /agents/{id}/export`. Update only + touches the workflow + agent metadata — alert_configs, evaluation_criteria, governance_policies, + schedules, and solution links from the imported file are NOT applied (use the dedicated endpoints + for those, or `POST /agents` to import as a new agent). + - `entity_remap: {source_uuid: target_uuid}` substitutes workflow entity refs before save (same + shape as `POST /agents`). + At least one field must be provided. Auth & scoping: @@ -253,7 +287,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - AgentSummaryResponse | HTTPValidationError + AgentDefinitionImportErrorResponse | AgentSummaryResponse """ return ( diff --git a/seclai/_generated/api/agents/update_agent_definition_api_agents_agent_id_definition_put.py b/seclai/_generated/api/agents/update_agent_definition_api_agents_agent_id_definition_put.py index a299b71..b678663 100644 --- a/seclai/_generated/api/agents/update_agent_definition_api_agents_agent_id_definition_put.py +++ b/seclai/_generated/api/agents/update_agent_definition_api_agents_agent_id_definition_put.py @@ -84,11 +84,12 @@ def sync_detailed( last read. The definition contains the agent's step workflow. Step types include `prompt_call`, `retrieval`, - `transform`, `gate`, `retry`, `evaluate_step`, `insight`, `extract_content`, `streaming_result`, - `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, `write_metadata`, - `write_content_attachment`, `load_content_attachment`, `load_content`, `display_result`, `join`, - `combinator`, and `text`. Non-composite step types (`display_result`, `join`, `retry`, - `evaluate_step`, `streaming_result`) cannot contain child steps. + `regex_replace`, `gate`, `retry`, `evaluate_step`, `extract_data`, `extract_content`, + `add_chat_turn`, `load_chat_history`, `add_memory`, `search_memory`, `load_memory`, + `streaming_result`, `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, + `write_metadata`, `write_content_attachment`, `load_content_attachment`, `load_content`, + `display_result`, `join`, `merge`, `text`, and `for_each`. Non-composite step types + (`display_result`, `join`, `retry`, `streaming_result`) cannot contain child steps. **Retry steps** re-execute from a target ancestor step for quality-control loops. Configure with `target_step_id` (ancestor step ID) and `max_retries` (1–10). Best practice: place a `gate` step @@ -140,11 +141,12 @@ def sync( last read. The definition contains the agent's step workflow. Step types include `prompt_call`, `retrieval`, - `transform`, `gate`, `retry`, `evaluate_step`, `insight`, `extract_content`, `streaming_result`, - `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, `write_metadata`, - `write_content_attachment`, `load_content_attachment`, `load_content`, `display_result`, `join`, - `combinator`, and `text`. Non-composite step types (`display_result`, `join`, `retry`, - `evaluate_step`, `streaming_result`) cannot contain child steps. + `regex_replace`, `gate`, `retry`, `evaluate_step`, `extract_data`, `extract_content`, + `add_chat_turn`, `load_chat_history`, `add_memory`, `search_memory`, `load_memory`, + `streaming_result`, `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, + `write_metadata`, `write_content_attachment`, `load_content_attachment`, `load_content`, + `display_result`, `join`, `merge`, `text`, and `for_each`. Non-composite step types + (`display_result`, `join`, `retry`, `streaming_result`) cannot contain child steps. **Retry steps** re-execute from a target ancestor step for quality-control loops. Configure with `target_step_id` (ancestor step ID) and `max_retries` (1–10). Best practice: place a `gate` step @@ -191,11 +193,12 @@ async def asyncio_detailed( last read. The definition contains the agent's step workflow. Step types include `prompt_call`, `retrieval`, - `transform`, `gate`, `retry`, `evaluate_step`, `insight`, `extract_content`, `streaming_result`, - `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, `write_metadata`, - `write_content_attachment`, `load_content_attachment`, `load_content`, `display_result`, `join`, - `combinator`, and `text`. Non-composite step types (`display_result`, `join`, `retry`, - `evaluate_step`, `streaming_result`) cannot contain child steps. + `regex_replace`, `gate`, `retry`, `evaluate_step`, `extract_data`, `extract_content`, + `add_chat_turn`, `load_chat_history`, `add_memory`, `search_memory`, `load_memory`, + `streaming_result`, `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, + `write_metadata`, `write_content_attachment`, `load_content_attachment`, `load_content`, + `display_result`, `join`, `merge`, `text`, and `for_each`. Non-composite step types + (`display_result`, `join`, `retry`, `streaming_result`) cannot contain child steps. **Retry steps** re-execute from a target ancestor step for quality-control loops. Configure with `target_step_id` (ancestor step ID) and `max_retries` (1–10). Best practice: place a `gate` step @@ -245,11 +248,12 @@ async def asyncio( last read. The definition contains the agent's step workflow. Step types include `prompt_call`, `retrieval`, - `transform`, `gate`, `retry`, `evaluate_step`, `insight`, `extract_content`, `streaming_result`, - `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, `write_metadata`, - `write_content_attachment`, `load_content_attachment`, `load_content`, `display_result`, `join`, - `combinator`, and `text`. Non-composite step types (`display_result`, `join`, `retry`, - `evaluate_step`, `streaming_result`) cannot contain child steps. + `regex_replace`, `gate`, `retry`, `evaluate_step`, `extract_data`, `extract_content`, + `add_chat_turn`, `load_chat_history`, `add_memory`, `search_memory`, `load_memory`, + `streaming_result`, `send_email`, `webhook_call`, `write_aws_s3_object`, `call_agent`, + `write_metadata`, `write_content_attachment`, `load_content_attachment`, `load_content`, + `display_result`, `join`, `merge`, `text`, and `for_each`. Non-composite step types + (`display_result`, `join`, `retry`, `streaming_result`) cannot contain child steps. **Retry steps** re-execute from a target ancestor step for quality-control loops. Configure with `target_step_id` (ancestor step ID) and `max_retries` (1–10). Best practice: place a `gate` step diff --git a/seclai/_generated/models/__init__.py b/seclai/_generated/models/__init__.py index ffc2995..0a1b7a4 100644 --- a/seclai/_generated/models/__init__.py +++ b/seclai/_generated/models/__init__.py @@ -8,6 +8,7 @@ from .add_conversation_turn_request_actions_taken_type_0 import ( AddConversationTurnRequestActionsTakenType0, ) +from .agent_definition_import_error_response import AgentDefinitionImportErrorResponse from .agent_definition_response import AgentDefinitionResponse from .agent_definition_response_definition import AgentDefinitionResponseDefinition from .agent_definition_response_warnings_type_0_item import ( @@ -29,6 +30,14 @@ AgentExportResponseGovernancePoliciesType0Item, ) from .agent_export_response_trigger_type_0 import AgentExportResponseTriggerType0 +from .agent_import_preview_request import AgentImportPreviewRequest +from .agent_import_preview_request_agent_definition import ( + AgentImportPreviewRequestAgentDefinition, +) +from .agent_import_preview_response import AgentImportPreviewResponse +from .agent_import_preview_response_unresolved_refs_item import ( + AgentImportPreviewResponseUnresolvedRefsItem, +) from .agent_list_response import AgentListResponse from .agent_run_attempt_response import AgentRunAttemptResponse from .agent_run_list_response import AgentRunListResponse @@ -90,6 +99,10 @@ from .content_embedding_response import ContentEmbeddingResponse from .content_embeddings_list_response import ContentEmbeddingsListResponse from .create_agent_request import CreateAgentRequest +from .create_agent_request_agent_definition_type_0 import ( + CreateAgentRequestAgentDefinitionType0, +) +from .create_agent_request_entity_remap_type_0 import CreateAgentRequestEntityRemapType0 from .create_alert_config_api_alerts_configs_post_response_create_alert_config_api_alerts_configs_post import ( CreateAlertConfigApiAlertsConfigsPostResponseCreateAlertConfigApiAlertsConfigsPost, ) @@ -203,7 +216,11 @@ from .governance_conversation_response_proposed_actions_type_0 import ( GovernanceConversationResponseProposedActionsType0, ) +from .governance_policy_ref_response import GovernancePolicyRefResponse from .http_validation_error import HTTPValidationError +from .import_field_error_model import ImportFieldErrorModel +from .import_skip_response import ImportSkipResponse +from .import_skip_response_details import ImportSkipResponseDetails from .inline_text_replace_request import InlineTextReplaceRequest from .inline_text_replace_request_metadata_type_0 import ( InlineTextReplaceRequestMetadataType0, @@ -282,6 +299,7 @@ from .prompt_tool_response_headers_type_0 import PromptToolResponseHeadersType0 from .proposed_action_response import ProposedActionResponse from .proposed_action_response_params import ProposedActionResponseParams +from .proposed_action_response_preview_type_0 import ProposedActionResponsePreviewType0 from .proposed_policy_action_response import ProposedPolicyActionResponse from .proposed_policy_action_response_params import ProposedPolicyActionResponseParams from .provider_group_response import ProviderGroupResponse @@ -325,6 +343,10 @@ UpdateAgentDefinitionRequestDefinition, ) from .update_agent_request import UpdateAgentRequest +from .update_agent_request_agent_definition_type_0 import ( + UpdateAgentRequestAgentDefinitionType0, +) +from .update_agent_request_entity_remap_type_0 import UpdateAgentRequestEntityRemapType0 from .update_agent_request_sampling_config_type_0 import ( UpdateAgentRequestSamplingConfigType0, ) @@ -356,6 +378,7 @@ "AddCommentRequest", "AddConversationTurnRequest", "AddConversationTurnRequestActionsTakenType0", + "AgentDefinitionImportErrorResponse", "AgentDefinitionResponse", "AgentDefinitionResponseDefinition", "AgentDefinitionResponseWarningsType0Item", @@ -367,6 +390,10 @@ "AgentExportResponseEvaluationCriteriaType0Item", "AgentExportResponseGovernancePoliciesType0Item", "AgentExportResponseTriggerType0", + "AgentImportPreviewRequest", + "AgentImportPreviewRequestAgentDefinition", + "AgentImportPreviewResponse", + "AgentImportPreviewResponseUnresolvedRefsItem", "AgentListResponse", "AgentRunAttemptResponse", "AgentRunListResponse", @@ -408,6 +435,8 @@ "ContentEmbeddingResponse", "ContentEmbeddingsListResponse", "CreateAgentRequest", + "CreateAgentRequestAgentDefinitionType0", + "CreateAgentRequestEntityRemapType0", "CreateAlertConfigApiAlertsConfigsPostResponseCreateAlertConfigApiAlertsConfigsPost", "CreateAlertConfigRequest", "CreateAlertConfigRequestThresholdType0", @@ -467,7 +496,11 @@ "GovernanceAiAssistantResponse", "GovernanceConversationResponse", "GovernanceConversationResponseProposedActionsType0", + "GovernancePolicyRefResponse", "HTTPValidationError", + "ImportFieldErrorModel", + "ImportSkipResponse", + "ImportSkipResponseDetails", "InlineTextReplaceRequest", "InlineTextReplaceRequestMetadataType0", "InlineTextUploadRequest", @@ -512,6 +545,7 @@ "PromptToolResponseHeadersType0", "ProposedActionResponse", "ProposedActionResponseParams", + "ProposedActionResponsePreviewType0", "ProposedPolicyActionResponse", "ProposedPolicyActionResponseParams", "ProviderGroupResponse", @@ -541,6 +575,8 @@ "UpdateAgentDefinitionRequest", "UpdateAgentDefinitionRequestDefinition", "UpdateAgentRequest", + "UpdateAgentRequestAgentDefinitionType0", + "UpdateAgentRequestEntityRemapType0", "UpdateAgentRequestSamplingConfigType0", "UpdateAlertConfigApiAlertsConfigsConfigIdPatchResponseUpdateAlertConfigApiAlertsConfigsConfigIdPatch", "UpdateAlertConfigRequest", diff --git a/seclai/_generated/models/agent_definition_import_error_response.py b/seclai/_generated/models/agent_definition_import_error_response.py new file mode 100644 index 0000000..366a772 --- /dev/null +++ b/seclai/_generated/models/agent_definition_import_error_response.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +if TYPE_CHECKING: + from ..models.import_field_error_model import ImportFieldErrorModel + + +T = TypeVar("T", bound="AgentDefinitionImportErrorResponse") + + +@_attrs_define +class AgentDefinitionImportErrorResponse: + """422 body for invalid `agent_definition` payloads. + + Mirrors :py:meth:`AgentDefinitionImportError.to_response_dict`. + + Attributes: + errors (list[ImportFieldErrorModel]): + message (str): + source (str): Canonical pretty-printed echo of the supplied payload — error line/column refer to this string. + error (str | Unset): Default: 'invalid_agent_definition'. + """ + + errors: list[ImportFieldErrorModel] + message: str + source: str + error: str | Unset = "invalid_agent_definition" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + errors = [] + for errors_item_data in self.errors: + errors_item = errors_item_data.to_dict() + errors.append(errors_item) + + message = self.message + + source = self.source + + error = self.error + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "errors": errors, + "message": message, + "source": source, + } + ) + if error is not UNSET: + field_dict["error"] = error + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.import_field_error_model import ImportFieldErrorModel + + d = dict(src_dict) + errors = [] + _errors = d.pop("errors") + for errors_item_data in _errors: + errors_item = ImportFieldErrorModel.from_dict(errors_item_data) + + errors.append(errors_item) + + message = d.pop("message") + + source = d.pop("source") + + error = d.pop("error", UNSET) + + agent_definition_import_error_response = cls( + errors=errors, + message=message, + source=source, + error=error, + ) + + agent_definition_import_error_response.additional_properties = d + return agent_definition_import_error_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/agent_definition_response.py b/seclai/_generated/models/agent_definition_response.py index f094be4..702b8b3 100644 --- a/seclai/_generated/models/agent_definition_response.py +++ b/seclai/_generated/models/agent_definition_response.py @@ -26,9 +26,10 @@ class AgentDefinitionResponse: Attributes: change_id (str): Current change ID (use as expected_change_id when updating). definition (AgentDefinitionResponseDefinition): The agent definition containing name, description, tags, and - step workflow tree. Step types include prompt_call, retrieval, transform, gate, retry, evaluate_step, insight, - extract_content, streaming_result, send_email, webhook_call, call_agent, write_metadata, - write_content_attachment, load_content_attachment, load_content, display_result, and others. + step workflow tree. Step types include prompt_call, retrieval, regex_replace, gate, retry, evaluate_step, + extract_data, extract_content, add_chat_turn, load_chat_history, add_memory, search_memory, load_memory, + streaming_result, send_email, webhook_call, call_agent, write_metadata, write_content_attachment, + load_content_attachment, load_content, display_result, merge, for_each, and others. schema_version (str): Agent schema version. warnings (list[AgentDefinitionResponseWarningsType0Item] | None | Unset): Validation warnings, if any. """ diff --git a/seclai/_generated/models/agent_definition_response_definition.py b/seclai/_generated/models/agent_definition_response_definition.py index c84c270..4036221 100644 --- a/seclai/_generated/models/agent_definition_response_definition.py +++ b/seclai/_generated/models/agent_definition_response_definition.py @@ -12,9 +12,10 @@ @_attrs_define class AgentDefinitionResponseDefinition: """The agent definition containing name, description, tags, and step workflow tree. Step types include prompt_call, - retrieval, transform, gate, retry, evaluate_step, insight, extract_content, streaming_result, send_email, - webhook_call, call_agent, write_metadata, write_content_attachment, load_content_attachment, load_content, - display_result, and others. + retrieval, regex_replace, gate, retry, evaluate_step, extract_data, extract_content, add_chat_turn, + load_chat_history, add_memory, search_memory, load_memory, streaming_result, send_email, webhook_call, call_agent, + write_metadata, write_content_attachment, load_content_attachment, load_content, display_result, merge, for_each, + and others. """ diff --git a/seclai/_generated/models/agent_import_preview_request.py b/seclai/_generated/models/agent_import_preview_request.py new file mode 100644 index 0000000..41945dd --- /dev/null +++ b/seclai/_generated/models/agent_import_preview_request.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.agent_import_preview_request_agent_definition import ( + AgentImportPreviewRequestAgentDefinition, + ) + + +T = TypeVar("T", bound="AgentImportPreviewRequest") + + +@_attrs_define +class AgentImportPreviewRequest: + """Dry-run import request — same payload shape as the export endpoint. + + Attributes: + agent_definition (AgentImportPreviewRequestAgentDefinition): Payload in the same shape as GET + /api/agents/{agent_id}/export. + """ + + agent_definition: AgentImportPreviewRequestAgentDefinition + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + agent_definition = self.agent_definition.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "agent_definition": agent_definition, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.agent_import_preview_request_agent_definition import ( + AgentImportPreviewRequestAgentDefinition, + ) + + d = dict(src_dict) + agent_definition = AgentImportPreviewRequestAgentDefinition.from_dict( + d.pop("agent_definition") + ) + + agent_import_preview_request = cls( + agent_definition=agent_definition, + ) + + agent_import_preview_request.additional_properties = d + return agent_import_preview_request + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/agent_import_preview_request_agent_definition.py b/seclai/_generated/models/agent_import_preview_request_agent_definition.py new file mode 100644 index 0000000..b813672 --- /dev/null +++ b/seclai/_generated/models/agent_import_preview_request_agent_definition.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="AgentImportPreviewRequestAgentDefinition") + + +@_attrs_define +class AgentImportPreviewRequestAgentDefinition: + """Payload in the same shape as GET /api/agents/{agent_id}/export.""" + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + agent_import_preview_request_agent_definition = cls() + + agent_import_preview_request_agent_definition.additional_properties = d + return agent_import_preview_request_agent_definition + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/agent_import_preview_response.py b/seclai/_generated/models/agent_import_preview_response.py new file mode 100644 index 0000000..4437f28 --- /dev/null +++ b/seclai/_generated/models/agent_import_preview_response.py @@ -0,0 +1,222 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +if TYPE_CHECKING: + from ..models.agent_import_preview_response_unresolved_refs_item import ( + AgentImportPreviewResponseUnresolvedRefsItem, + ) + + +T = TypeVar("T", bound="AgentImportPreviewResponse") + + +@_attrs_define +class AgentImportPreviewResponse: + """Summary of a successfully validated import payload (no DB writes). + + Counts are derived from the validated payload as supplied — they + reflect what was requested, not what would eventually be persisted + (cross-account skips for recipients and KB names happen later, only + on commit). + + Attributes: + agent_name (None | str): Imported agent name, if any. + alert_configs (int): Number of alert configs in the payload. + description (None | str): Imported agent description, if any. + evaluation_criteria (int): Number of evaluation criteria in the payload. + governance_policies (int): Number of agent-scoped governance policies in the payload. + ok (bool): Always true on a 200 response; failures use HTTP 422. + schedules (int): Number of trigger schedules in the payload. + step_count (int): Total number of steps in the workflow tree (recursive). + payload_export_version (None | str | Unset): Export-format version the payload claims (or ``null`` for legacy + payloads). When this differs from ``supported_export_version``, fields may have been silently dropped or + defaulted on import. + solutions (int | Unset): Number of solutions the source agent belonged to. On import these are matched by name + in the target account; unmatched names are silently skipped. Default: 0. + supported_export_version (str | Unset): Export-format version this server understands. Compare against + ``payload_export_version`` to detect cross-version imports. Default: '2'. + unresolved_refs (list[AgentImportPreviewResponseUnresolvedRefsItem] | Unset): Entity references in the imported + workflow that don't exist in the target account. Each entry: {category, ref_id, ref_name?, + locations:[step:], alternatives:[{id, name, description?}]}. Pass {source_uuid: target_uuid} as + ``entity_remap`` on the create/update call to substitute these references before save. + """ + + agent_name: None | str + alert_configs: int + description: None | str + evaluation_criteria: int + governance_policies: int + ok: bool + schedules: int + step_count: int + payload_export_version: None | str | Unset = UNSET + solutions: int | Unset = 0 + supported_export_version: str | Unset = "2" + unresolved_refs: list[AgentImportPreviewResponseUnresolvedRefsItem] | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + agent_name: None | str + agent_name = self.agent_name + + alert_configs = self.alert_configs + + description: None | str + description = self.description + + evaluation_criteria = self.evaluation_criteria + + governance_policies = self.governance_policies + + ok = self.ok + + schedules = self.schedules + + step_count = self.step_count + + payload_export_version: None | str | Unset + if isinstance(self.payload_export_version, Unset): + payload_export_version = UNSET + else: + payload_export_version = self.payload_export_version + + solutions = self.solutions + + supported_export_version = self.supported_export_version + + unresolved_refs: list[dict[str, Any]] | Unset = UNSET + if not isinstance(self.unresolved_refs, Unset): + unresolved_refs = [] + for unresolved_refs_item_data in self.unresolved_refs: + unresolved_refs_item = unresolved_refs_item_data.to_dict() + unresolved_refs.append(unresolved_refs_item) + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "agent_name": agent_name, + "alert_configs": alert_configs, + "description": description, + "evaluation_criteria": evaluation_criteria, + "governance_policies": governance_policies, + "ok": ok, + "schedules": schedules, + "step_count": step_count, + } + ) + if payload_export_version is not UNSET: + field_dict["payload_export_version"] = payload_export_version + if solutions is not UNSET: + field_dict["solutions"] = solutions + if supported_export_version is not UNSET: + field_dict["supported_export_version"] = supported_export_version + if unresolved_refs is not UNSET: + field_dict["unresolved_refs"] = unresolved_refs + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.agent_import_preview_response_unresolved_refs_item import ( + AgentImportPreviewResponseUnresolvedRefsItem, + ) + + d = dict(src_dict) + + def _parse_agent_name(data: object) -> None | str: + if data is None: + return data + return cast(None | str, data) + + agent_name = _parse_agent_name(d.pop("agent_name")) + + alert_configs = d.pop("alert_configs") + + def _parse_description(data: object) -> None | str: + if data is None: + return data + return cast(None | str, data) + + description = _parse_description(d.pop("description")) + + evaluation_criteria = d.pop("evaluation_criteria") + + governance_policies = d.pop("governance_policies") + + ok = d.pop("ok") + + schedules = d.pop("schedules") + + step_count = d.pop("step_count") + + def _parse_payload_export_version(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + payload_export_version = _parse_payload_export_version( + d.pop("payload_export_version", UNSET) + ) + + solutions = d.pop("solutions", UNSET) + + supported_export_version = d.pop("supported_export_version", UNSET) + + _unresolved_refs = d.pop("unresolved_refs", UNSET) + unresolved_refs: list[AgentImportPreviewResponseUnresolvedRefsItem] | Unset = ( + UNSET + ) + if _unresolved_refs is not UNSET: + unresolved_refs = [] + for unresolved_refs_item_data in _unresolved_refs: + unresolved_refs_item = ( + AgentImportPreviewResponseUnresolvedRefsItem.from_dict( + unresolved_refs_item_data + ) + ) + + unresolved_refs.append(unresolved_refs_item) + + agent_import_preview_response = cls( + agent_name=agent_name, + alert_configs=alert_configs, + description=description, + evaluation_criteria=evaluation_criteria, + governance_policies=governance_policies, + ok=ok, + schedules=schedules, + step_count=step_count, + payload_export_version=payload_export_version, + solutions=solutions, + supported_export_version=supported_export_version, + unresolved_refs=unresolved_refs, + ) + + agent_import_preview_response.additional_properties = d + return agent_import_preview_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/agent_import_preview_response_unresolved_refs_item.py b/seclai/_generated/models/agent_import_preview_response_unresolved_refs_item.py new file mode 100644 index 0000000..bb355d8 --- /dev/null +++ b/seclai/_generated/models/agent_import_preview_response_unresolved_refs_item.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="AgentImportPreviewResponseUnresolvedRefsItem") + + +@_attrs_define +class AgentImportPreviewResponseUnresolvedRefsItem: + """ """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + agent_import_preview_response_unresolved_refs_item = cls() + + agent_import_preview_response_unresolved_refs_item.additional_properties = d + return agent_import_preview_response_unresolved_refs_item + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/agent_run_response.py b/seclai/_generated/models/agent_run_response.py index d7dc09c..264c2c9 100644 --- a/seclai/_generated/models/agent_run_response.py +++ b/seclai/_generated/models/agent_run_response.py @@ -14,6 +14,7 @@ if TYPE_CHECKING: from ..models.agent_run_attempt_response import AgentRunAttemptResponse from ..models.agent_run_step_response import AgentRunStepResponse + from ..models.governance_policy_ref_response import GovernancePolicyRefResponse T = TypeVar("T", bound="AgentRunResponse") @@ -31,6 +32,16 @@ class AgentRunResponse: priority (bool): Indicates if the run was treated as a priority execution. run_id (str): Unique identifier for the agent run. status (PendingProcessingCompletedFailedStatus): + blocked_policies (list[GovernancePolicyRefResponse] | Unset): Governance policies that produced at least one + BLOCK verdict during this run. Deduplicated by policy id. + flagged_policies (list[GovernancePolicyRefResponse] | Unset): Governance policies that produced at least one + FLAG verdict during this run. Deduplicated by policy id. + governance_input_status (None | str | Unset): Result of the governance input evaluation: safe, blocked, skipped, + or timed_out. + governance_input_wait_ms (int | None | Unset): Milliseconds spent waiting for governance input evaluation. + input_scan_status (None | str | Unset): Result of the prompt injection scan: safe, unsafe, skipped, timed_out, + or error. + scan_wait_ms (int | None | Unset): Milliseconds spent waiting for prompt injection scan. steps (list[AgentRunStepResponse] | None | Unset): Step outputs and per-step timing/credits. Only included when requested. """ @@ -43,6 +54,12 @@ class AgentRunResponse: priority: bool run_id: str status: PendingProcessingCompletedFailedStatus + blocked_policies: list[GovernancePolicyRefResponse] | Unset = UNSET + flagged_policies: list[GovernancePolicyRefResponse] | Unset = UNSET + governance_input_status: None | str | Unset = UNSET + governance_input_wait_ms: int | None | Unset = UNSET + input_scan_status: None | str | Unset = UNSET + scan_wait_ms: int | None | Unset = UNSET steps: list[AgentRunStepResponse] | None | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -69,6 +86,44 @@ def to_dict(self) -> dict[str, Any]: status = self.status.value + blocked_policies: list[dict[str, Any]] | Unset = UNSET + if not isinstance(self.blocked_policies, Unset): + blocked_policies = [] + for blocked_policies_item_data in self.blocked_policies: + blocked_policies_item = blocked_policies_item_data.to_dict() + blocked_policies.append(blocked_policies_item) + + flagged_policies: list[dict[str, Any]] | Unset = UNSET + if not isinstance(self.flagged_policies, Unset): + flagged_policies = [] + for flagged_policies_item_data in self.flagged_policies: + flagged_policies_item = flagged_policies_item_data.to_dict() + flagged_policies.append(flagged_policies_item) + + governance_input_status: None | str | Unset + if isinstance(self.governance_input_status, Unset): + governance_input_status = UNSET + else: + governance_input_status = self.governance_input_status + + governance_input_wait_ms: int | None | Unset + if isinstance(self.governance_input_wait_ms, Unset): + governance_input_wait_ms = UNSET + else: + governance_input_wait_ms = self.governance_input_wait_ms + + input_scan_status: None | str | Unset + if isinstance(self.input_scan_status, Unset): + input_scan_status = UNSET + else: + input_scan_status = self.input_scan_status + + scan_wait_ms: int | None | Unset + if isinstance(self.scan_wait_ms, Unset): + scan_wait_ms = UNSET + else: + scan_wait_ms = self.scan_wait_ms + steps: list[dict[str, Any]] | None | Unset if isinstance(self.steps, Unset): steps = UNSET @@ -95,6 +150,18 @@ def to_dict(self) -> dict[str, Any]: "status": status, } ) + if blocked_policies is not UNSET: + field_dict["blocked_policies"] = blocked_policies + if flagged_policies is not UNSET: + field_dict["flagged_policies"] = flagged_policies + if governance_input_status is not UNSET: + field_dict["governance_input_status"] = governance_input_status + if governance_input_wait_ms is not UNSET: + field_dict["governance_input_wait_ms"] = governance_input_wait_ms + if input_scan_status is not UNSET: + field_dict["input_scan_status"] = input_scan_status + if scan_wait_ms is not UNSET: + field_dict["scan_wait_ms"] = scan_wait_ms if steps is not UNSET: field_dict["steps"] = steps @@ -104,6 +171,7 @@ def to_dict(self) -> dict[str, Any]: def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.agent_run_attempt_response import AgentRunAttemptResponse from ..models.agent_run_step_response import AgentRunStepResponse + from ..models.governance_policy_ref_response import GovernancePolicyRefResponse d = dict(src_dict) attempts = [] @@ -142,6 +210,68 @@ def _parse_output(data: object) -> None | str: status = PendingProcessingCompletedFailedStatus(d.pop("status")) + _blocked_policies = d.pop("blocked_policies", UNSET) + blocked_policies: list[GovernancePolicyRefResponse] | Unset = UNSET + if _blocked_policies is not UNSET: + blocked_policies = [] + for blocked_policies_item_data in _blocked_policies: + blocked_policies_item = GovernancePolicyRefResponse.from_dict( + blocked_policies_item_data + ) + + blocked_policies.append(blocked_policies_item) + + _flagged_policies = d.pop("flagged_policies", UNSET) + flagged_policies: list[GovernancePolicyRefResponse] | Unset = UNSET + if _flagged_policies is not UNSET: + flagged_policies = [] + for flagged_policies_item_data in _flagged_policies: + flagged_policies_item = GovernancePolicyRefResponse.from_dict( + flagged_policies_item_data + ) + + flagged_policies.append(flagged_policies_item) + + def _parse_governance_input_status(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + governance_input_status = _parse_governance_input_status( + d.pop("governance_input_status", UNSET) + ) + + def _parse_governance_input_wait_ms(data: object) -> int | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(int | None | Unset, data) + + governance_input_wait_ms = _parse_governance_input_wait_ms( + d.pop("governance_input_wait_ms", UNSET) + ) + + def _parse_input_scan_status(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + input_scan_status = _parse_input_scan_status(d.pop("input_scan_status", UNSET)) + + def _parse_scan_wait_ms(data: object) -> int | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(int | None | Unset, data) + + scan_wait_ms = _parse_scan_wait_ms(d.pop("scan_wait_ms", UNSET)) + def _parse_steps(data: object) -> list[AgentRunStepResponse] | None | Unset: if data is None: return data @@ -175,6 +305,12 @@ def _parse_steps(data: object) -> list[AgentRunStepResponse] | None | Unset: priority=priority, run_id=run_id, status=status, + blocked_policies=blocked_policies, + flagged_policies=flagged_policies, + governance_input_status=governance_input_status, + governance_input_wait_ms=governance_input_wait_ms, + input_scan_status=input_scan_status, + scan_wait_ms=scan_wait_ms, steps=steps, ) diff --git a/seclai/_generated/models/agent_summary_response.py b/seclai/_generated/models/agent_summary_response.py index 6ac329c..d4be5d8 100644 --- a/seclai/_generated/models/agent_summary_response.py +++ b/seclai/_generated/models/agent_summary_response.py @@ -12,6 +12,7 @@ from ..models.agent_summary_response_sampling_config_type_0 import ( AgentSummaryResponseSamplingConfigType0, ) + from ..models.import_skip_response import ImportSkipResponse T = TypeVar("T", bound="AgentSummaryResponse") @@ -30,6 +31,9 @@ class AgentSummaryResponse: default_evaluation_tier (None | str | Unset): Default evaluation tier: fast, balanced, or thorough. evaluation_mode (str | Unset): Evaluation mode: output_expectation, eval_and_retry, or sample_and_flag. Default: 'eval_and_retry'. + import_warnings (list[ImportSkipResponse] | None | Unset): One entry per item dropped or substituted during + import. Present only on endpoints that accept agent_definition; null on non-import calls; [] when the import had + no skips. max_retries (int | Unset): Max retries for eval_and_retry mode. Default: 3. prompt_model_auto_rollback_enabled (bool | Unset): Whether automatic rollback is enabled for upgraded models. Default: False. @@ -50,6 +54,7 @@ class AgentSummaryResponse: updated_at: str default_evaluation_tier: None | str | Unset = UNSET evaluation_mode: str | Unset = "eval_and_retry" + import_warnings: list[ImportSkipResponse] | None | Unset = UNSET max_retries: int | Unset = 3 prompt_model_auto_rollback_enabled: bool | Unset = False prompt_model_auto_rollback_triggers: list[str] | None | Unset = UNSET @@ -85,6 +90,18 @@ def to_dict(self) -> dict[str, Any]: evaluation_mode = self.evaluation_mode + import_warnings: list[dict[str, Any]] | None | Unset + if isinstance(self.import_warnings, Unset): + import_warnings = UNSET + elif isinstance(self.import_warnings, list): + import_warnings = [] + for import_warnings_type_0_item_data in self.import_warnings: + import_warnings_type_0_item = import_warnings_type_0_item_data.to_dict() + import_warnings.append(import_warnings_type_0_item) + + else: + import_warnings = self.import_warnings + max_retries = self.max_retries prompt_model_auto_rollback_enabled = self.prompt_model_auto_rollback_enabled @@ -130,6 +147,8 @@ def to_dict(self) -> dict[str, Any]: field_dict["default_evaluation_tier"] = default_evaluation_tier if evaluation_mode is not UNSET: field_dict["evaluation_mode"] = evaluation_mode + if import_warnings is not UNSET: + field_dict["import_warnings"] = import_warnings if max_retries is not UNSET: field_dict["max_retries"] = max_retries if prompt_model_auto_rollback_enabled is not UNSET: @@ -156,6 +175,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.agent_summary_response_sampling_config_type_0 import ( AgentSummaryResponseSamplingConfigType0, ) + from ..models.import_skip_response import ImportSkipResponse d = dict(src_dict) created_at = d.pop("created_at") @@ -193,6 +213,32 @@ def _parse_default_evaluation_tier(data: object) -> None | str | Unset: evaluation_mode = d.pop("evaluation_mode", UNSET) + def _parse_import_warnings( + data: object, + ) -> list[ImportSkipResponse] | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, list): + raise TypeError() + import_warnings_type_0 = [] + _import_warnings_type_0 = data + for import_warnings_type_0_item_data in _import_warnings_type_0: + import_warnings_type_0_item = ImportSkipResponse.from_dict( + import_warnings_type_0_item_data + ) + + import_warnings_type_0.append(import_warnings_type_0_item) + + return import_warnings_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(list[ImportSkipResponse] | None | Unset, data) + + import_warnings = _parse_import_warnings(d.pop("import_warnings", UNSET)) + max_retries = d.pop("max_retries", UNSET) prompt_model_auto_rollback_enabled = d.pop( @@ -258,6 +304,7 @@ def _parse_sampling_config( updated_at=updated_at, default_evaluation_tier=default_evaluation_tier, evaluation_mode=evaluation_mode, + import_warnings=import_warnings, max_retries=max_retries, prompt_model_auto_rollback_enabled=prompt_model_auto_rollback_enabled, prompt_model_auto_rollback_triggers=prompt_model_auto_rollback_triggers, diff --git a/seclai/_generated/models/create_agent_request.py b/seclai/_generated/models/create_agent_request.py index a3579fc..ac3f9d7 100644 --- a/seclai/_generated/models/create_agent_request.py +++ b/seclai/_generated/models/create_agent_request.py @@ -1,13 +1,22 @@ from __future__ import annotations from collections.abc import Mapping -from typing import Any, TypeVar, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field from ..types import UNSET, Unset +if TYPE_CHECKING: + from ..models.create_agent_request_agent_definition_type_0 import ( + CreateAgentRequestAgentDefinitionType0, + ) + from ..models.create_agent_request_entity_remap_type_0 import ( + CreateAgentRequestEntityRemapType0, + ) + + T = TypeVar("T", bound="CreateAgentRequest") @@ -16,22 +25,47 @@ class CreateAgentRequest: """ Attributes: name (str): Name for the new agent. + agent_definition (CreateAgentRequestAgentDefinitionType0 | None | Unset): Optional payload in the same format + produced by GET /agents/{id}/export. When provided, replaces any template-derived workflow and pre-fills + metadata/trigger fields the request does not specify explicitly. Validation errors include line/column + references against a canonical pretty-printed echo of the supplied payload. agent_template (None | str | Unset): Template to initialize the agent from. Values: blank, retrieval_example, simple_qa, summarizer, json_extractor, content_change_notifier, scheduled_report, webhook_pipeline. description (None | str | Unset): Optional description. + entity_remap (CreateAgentRequestEntityRemapType0 | None | Unset): Optional UUID-substitution map applied to the + imported workflow before save. Each key is a source-account UUID (as returned by /agents/preview-import's + ``unresolved_refs``); each value is the target-account UUID to substitute. Used to relink knowledge bases, + memory banks, source connections, and sub-agents on cross-account imports. trigger_type (str | Unset): Trigger type: dynamic_input, template_input, schedule, new_content. Default: 'dynamic_input'. """ name: str + agent_definition: CreateAgentRequestAgentDefinitionType0 | None | Unset = UNSET agent_template: None | str | Unset = UNSET description: None | str | Unset = UNSET + entity_remap: CreateAgentRequestEntityRemapType0 | None | Unset = UNSET trigger_type: str | Unset = "dynamic_input" additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.create_agent_request_agent_definition_type_0 import ( + CreateAgentRequestAgentDefinitionType0, + ) + from ..models.create_agent_request_entity_remap_type_0 import ( + CreateAgentRequestEntityRemapType0, + ) + name = self.name + agent_definition: dict[str, Any] | None | Unset + if isinstance(self.agent_definition, Unset): + agent_definition = UNSET + elif isinstance(self.agent_definition, CreateAgentRequestAgentDefinitionType0): + agent_definition = self.agent_definition.to_dict() + else: + agent_definition = self.agent_definition + agent_template: None | str | Unset if isinstance(self.agent_template, Unset): agent_template = UNSET @@ -44,6 +78,14 @@ def to_dict(self) -> dict[str, Any]: else: description = self.description + entity_remap: dict[str, Any] | None | Unset + if isinstance(self.entity_remap, Unset): + entity_remap = UNSET + elif isinstance(self.entity_remap, CreateAgentRequestEntityRemapType0): + entity_remap = self.entity_remap.to_dict() + else: + entity_remap = self.entity_remap + trigger_type = self.trigger_type field_dict: dict[str, Any] = {} @@ -53,10 +95,14 @@ def to_dict(self) -> dict[str, Any]: "name": name, } ) + if agent_definition is not UNSET: + field_dict["agent_definition"] = agent_definition if agent_template is not UNSET: field_dict["agent_template"] = agent_template if description is not UNSET: field_dict["description"] = description + if entity_remap is not UNSET: + field_dict["entity_remap"] = entity_remap if trigger_type is not UNSET: field_dict["trigger_type"] = trigger_type @@ -64,9 +110,37 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.create_agent_request_agent_definition_type_0 import ( + CreateAgentRequestAgentDefinitionType0, + ) + from ..models.create_agent_request_entity_remap_type_0 import ( + CreateAgentRequestEntityRemapType0, + ) + d = dict(src_dict) name = d.pop("name") + def _parse_agent_definition( + data: object, + ) -> CreateAgentRequestAgentDefinitionType0 | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + agent_definition_type_0 = ( + CreateAgentRequestAgentDefinitionType0.from_dict(data) + ) + + return agent_definition_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(CreateAgentRequestAgentDefinitionType0 | None | Unset, data) + + agent_definition = _parse_agent_definition(d.pop("agent_definition", UNSET)) + def _parse_agent_template(data: object) -> None | str | Unset: if data is None: return data @@ -85,12 +159,33 @@ def _parse_description(data: object) -> None | str | Unset: description = _parse_description(d.pop("description", UNSET)) + def _parse_entity_remap( + data: object, + ) -> CreateAgentRequestEntityRemapType0 | None | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + entity_remap_type_0 = CreateAgentRequestEntityRemapType0.from_dict(data) + + return entity_remap_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(CreateAgentRequestEntityRemapType0 | None | Unset, data) + + entity_remap = _parse_entity_remap(d.pop("entity_remap", UNSET)) + trigger_type = d.pop("trigger_type", UNSET) create_agent_request = cls( name=name, + agent_definition=agent_definition, agent_template=agent_template, description=description, + entity_remap=entity_remap, trigger_type=trigger_type, ) diff --git a/seclai/_generated/models/create_agent_request_agent_definition_type_0.py b/seclai/_generated/models/create_agent_request_agent_definition_type_0.py new file mode 100644 index 0000000..97dd852 --- /dev/null +++ b/seclai/_generated/models/create_agent_request_agent_definition_type_0.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="CreateAgentRequestAgentDefinitionType0") + + +@_attrs_define +class CreateAgentRequestAgentDefinitionType0: + """ """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + create_agent_request_agent_definition_type_0 = cls() + + create_agent_request_agent_definition_type_0.additional_properties = d + return create_agent_request_agent_definition_type_0 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/create_agent_request_entity_remap_type_0.py b/seclai/_generated/models/create_agent_request_entity_remap_type_0.py new file mode 100644 index 0000000..ff7ec40 --- /dev/null +++ b/seclai/_generated/models/create_agent_request_entity_remap_type_0.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="CreateAgentRequestEntityRemapType0") + + +@_attrs_define +class CreateAgentRequestEntityRemapType0: + """ """ + + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + create_agent_request_entity_remap_type_0 = cls() + + create_agent_request_entity_remap_type_0.additional_properties = d + return create_agent_request_entity_remap_type_0 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> str: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: str) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/generate_step_config_request.py b/seclai/_generated/models/generate_step_config_request.py index 6807cfe..fed5801 100644 --- a/seclai/_generated/models/generate_step_config_request.py +++ b/seclai/_generated/models/generate_step_config_request.py @@ -24,7 +24,7 @@ class GenerateStepConfigRequest: """ Attributes: - step_type (str): The step type to generate config for (e.g. 'transform', 'gate', 'text', 'prompt_call', + step_type (str): The step type to generate config for (e.g. 'regex_replace', 'gate', 'text', 'prompt_call', 'retrieval'). user_input (str): Natural language description of what the step should do. agent_steps (list[GenerateStepConfigRequestAgentStepsType0Item] | None | Unset): Current agent step hierarchy diff --git a/seclai/_generated/models/governance_policy_ref_response.py b/seclai/_generated/models/governance_policy_ref_response.py new file mode 100644 index 0000000..6877f33 --- /dev/null +++ b/seclai/_generated/models/governance_policy_ref_response.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, cast + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="GovernancePolicyRefResponse") + + +@_attrs_define +class GovernancePolicyRefResponse: + """Reference to a governance policy by id and name. + + Attributes: + policy_id (str): Governance policy identifier. + policy_name (None | str | Unset): Display name of the policy at evaluation time. May be null when the policy has + been deleted. + """ + + policy_id: str + policy_name: None | str | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + policy_id = self.policy_id + + policy_name: None | str | Unset + if isinstance(self.policy_name, Unset): + policy_name = UNSET + else: + policy_name = self.policy_name + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "policy_id": policy_id, + } + ) + if policy_name is not UNSET: + field_dict["policy_name"] = policy_name + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + policy_id = d.pop("policy_id") + + def _parse_policy_name(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + policy_name = _parse_policy_name(d.pop("policy_name", UNSET)) + + governance_policy_ref_response = cls( + policy_id=policy_id, + policy_name=policy_name, + ) + + governance_policy_ref_response.additional_properties = d + return governance_policy_ref_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/import_field_error_model.py b/seclai/_generated/models/import_field_error_model.py new file mode 100644 index 0000000..4b184a0 --- /dev/null +++ b/seclai/_generated/models/import_field_error_model.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="ImportFieldErrorModel") + + +@_attrs_define +class ImportFieldErrorModel: + """Single agent_definition validation error with source position. + + Attributes: + column (int): 1-indexed column in `source`. + line (int): 1-indexed line in `source`. + message (str): Human-readable description of the problem. + path (str): Dotted path of the offending field, e.g. `agent.definition.child_steps[0].step_type`. + """ + + column: int + line: int + message: str + path: str + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + column = self.column + + line = self.line + + message = self.message + + path = self.path + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "column": column, + "line": line, + "message": message, + "path": path, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + column = d.pop("column") + + line = d.pop("line") + + message = d.pop("message") + + path = d.pop("path") + + import_field_error_model = cls( + column=column, + line=line, + message=message, + path=path, + ) + + import_field_error_model.additional_properties = d + return import_field_error_model + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/import_skip_response.py b/seclai/_generated/models/import_skip_response.py new file mode 100644 index 0000000..5e91c2e --- /dev/null +++ b/seclai/_generated/models/import_skip_response.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +if TYPE_CHECKING: + from ..models.import_skip_response_details import ImportSkipResponseDetails + + +T = TypeVar("T", bound="ImportSkipResponse") + + +@_attrs_define +class ImportSkipResponse: + """One item that was not applied during an agent import. + + Used as the element type for ``import_warnings`` on every + response model that accepts an ``agent_definition`` payload. + See :py:class:`services.agent_definition_import.AgentImportSkip` + for the full category list. + + Lives here (not on each router) so the authenticated and public + API responses share one definition — keeping the shape that + clients (UI modal, MCP, OpenAPI consumers) depend on aligned. + + Attributes: + category (str): The kind of item that was skipped or substituted: 'schedule', 'evaluation_criteria', + 'alert_config', 'alert_recipient', 'governance_policy', 'governance_kb_link', 'solution_link'. + message (str): Human-readable explanation of what was skipped and why. + details (ImportSkipResponseDetails | Unset): Category-specific identifiers for the skipped item (step_id, + alert_type, kb_name, etc.). Stable keys per category; absent keys are simply not applicable. + """ + + category: str + message: str + details: ImportSkipResponseDetails | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + category = self.category + + message = self.message + + details: dict[str, Any] | Unset = UNSET + if not isinstance(self.details, Unset): + details = self.details.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "category": category, + "message": message, + } + ) + if details is not UNSET: + field_dict["details"] = details + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.import_skip_response_details import ImportSkipResponseDetails + + d = dict(src_dict) + category = d.pop("category") + + message = d.pop("message") + + _details = d.pop("details", UNSET) + details: ImportSkipResponseDetails | Unset + if isinstance(_details, Unset): + details = UNSET + else: + details = ImportSkipResponseDetails.from_dict(_details) + + import_skip_response = cls( + category=category, + message=message, + details=details, + ) + + import_skip_response.additional_properties = d + return import_skip_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/import_skip_response_details.py b/seclai/_generated/models/import_skip_response_details.py new file mode 100644 index 0000000..364cd8b --- /dev/null +++ b/seclai/_generated/models/import_skip_response_details.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="ImportSkipResponseDetails") + + +@_attrs_define +class ImportSkipResponseDetails: + """Category-specific identifiers for the skipped item (step_id, alert_type, kb_name, etc.). Stable keys per category; + absent keys are simply not applicable. + + """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + import_skip_response_details = cls() + + import_skip_response_details.additional_properties = d + return import_skip_response_details + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/proposed_action_response.py b/seclai/_generated/models/proposed_action_response.py index 733a169..651d102 100644 --- a/seclai/_generated/models/proposed_action_response.py +++ b/seclai/_generated/models/proposed_action_response.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar, cast from attrs import define as _attrs_define from attrs import field as _attrs_field @@ -10,6 +10,9 @@ if TYPE_CHECKING: from ..models.proposed_action_response_params import ProposedActionResponseParams + from ..models.proposed_action_response_preview_type_0 import ( + ProposedActionResponsePreviewType0, + ) T = TypeVar("T", bound="ProposedActionResponse") @@ -24,15 +27,27 @@ class ProposedActionResponse: description (str): Human-readable description of the action. params (ProposedActionResponseParams): Parameters for the action. is_destructive (bool | Unset): Whether the action is destructive. Default: False. + preview (None | ProposedActionResponsePreviewType0 | Unset): Planning-time dry-run preview attached by the + solution AI assistant for create_agent / update_agent actions. Contains ``steps`` (the generated step tree), + ``step_count``, ``warnings`` (a mix of heuristic structural issues — e.g. brittle JSONPath, pass-through + ``regex_replace``, ``prompt_call`` missing a model — and deterministic resource-usage issues: every pre-bound + knowledge base / memory bank must be referenced by at least one step, and no step may reference an unknown id), + and ``skipped`` / ``skipped_reason`` when preview couldn't run (e.g. the action depends on resources created + earlier in the same plan). ``None`` for non-agent actions or when generation failed. """ action_type: str description: str params: ProposedActionResponseParams is_destructive: bool | Unset = False + preview: None | ProposedActionResponsePreviewType0 | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.proposed_action_response_preview_type_0 import ( + ProposedActionResponsePreviewType0, + ) + action_type = self.action_type description = self.description @@ -41,6 +56,14 @@ def to_dict(self) -> dict[str, Any]: is_destructive = self.is_destructive + preview: dict[str, Any] | None | Unset + if isinstance(self.preview, Unset): + preview = UNSET + elif isinstance(self.preview, ProposedActionResponsePreviewType0): + preview = self.preview.to_dict() + else: + preview = self.preview + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( @@ -52,6 +75,8 @@ def to_dict(self) -> dict[str, Any]: ) if is_destructive is not UNSET: field_dict["is_destructive"] = is_destructive + if preview is not UNSET: + field_dict["preview"] = preview return field_dict @@ -60,6 +85,9 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.proposed_action_response_params import ( ProposedActionResponseParams, ) + from ..models.proposed_action_response_preview_type_0 import ( + ProposedActionResponsePreviewType0, + ) d = dict(src_dict) action_type = d.pop("action_type") @@ -70,11 +98,31 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: is_destructive = d.pop("is_destructive", UNSET) + def _parse_preview( + data: object, + ) -> None | ProposedActionResponsePreviewType0 | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + preview_type_0 = ProposedActionResponsePreviewType0.from_dict(data) + + return preview_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | ProposedActionResponsePreviewType0 | Unset, data) + + preview = _parse_preview(d.pop("preview", UNSET)) + proposed_action_response = cls( action_type=action_type, description=description, params=params, is_destructive=is_destructive, + preview=preview, ) proposed_action_response.additional_properties = d diff --git a/seclai/_generated/models/proposed_action_response_preview_type_0.py b/seclai/_generated/models/proposed_action_response_preview_type_0.py new file mode 100644 index 0000000..cf8e37a --- /dev/null +++ b/seclai/_generated/models/proposed_action_response_preview_type_0.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="ProposedActionResponsePreviewType0") + + +@_attrs_define +class ProposedActionResponsePreviewType0: + """ """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + proposed_action_response_preview_type_0 = cls() + + proposed_action_response_preview_type_0.additional_properties = d + return proposed_action_response_preview_type_0 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/update_agent_request.py b/seclai/_generated/models/update_agent_request.py index 6fcb8e1..ae34ee8 100644 --- a/seclai/_generated/models/update_agent_request.py +++ b/seclai/_generated/models/update_agent_request.py @@ -10,6 +10,12 @@ from ..types import UNSET, Unset if TYPE_CHECKING: + from ..models.update_agent_request_agent_definition_type_0 import ( + UpdateAgentRequestAgentDefinitionType0, + ) + from ..models.update_agent_request_entity_remap_type_0 import ( + UpdateAgentRequestEntityRemapType0, + ) from ..models.update_agent_request_sampling_config_type_0 import ( UpdateAgentRequestSamplingConfigType0, ) @@ -22,8 +28,15 @@ class UpdateAgentRequest: """ Attributes: + agent_definition (None | Unset | UpdateAgentRequestAgentDefinitionType0): Optional payload in the same format + produced by GET /agents/{id}/export. When provided, agent metadata fields the request does not set explicitly + are taken from the payload, and the agent's workflow is replaced from `agent.definition`. The previous version + is preserved in history. Validation errors include line/column references against a canonical pretty-printed + echo of the supplied payload. default_evaluation_tier (None | str | Unset): Default evaluation tier: 'fast', 'balanced', or 'thorough'. description (None | str | Unset): New description for the agent. + entity_remap (None | Unset | UpdateAgentRequestEntityRemapType0): Optional UUID-substitution map applied to the + imported workflow before save (same shape as on POST /agents). evaluation_mode (None | str | Unset): Evaluation mode: 'output_expectation', 'eval_and_retry', or 'sample_and_flag'. max_retries (int | None | Unset): Max retries for eval_and_retry mode (1-10). @@ -45,8 +58,10 @@ class UpdateAgentRequest: Default: False. """ + agent_definition: None | Unset | UpdateAgentRequestAgentDefinitionType0 = UNSET default_evaluation_tier: None | str | Unset = UNSET description: None | str | Unset = UNSET + entity_remap: None | Unset | UpdateAgentRequestEntityRemapType0 = UNSET evaluation_mode: None | str | Unset = UNSET max_retries: int | None | Unset = UNSET name: None | str | Unset = UNSET @@ -63,10 +78,24 @@ class UpdateAgentRequest: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.update_agent_request_agent_definition_type_0 import ( + UpdateAgentRequestAgentDefinitionType0, + ) + from ..models.update_agent_request_entity_remap_type_0 import ( + UpdateAgentRequestEntityRemapType0, + ) from ..models.update_agent_request_sampling_config_type_0 import ( UpdateAgentRequestSamplingConfigType0, ) + agent_definition: dict[str, Any] | None | Unset + if isinstance(self.agent_definition, Unset): + agent_definition = UNSET + elif isinstance(self.agent_definition, UpdateAgentRequestAgentDefinitionType0): + agent_definition = self.agent_definition.to_dict() + else: + agent_definition = self.agent_definition + default_evaluation_tier: None | str | Unset if isinstance(self.default_evaluation_tier, Unset): default_evaluation_tier = UNSET @@ -79,6 +108,14 @@ def to_dict(self) -> dict[str, Any]: else: description = self.description + entity_remap: dict[str, Any] | None | Unset + if isinstance(self.entity_remap, Unset): + entity_remap = UNSET + elif isinstance(self.entity_remap, UpdateAgentRequestEntityRemapType0): + entity_remap = self.entity_remap.to_dict() + else: + entity_remap = self.entity_remap + evaluation_mode: None | str | Unset if isinstance(self.evaluation_mode, Unset): evaluation_mode = UNSET @@ -153,10 +190,14 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({}) + if agent_definition is not UNSET: + field_dict["agent_definition"] = agent_definition if default_evaluation_tier is not UNSET: field_dict["default_evaluation_tier"] = default_evaluation_tier if description is not UNSET: field_dict["description"] = description + if entity_remap is not UNSET: + field_dict["entity_remap"] = entity_remap if evaluation_mode is not UNSET: field_dict["evaluation_mode"] = evaluation_mode if max_retries is not UNSET: @@ -192,12 +233,39 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.update_agent_request_agent_definition_type_0 import ( + UpdateAgentRequestAgentDefinitionType0, + ) + from ..models.update_agent_request_entity_remap_type_0 import ( + UpdateAgentRequestEntityRemapType0, + ) from ..models.update_agent_request_sampling_config_type_0 import ( UpdateAgentRequestSamplingConfigType0, ) d = dict(src_dict) + def _parse_agent_definition( + data: object, + ) -> None | Unset | UpdateAgentRequestAgentDefinitionType0: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + agent_definition_type_0 = ( + UpdateAgentRequestAgentDefinitionType0.from_dict(data) + ) + + return agent_definition_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | Unset | UpdateAgentRequestAgentDefinitionType0, data) + + agent_definition = _parse_agent_definition(d.pop("agent_definition", UNSET)) + def _parse_default_evaluation_tier(data: object) -> None | str | Unset: if data is None: return data @@ -218,6 +286,25 @@ def _parse_description(data: object) -> None | str | Unset: description = _parse_description(d.pop("description", UNSET)) + def _parse_entity_remap( + data: object, + ) -> None | Unset | UpdateAgentRequestEntityRemapType0: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + entity_remap_type_0 = UpdateAgentRequestEntityRemapType0.from_dict(data) + + return entity_remap_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | Unset | UpdateAgentRequestEntityRemapType0, data) + + entity_remap = _parse_entity_remap(d.pop("entity_remap", UNSET)) + def _parse_evaluation_mode(data: object) -> None | str | Unset: if data is None: return data @@ -343,8 +430,10 @@ def _parse_sampling_config( set_sampling_config = d.pop("set_sampling_config", UNSET) update_agent_request = cls( + agent_definition=agent_definition, default_evaluation_tier=default_evaluation_tier, description=description, + entity_remap=entity_remap, evaluation_mode=evaluation_mode, max_retries=max_retries, name=name, diff --git a/seclai/_generated/models/update_agent_request_agent_definition_type_0.py b/seclai/_generated/models/update_agent_request_agent_definition_type_0.py new file mode 100644 index 0000000..e90a89b --- /dev/null +++ b/seclai/_generated/models/update_agent_request_agent_definition_type_0.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="UpdateAgentRequestAgentDefinitionType0") + + +@_attrs_define +class UpdateAgentRequestAgentDefinitionType0: + """ """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + update_agent_request_agent_definition_type_0 = cls() + + update_agent_request_agent_definition_type_0.additional_properties = d + return update_agent_request_agent_definition_type_0 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/_generated/models/update_agent_request_entity_remap_type_0.py b/seclai/_generated/models/update_agent_request_entity_remap_type_0.py new file mode 100644 index 0000000..17eb665 --- /dev/null +++ b/seclai/_generated/models/update_agent_request_entity_remap_type_0.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="UpdateAgentRequestEntityRemapType0") + + +@_attrs_define +class UpdateAgentRequestEntityRemapType0: + """ """ + + additional_properties: dict[str, str] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + update_agent_request_entity_remap_type_0 = cls() + + update_agent_request_entity_remap_type_0.additional_properties = d + return update_agent_request_entity_remap_type_0 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> str: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: str) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/seclai/seclai.py b/seclai/seclai.py index 41a90e8..36e01bc 100644 --- a/seclai/seclai.py +++ b/seclai/seclai.py @@ -1621,6 +1621,37 @@ def export_agent(self, agent_id: str, *, download: bool = True) -> dict[str, Any self.request("GET", f"/agents/{agent_id}/export", params=params), ) + def preview_import_agent(self, body: dict[str, Any]) -> dict[str, Any]: + """Validate an ``agent_definition`` payload without creating an agent. + + Use before :meth:`create_agent` or :meth:`update_agent` with an + ``agent_definition`` to surface ``unresolved_refs`` — workflow + references to knowledge bases, memory banks, source connections, or + sub-agents that don't exist in the target account. Pass the returned + ids back in ``entity_remap`` on the commit call to substitute them. + + Args: + body: ``{"agent_definition": ...}`` — same shape produced by + :meth:`export_agent`. + + Returns: + Summary with step/schedule/alert/criteria/policy counts and any + ``unresolved_refs``. + + Raises: + SeclaiAPIStatusError: For any non-success response, including + HTTP 422 when the ``agent_definition`` fails validation. + On 422 the response body (available as + ``SeclaiAPIStatusError.response_text``) is an + ``AgentDefinitionImportErrorResponse`` listing each field + error with a 1-indexed line/column anchored to the canonical + ``source`` echo. + """ + return cast( + dict[str, Any], + self.request("POST", "/agents/preview-import", json=body), + ) + # ── Agent Definitions ───────────────────────────────────────────────────── def get_agent_definition(self, agent_id: str) -> dict[str, Any]: @@ -4924,6 +4955,37 @@ async def export_agent( await self.request("GET", f"/agents/{agent_id}/export", params=params), ) + async def preview_import_agent(self, body: dict[str, Any]) -> dict[str, Any]: + """Validate an ``agent_definition`` payload without creating an agent. + + Use before :meth:`create_agent` or :meth:`update_agent` with an + ``agent_definition`` to surface ``unresolved_refs`` — workflow + references to knowledge bases, memory banks, source connections, or + sub-agents that don't exist in the target account. Pass the returned + ids back in ``entity_remap`` on the commit call to substitute them. + + Args: + body: ``{"agent_definition": ...}`` — same shape produced by + :meth:`export_agent`. + + Returns: + Summary with step/schedule/alert/criteria/policy counts and any + ``unresolved_refs``. + + Raises: + SeclaiAPIStatusError: For any non-success response, including + HTTP 422 when the ``agent_definition`` fails validation. + On 422 the response body (available as + ``SeclaiAPIStatusError.response_text``) is an + ``AgentDefinitionImportErrorResponse`` listing each field + error with a 1-indexed line/column anchored to the canonical + ``source`` echo. + """ + return cast( + dict[str, Any], + await self.request("POST", "/agents/preview-import", json=body), + ) + # ── Agent Definitions ───────────────────────────────────────────────────── async def get_agent_definition(self, agent_id: str) -> dict[str, Any]: diff --git a/tests/test_new_methods.py b/tests/test_new_methods.py index bb98490..be8b08f 100644 --- a/tests/test_new_methods.py +++ b/tests/test_new_methods.py @@ -120,6 +120,35 @@ def handler(req: httpx.Request) -> httpx.Response: client.delete_agent("a1") assert seen == {"method": "DELETE", "path": "/agents/a1"} + def test_preview_import_agent(self) -> None: + seen: dict[str, Any] = {} + + def handler(req: httpx.Request) -> httpx.Response: + seen["method"] = req.method + seen["path"] = req.url.path + seen["body"] = json.loads(req.content) + return _json_response( + { + "ok": True, + "agent_name": "n", + "description": None, + "step_count": 0, + "schedules": 0, + "alert_configs": 0, + "evaluation_criteria": 0, + "governance_policies": 0, + } + ) + + client = _sync_client(handler) + result = client.preview_import_agent( + {"agent_definition": {"agent": {"name": "n"}}} + ) + assert seen["method"] == "POST" + assert seen["path"] == "/agents/preview-import" + assert seen["body"] == {"agent_definition": {"agent": {"name": "n"}}} + assert result["ok"] is True + # --------------------------------------------------------------------------- # Agent Definitions @@ -1468,6 +1497,24 @@ async def handler(req: httpx.Request) -> httpx.Response: assert seen == {"method": "POST", "path": "/agents"} assert result["id"] == "a1" + @pytest.mark.asyncio + async def test_async_preview_import_agent(self) -> None: + seen: dict[str, Any] = {} + + async def handler(req: httpx.Request) -> httpx.Response: + seen["method"] = req.method + seen["path"] = req.url.path + seen["body"] = json.loads(req.content) + return _json_response({"ok": True}) + + client = _async_client(handler) + await client.preview_import_agent( + {"agent_definition": {"agent": {"name": "n"}}} + ) + assert seen["method"] == "POST" + assert seen["path"] == "/agents/preview-import" + assert seen["body"] == {"agent_definition": {"agent": {"name": "n"}}} + @pytest.mark.asyncio async def test_async_delete_agent(self) -> None: seen: dict[str, Any] = {}