pangea-sdk 6.5.0b1__py3-none-any.whl → 6.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pangea/__init__.py CHANGED
@@ -1,15 +1,7 @@
1
- __version__ = "6.5.0beta1"
1
+ __version__ = "6.6.0"
2
2
 
3
- from pangea.asyncio.request import PangeaRequestAsync
4
3
  from pangea.config import PangeaConfig
5
4
  from pangea.file_uploader import FileUploader
6
- from pangea.request import PangeaRequest
7
- from pangea.response import PangeaResponse
5
+ from pangea.response import PangeaResponse, PangeaResponseResult, TransferMethod
8
6
 
9
- __all__ = (
10
- "FileUploader",
11
- "PangeaConfig",
12
- "PangeaRequest",
13
- "PangeaRequestAsync",
14
- "PangeaResponse",
15
- )
7
+ __all__ = ("FileUploader", "PangeaConfig", "PangeaResponse", "PangeaResponseResult", "TransferMethod")
pangea/_constants.py ADDED
@@ -0,0 +1,4 @@
1
+ from __future__ import annotations
2
+
3
+ MAX_RETRY_DELAY = 8.0
4
+ RETRYABLE_HTTP_CODES = frozenset({500, 502, 503, 504})
pangea/_typing.py ADDED
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterator, Sequence
4
+
5
+ from typing_extensions import Any, Protocol, SupportsIndex, TypeVar, overload
6
+
7
+ T = TypeVar("T")
8
+ T_co = TypeVar("T_co", covariant=True)
9
+
10
+
11
+ class SequenceNotStr(Protocol[T_co]):
12
+ """Sequence-like object that isn't str or bytes."""
13
+
14
+ @overload
15
+ def __getitem__(self, index: SupportsIndex, /) -> T_co: ...
16
+
17
+ @overload
18
+ def __getitem__(self, index: slice, /) -> Sequence[T_co]: ...
19
+
20
+ def __contains__(self, value: object, /) -> bool: ...
21
+
22
+ def __len__(self) -> int: ...
23
+
24
+ def __iter__(self) -> Iterator[T_co]: ...
25
+
26
+ def index(self, value: Any, start: int = ..., stop: int = ..., /) -> int: ...
27
+
28
+ def count(self, value: Any, /) -> int: ...
29
+
30
+ def __reversed__(self) -> Iterator[T_co]: ...
@@ -1,2 +1,3 @@
1
- # ruff: noqa: F401
2
1
  from .file_uploader import FileUploaderAsync
2
+
3
+ __all__ = ("FileUploaderAsync",)
@@ -6,9 +6,10 @@ from __future__ import annotations
6
6
  import io
7
7
  import logging
8
8
 
9
+ from pangea import PangeaConfig, TransferMethod
9
10
  from pangea.asyncio.request import PangeaRequestAsync
10
- from pangea.request import PangeaConfig
11
- from pangea.response import TransferMethod
11
+
12
+ __all__ = ("FileUploaderAsync",)
12
13
 
13
14
 
14
15
  class FileUploaderAsync:
pangea/asyncio/request.py CHANGED
@@ -10,19 +10,23 @@ import asyncio
10
10
  import json
11
11
  import time
12
12
  from collections.abc import Iterable, Mapping
13
- from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast, overload
13
+ from random import random
14
+ from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast
14
15
 
15
16
  import aiohttp
16
17
  from aiohttp import FormData
17
- from pydantic import BaseModel, TypeAdapter
18
+ from pydantic import BaseModel
18
19
  from pydantic_core import to_jsonable_python
19
- from typing_extensions import Any, Literal, TypeAlias, TypeVar, override
20
+ from typing_extensions import Any, TypeAlias, TypeVar, override
20
21
 
21
22
  import pangea.exceptions as pe
23
+ from pangea._constants import MAX_RETRY_DELAY, RETRYABLE_HTTP_CODES
22
24
  from pangea.request import MultipartResponse, PangeaRequestBase
23
25
  from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
24
26
  from pangea.utils import default_encoder
25
27
 
28
+ __all__ = ("PangeaRequestAsync",)
29
+
26
30
  _FileName: TypeAlias = Union[str, None]
27
31
  _FileContent: TypeAlias = Union[str, bytes]
28
32
  _FileContentType: TypeAlias = str
@@ -50,24 +54,6 @@ class PangeaRequestAsync(PangeaRequestBase):
50
54
  be set in PangeaConfig.
51
55
  """
52
56
 
53
- async def delete(self, endpoint: str) -> None:
54
- """
55
- Makes a DELETE call to a Pangea endpoint.
56
-
57
- Args:
58
- endpoint: The Pangea API endpoint.
59
- """
60
-
61
- url = self._url(endpoint)
62
-
63
- self.logger.debug(
64
- json.dumps({"service": self.service, "action": "delete", "url": url}, default=default_encoder)
65
- )
66
-
67
- requests_response = await self._http_delete(url, headers=self._headers())
68
- await self._check_http_errors(requests_response)
69
-
70
- @overload
71
57
  async def post(
72
58
  self,
73
59
  endpoint: str,
@@ -76,60 +62,18 @@ class PangeaRequestAsync(PangeaRequestBase):
76
62
  files: Optional[List[Tuple]] = None,
77
63
  poll_result: bool = True,
78
64
  url: Optional[str] = None,
79
- *,
80
- pangea_response: Literal[True] = True,
81
65
  ) -> PangeaResponse[TResult]:
82
- """
83
- Makes a POST call to a Pangea Service endpoint.
66
+ """Makes the POST call to a Pangea Service endpoint.
84
67
 
85
68
  Args:
86
- endpoint: The Pangea Service API endpoint.
87
- data: The POST body payload object
69
+ endpoint(str): The Pangea Service API endpoint.
70
+ data(dict): The POST body payload object
88
71
 
89
72
  Returns:
90
73
  PangeaResponse which contains the response in its entirety and
91
74
  various properties to retrieve individual fields
92
75
  """
93
76
 
94
- @overload
95
- async def post(
96
- self,
97
- endpoint: str,
98
- result_class: Type[TResult],
99
- data: str | BaseModel | Mapping[str, Any] | None = None,
100
- files: Optional[List[Tuple]] = None,
101
- poll_result: bool = True,
102
- url: Optional[str] = None,
103
- *,
104
- pangea_response: Literal[False],
105
- ) -> TResult:
106
- """
107
- Makes a POST call to a Pangea Service endpoint.
108
-
109
- Args:
110
- endpoint: The Pangea Service API endpoint.
111
- data: The POST body payload object
112
- """
113
-
114
- async def post(
115
- self,
116
- endpoint: str,
117
- result_class: Type[TResult],
118
- data: str | BaseModel | Mapping[str, Any] | None = None,
119
- files: Optional[List[Tuple]] = None,
120
- poll_result: bool = True,
121
- url: Optional[str] = None,
122
- *,
123
- pangea_response: bool = True,
124
- ) -> PangeaResponse[TResult] | TResult:
125
- """
126
- Makes a POST call to a Pangea Service endpoint.
127
-
128
- Args:
129
- endpoint: The Pangea Service API endpoint.
130
- data: The POST body payload object
131
- """
132
-
133
77
  if isinstance(data, BaseModel):
134
78
  data = data.model_dump(exclude_none=True)
135
79
 
@@ -169,13 +113,9 @@ class PangeaRequestAsync(PangeaRequestBase):
169
113
 
170
114
  await self._check_http_errors(requests_response)
171
115
 
172
- if not pangea_response:
173
- type_adapter = TypeAdapter(result_class)
174
- return type_adapter.validate_python(await requests_response.json())
175
-
176
116
  if "multipart/form-data" in requests_response.headers.get("content-type", ""):
177
117
  multipart_response = await self._process_multipart_response(requests_response)
178
- pangea_response_obj: PangeaResponse = PangeaResponse(
118
+ pangea_response: PangeaResponse = PangeaResponse(
179
119
  requests_response,
180
120
  result_class=result_class,
181
121
  json=multipart_response.pangea_json,
@@ -188,110 +128,49 @@ class PangeaRequestAsync(PangeaRequestBase):
188
128
  json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp})
189
129
  )
190
130
 
191
- pangea_response_obj = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
131
+ pangea_response = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
192
132
  except aiohttp.ContentTypeError as e:
193
133
  raise pe.PangeaException(
194
134
  f"Failed to decode json response. {e}. Body: {await requests_response.text()}"
195
135
  ) from e
196
136
 
197
137
  if poll_result:
198
- pangea_response_obj = await self._handle_queued_result(pangea_response_obj)
138
+ pangea_response = await self._handle_queued_result(pangea_response)
199
139
 
200
- return self._check_response(pangea_response_obj)
140
+ return self._check_response(pangea_response)
201
141
 
202
- @overload
203
- async def get(
204
- self,
205
- path: str,
206
- result_class: Type[TResult],
207
- check_response: bool = True,
208
- *,
209
- params: (
210
- Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
211
- | None
212
- ) = None,
213
- pangea_response: Literal[True] = True,
214
- ) -> PangeaResponse[TResult]:
215
- """
216
- Makes the GET call to a Pangea Service endpoint.
142
+ async def get(self, path: str, result_class: Type[TResult], check_response: bool = True) -> PangeaResponse[TResult]:
143
+ """Makes the GET call to a Pangea Service endpoint.
217
144
 
218
145
  Args:
219
- path: Additional URL path
220
- params: Dictionary of querystring data to attach to the request
146
+ endpoint(str): The Pangea Service API endpoint.
147
+ path(str): Additional URL path
221
148
 
222
149
  Returns:
223
150
  PangeaResponse which contains the response in its entirety and
224
151
  various properties to retrieve individual fields
225
152
  """
226
153
 
227
- @overload
228
- async def get(
229
- self,
230
- path: str,
231
- result_class: Type[TResult],
232
- check_response: bool = True,
233
- *,
234
- params: (
235
- Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
236
- | None
237
- ) = None,
238
- pangea_response: Literal[False] = False,
239
- ) -> TResult:
240
- """
241
- Makes the GET call to a Pangea Service endpoint.
242
-
243
- Args:
244
- path: Additional URL path
245
- params: Dictionary of querystring data to attach to the request
246
- """
247
-
248
- async def get(
249
- self,
250
- path: str,
251
- result_class: Type[TResult],
252
- check_response: bool = True,
253
- *,
254
- params: (
255
- Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
256
- | None
257
- ) = None,
258
- pangea_response: bool = True,
259
- ) -> PangeaResponse[TResult] | TResult:
260
- """
261
- Makes the GET call to a Pangea Service endpoint.
262
-
263
- Args:
264
- path: Additional URL path
265
- params: Dictionary of querystring data to attach to the request
266
- pangea_response: Whether or not the response body follows Pangea's
267
- standard response schema
268
- """
269
-
270
154
  url = self._url(path)
271
155
  self.logger.debug(json.dumps({"service": self.service, "action": "get", "url": url}))
272
156
 
273
- async with self.session.get(url, params=params, headers=self._headers()) as requests_response:
157
+ async with self.session.get(url, headers=self._headers()) as requests_response:
274
158
  await self._check_http_errors(requests_response)
275
-
276
- if not pangea_response:
277
- type_adapter = TypeAdapter(result_class)
278
- return type_adapter.validate_python(await requests_response.json())
279
-
280
- pangea_response_obj = PangeaResponse(
159
+ pangea_response = PangeaResponse(
281
160
  requests_response, result_class=result_class, json=await requests_response.json()
282
161
  )
283
162
 
284
163
  self.logger.debug(
285
164
  json.dumps(
286
- {"service": self.service, "action": "get", "url": url, "response": pangea_response_obj.json},
165
+ {"service": self.service, "action": "get", "url": url, "response": pangea_response.json},
287
166
  default=default_encoder,
288
167
  )
289
168
  )
290
169
 
291
170
  if check_response is False:
292
- return pangea_response_obj
171
+ return pangea_response
293
172
 
294
- return self._check_response(pangea_response_obj)
173
+ return self._check_response(pangea_response)
295
174
 
296
175
  async def _check_http_errors(self, resp: aiohttp.ClientResponse):
297
176
  if resp.status == 503:
@@ -425,14 +304,6 @@ class PangeaRequestAsync(PangeaRequestBase):
425
304
  attached_files = await self._get_attached_files(multipart_reader)
426
305
  return MultipartResponse(pangea_json, attached_files) # type: ignore[arg-type]
427
306
 
428
- async def _http_delete(
429
- self,
430
- url: str,
431
- *,
432
- headers: Mapping[str, str | bytes | None] = {},
433
- ) -> aiohttp.ClientResponse:
434
- return await self.session.delete(url, headers=headers)
435
-
436
307
  async def _http_post(
437
308
  self,
438
309
  url: str,
@@ -595,13 +466,46 @@ class PangeaRequestAsync(PangeaRequestBase):
595
466
 
596
467
  @override
597
468
  def _init_session(self) -> aiohttp.ClientSession:
598
- # retry_config = Retry(
599
- # total=self.config.request_retries,
600
- # backoff_factor=self.config.request_backoff,
601
- # status_forcelist=[500, 502, 503, 504],
602
- # )
603
- # adapter = HTTPAdapter(max_retries=retry_config)
604
- # TODO: Add retry config
605
-
606
- session = aiohttp.ClientSession()
607
- return session
469
+ return aiohttp.ClientSession(middlewares=[self._retry_middleware])
470
+
471
+ def _calculate_retry_timeout(self, remaining_retries: int) -> float:
472
+ max_retries = self.config.request_retries
473
+ nb_retries = min(max_retries - remaining_retries, 1000)
474
+ sleep_seconds = min(self.config.request_backoff * pow(2.0, nb_retries), MAX_RETRY_DELAY)
475
+ jitter = 1 - 0.25 * random()
476
+ timeout = sleep_seconds * jitter
477
+ return max(timeout, 0)
478
+
479
+ async def _retry_middleware(
480
+ self, request: aiohttp.ClientRequest, handler: aiohttp.ClientHandlerType
481
+ ) -> aiohttp.ClientResponse:
482
+ max_retries = self.config.request_retries
483
+ request_ids = set[str]()
484
+ retries_taken = 0
485
+ for retries_taken in range(max_retries + 1):
486
+ remaining_retries = max_retries - retries_taken
487
+
488
+ if len(request_ids) > 0:
489
+ request.headers["X-Pangea-Retried-Request-Ids"] = ",".join(request_ids)
490
+
491
+ response = await handler(request)
492
+
493
+ request_id = response.headers.get("x-request-id")
494
+ if request_id:
495
+ request_ids.add(request_id)
496
+
497
+ if not response.ok and remaining_retries > 0 and self._should_retry(response):
498
+ await self._sleep_for_retry(retries_taken=retries_taken, max_retries=max_retries)
499
+ continue
500
+
501
+ break
502
+
503
+ return response
504
+
505
+ def _should_retry(self, response: aiohttp.ClientResponse) -> bool:
506
+ return response.status in RETRYABLE_HTTP_CODES
507
+
508
+ async def _sleep_for_retry(self, *, retries_taken: int, max_retries: int) -> None:
509
+ remaining_retries = max_retries - retries_taken
510
+ timeout = self._calculate_retry_timeout(remaining_retries)
511
+ await asyncio.sleep(timeout)
@@ -1,5 +1,3 @@
1
- # ruff: noqa: F401
2
-
3
1
  from .ai_guard import AIGuardAsync
4
2
  from .audit import AuditAsync
5
3
  from .authn import AuthNAsync
@@ -7,9 +5,27 @@ from .authz import AuthZAsync
7
5
  from .embargo import EmbargoAsync
8
6
  from .file_scan import FileScanAsync
9
7
  from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
10
- from .management import ManagementAsync
11
8
  from .prompt_guard import PromptGuardAsync
12
9
  from .redact import RedactAsync
13
10
  from .sanitize import SanitizeAsync
14
11
  from .share import ShareAsync
15
12
  from .vault import VaultAsync
13
+
14
+ __all__ = (
15
+ "AIGuardAsync",
16
+ "AuditAsync",
17
+ "AuthNAsync",
18
+ "AuthZAsync",
19
+ "DomainIntelAsync",
20
+ "EmbargoAsync",
21
+ "FileIntelAsync",
22
+ "FileScanAsync",
23
+ "IpIntelAsync",
24
+ "PromptGuardAsync",
25
+ "RedactAsync",
26
+ "SanitizeAsync",
27
+ "ShareAsync",
28
+ "UrlIntelAsync",
29
+ "UserIntelAsync",
30
+ "VaultAsync",
31
+ )
@@ -1,30 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Mapping, Sequence
4
- from typing import Any, overload
5
-
6
- from typing_extensions import Literal, TypeVar
3
+ from collections.abc import Sequence
4
+ from typing import overload
7
5
 
8
6
  from pangea.asyncio.services.base import ServiceBaseAsync
9
7
  from pangea.config import PangeaConfig
10
8
  from pangea.response import PangeaResponse
11
9
  from pangea.services.ai_guard import (
12
- AuditDataActivityConfig,
13
- ConnectionsConfig,
14
- ExtraInfo,
15
- GuardResult,
16
10
  LogFields,
11
+ McpToolsMessage,
17
12
  Message,
18
13
  Overrides,
19
- RecipeConfig,
20
- ServiceConfig,
21
- ServiceConfigFilter,
22
- ServiceConfigsPage,
23
14
  TextGuardResult,
15
+ get_relevant_content,
16
+ patch_messages,
24
17
  )
25
18
 
26
- _T = TypeVar("_T")
27
-
28
19
 
29
20
  class AIGuardAsync(ServiceBaseAsync):
30
21
  """AI Guard service client.
@@ -100,11 +91,12 @@ class AIGuardAsync(ServiceBaseAsync):
100
91
  async def guard_text(
101
92
  self,
102
93
  *,
103
- messages: Sequence[Message],
94
+ messages: Sequence[Message | McpToolsMessage],
104
95
  recipe: str | None = None,
105
96
  debug: bool | None = None,
106
97
  overrides: Overrides | None = None,
107
98
  log_fields: LogFields | None = None,
99
+ only_relevant_content: bool = False,
108
100
  ) -> PangeaResponse[TextGuardResult]:
109
101
  """
110
102
  Guard LLM input and output text
@@ -127,6 +119,8 @@ class AIGuardAsync(ServiceBaseAsync):
127
119
  recipe: Recipe key of a configuration of data types and settings
128
120
  defined in the Pangea User Console. It specifies the rules that
129
121
  are to be applied to the text, such as defang malicious URLs.
122
+ only_relevant_content: Whether or not to only send relevant content
123
+ to AI Guard.
130
124
 
131
125
  Examples:
132
126
  response = await ai_guard.guard_text(messages=[Message(role="user", content="hello world")])
@@ -136,11 +130,12 @@ class AIGuardAsync(ServiceBaseAsync):
136
130
  self,
137
131
  text: str | None = None,
138
132
  *,
139
- messages: Sequence[Message] | None = None,
133
+ messages: Sequence[Message | McpToolsMessage] | None = None,
140
134
  recipe: str | None = None,
141
135
  debug: bool | None = None,
142
136
  overrides: Overrides | None = None,
143
137
  log_fields: LogFields | None = None,
138
+ only_relevant_content: bool = False,
144
139
  ) -> PangeaResponse[TextGuardResult]:
145
140
  """
146
141
  Guard LLM input and output text
@@ -166,6 +161,8 @@ class AIGuardAsync(ServiceBaseAsync):
166
161
  recipe: Recipe key of a configuration of data types and settings
167
162
  defined in the Pangea User Console. It specifies the rules that
168
163
  are to be applied to the text, such as defang malicious URLs.
164
+ only_relevant_content: Whether or not to only send relevant content
165
+ to AI Guard.
169
166
 
170
167
  Examples:
171
168
  response = await ai_guard.guard_text("text")
@@ -174,7 +171,11 @@ class AIGuardAsync(ServiceBaseAsync):
174
171
  if text is not None and messages is not None:
175
172
  raise ValueError("Exactly one of `text` or `messages` must be given")
176
173
 
177
- return await self.request.post(
174
+ if only_relevant_content and messages is not None:
175
+ original_messages = messages
176
+ messages, original_indices = get_relevant_content(messages)
177
+
178
+ response = await self.request.post(
178
179
  "v1/text/guard",
179
180
  TextGuardResult,
180
181
  data={
@@ -187,156 +188,9 @@ class AIGuardAsync(ServiceBaseAsync):
187
188
  },
188
189
  )
189
190
 
190
- async def guard(
191
- self,
192
- input: Mapping[str, Any],
193
- *,
194
- recipe: str | None = None,
195
- debug: bool | None = None,
196
- overrides: Overrides | None = None,
197
- app_id: str | None = None,
198
- actor_id: str | None = None,
199
- llm_provider: str | None = None,
200
- model: str | None = None,
201
- model_version: str | None = None,
202
- request_token_count: int | None = None,
203
- response_token_count: int | None = None,
204
- source_ip: str | None = None,
205
- source_location: str | None = None,
206
- tenant_id: str | None = None,
207
- event_type: Literal["input", "output"] | None = None,
208
- sensor_instance_id: str | None = None,
209
- extra_info: ExtraInfo | None = None,
210
- count_tokens: bool | None = None,
211
- ) -> PangeaResponse[GuardResult]:
212
- """
213
- Guard LLM input and output
214
-
215
- Analyze and redact content to avoid manipulation of the model, addition
216
- of malicious content, and other undesirable data transfers.
217
-
218
- OperationId: ai_guard_post_v1beta_guard
219
-
220
- Args:
221
- input: 'messages' (required) contains Prompt content and role array
222
- in JSON format. The `content` is the multimodal text or image
223
- input that will be analyzed. Additional properties such as
224
- 'tools' may be provided for analysis.
225
- recipe: Recipe key of a configuration of data types and settings defined in the Pangea User Console. It specifies the rules that are to be applied to the text, such as defang malicious URLs.
226
- debug: Setting this value to true will provide a detailed analysis of the text data
227
- app_name: Name of source application.
228
- llm_provider: Underlying LLM. Example: 'OpenAI'.
229
- model: Model used to perform the event. Example: 'gpt'.
230
- model_version: Model version used to perform the event. Example: '3.5'.
231
- request_token_count: Number of tokens in the request.
232
- response_token_count: Number of tokens in the response.
233
- source_ip: IP address of user or app or agent.
234
- source_location: Location of user or app or agent.
235
- tenant_id: For gateway-like integrations with multi-tenant support.
236
- event_type: (AIDR) Event Type.
237
- sensor_instance_id: (AIDR) sensor instance id.
238
- extra_info: (AIDR) Logging schema.
239
- count_tokens: Provide input and output token count.
240
- """
241
- return await self.request.post(
242
- "v1beta/guard",
243
- GuardResult,
244
- data={
245
- "input": input,
246
- "recipe": recipe,
247
- "debug": debug,
248
- "overrides": overrides,
249
- "app_id": app_id,
250
- "actor_id": actor_id,
251
- "llm_provider": llm_provider,
252
- "model": model,
253
- "model_version": model_version,
254
- "request_token_count": request_token_count,
255
- "response_token_count": response_token_count,
256
- "source_ip": source_ip,
257
- "source_location": source_location,
258
- "tenant_id": tenant_id,
259
- "event_type": event_type,
260
- "sensor_instance_id": sensor_instance_id,
261
- "extra_info": extra_info,
262
- "count_tokens": count_tokens,
263
- },
264
- )
265
-
266
- async def get_service_config(self, id: str) -> PangeaResponse[ServiceConfig]:
267
- """
268
- OperationId: ai_guard_post_v1beta_config
269
- """
270
- return await self.request.post("v1beta/config", data={"id": id}, result_class=ServiceConfig)
191
+ if only_relevant_content and response.result and response.result.prompt_messages:
192
+ response.result.prompt_messages = patch_messages(
193
+ original_messages, original_indices, response.result.prompt_messages
194
+ ) # type: ignore[assignment]
271
195
 
272
- async def create_service_config(
273
- self,
274
- name: str,
275
- *,
276
- id: str | None = None,
277
- audit_data_activity: AuditDataActivityConfig | None = None,
278
- connections: ConnectionsConfig | None = None,
279
- recipes: Mapping[str, RecipeConfig] | None = None,
280
- ) -> PangeaResponse[ServiceConfig]:
281
- """
282
- OperationId: ai_guard_post_v1beta_config_create
283
- """
284
- return await self.request.post(
285
- "v1beta/config/create",
286
- data={
287
- "name": name,
288
- "id": id,
289
- "audit_data_activity": audit_data_activity,
290
- "connections": connections,
291
- "recipes": recipes,
292
- },
293
- result_class=ServiceConfig,
294
- )
295
-
296
- async def update_service_config(
297
- self,
298
- id: str,
299
- name: str,
300
- *,
301
- audit_data_activity: AuditDataActivityConfig | None = None,
302
- connections: ConnectionsConfig | None = None,
303
- recipes: Mapping[str, RecipeConfig] | None = None,
304
- ) -> PangeaResponse[ServiceConfig]:
305
- """
306
- OperationId: ai_guard_post_v1beta_config_update
307
- """
308
- return await self.request.post(
309
- "v1beta/config/update",
310
- data={
311
- "id": id,
312
- "name": name,
313
- "audit_data_activity": audit_data_activity,
314
- "connections": connections,
315
- "recipes": recipes,
316
- },
317
- result_class=ServiceConfig,
318
- )
319
-
320
- async def delete_service_config(self, id: str) -> PangeaResponse[ServiceConfig]:
321
- """
322
- OperationId: ai_guard_post_v1beta_config_delete
323
- """
324
- return await self.request.post("v1beta/config/delete", data={"id": id}, result_class=ServiceConfig)
325
-
326
- async def list_service_configs(
327
- self,
328
- *,
329
- filter: ServiceConfigFilter | None = None,
330
- last: str | None = None,
331
- order: Literal["asc", "desc"] | None = None,
332
- order_by: Literal["id", "created_at", "updated_at"] | None = None,
333
- size: int | None = None,
334
- ) -> PangeaResponse[ServiceConfigsPage]:
335
- """
336
- OperationId: ai_guard_post_v1beta_config_list
337
- """
338
- return await self.request.post(
339
- "v1beta/config/list",
340
- data={"filter": filter, "last": last, "order": order, "order_by": order_by, "size": size},
341
- result_class=ServiceConfigsPage,
342
- )
196
+ return response