payi 0.1.0a90__py3-none-any.whl → 0.1.0a91__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.

Potentially problematic release.


This version of payi might be problematic. Click here for more details.

payi/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "payi"
4
- __version__ = "0.1.0-alpha.90" # x-release-please-version
4
+ __version__ = "0.1.0-alpha.91" # x-release-please-version
@@ -173,10 +173,15 @@ class _AnthropicProviderRequest(_ProviderRequest):
173
173
 
174
174
  messages = kwargs.get("messages")
175
175
  if messages:
176
+
176
177
  anthropic_has_image_and_get_texts(self, messages)
177
178
 
178
179
  return True
179
180
 
181
+ @override
182
+ def remove_inline_data(self, prompt: 'dict[str, Any]') -> bool:
183
+ return anthropic_remove_inline_data(prompt)
184
+
180
185
  @override
181
186
  def process_exception(self, exception: Exception, kwargs: Any, ) -> bool:
182
187
  try:
@@ -352,4 +357,30 @@ def has_image_and_get_texts(encoding: tiktoken.Encoding, content: Union[str, 'li
352
357
  token_count = sum(len(encoding.encode(item.get("text", ""))) for item in content if item.get("type") == "text")
353
358
  return has_image, token_count
354
359
 
355
- return False, 0
360
+ return False, 0
361
+
362
+ def anthropic_remove_inline_data(prompt: 'dict[str, Any]') -> bool:# noqa: ARG002
363
+ messages = prompt.get("messages", [])
364
+ if not messages:
365
+ return False
366
+
367
+ modified = False
368
+ for message in messages:
369
+ content = message.get('content', Any)
370
+ if not content or not isinstance(content, list):
371
+ continue
372
+
373
+ for item in content: # type: ignore
374
+ if not isinstance(item, dict):
375
+ continue
376
+ # item: dict[str, Any]
377
+ type = item.get("type", "") # type: ignore
378
+ if type != "image":
379
+ continue
380
+
381
+ source = item.get("source", {}) # type: ignore
382
+ if source.get("type", "") == "base64": # type: ignore
383
+ source["data"] = _PayiInstrumentor._not_instrumented
384
+ modified = True
385
+
386
+ return modified
@@ -356,6 +356,24 @@ class _BedrockInvokeProviderRequest(_BedrockProviderRequest):
356
356
 
357
357
  return response
358
358
 
359
+ @override
360
+ def remove_inline_data(self, prompt: 'dict[str, Any]') -> bool:# noqa: ARG002
361
+ if not self._is_anthropic:
362
+ return False
363
+
364
+ from .AnthropicInstrumentor import anthropic_remove_inline_data
365
+ body = prompt.get("body", "")
366
+ if not body:
367
+ return False
368
+
369
+ body_json = json.loads(body)
370
+
371
+ if anthropic_remove_inline_data(body_json):
372
+ prompt["body"] = json.dumps(body_json)
373
+ return True
374
+
375
+ return False
376
+
359
377
  class _BedrockConverseProviderRequest(_BedrockProviderRequest):
360
378
  @override
361
379
  def process_synchronous_response(
@@ -301,9 +301,9 @@ class _OpenAiProviderRequest(_ProviderRequest):
301
301
  units["text"] = Units(input=input, output=output)
302
302
 
303
303
  @staticmethod
304
- def has_image_and_get_texts(encoding: tiktoken.Encoding, content: Union[str, 'list[Any]'], image_type: str = "image_url", text_type:str = "text") -> 'tuple[bool, int]':
304
+ def has_image_and_get_texts(encoding: tiktoken.Encoding, content: Union[str, 'list[Any]'], image_type: str, text_type: str) -> 'tuple[bool, int]':
305
305
  if isinstance(content, list): # type: ignore
306
- has_image = any(item.get("type") == image_type for item in content)
306
+ has_image = any(item.get("type", "") == image_type for item in content)
307
307
  if has_image is False:
308
308
  return has_image, 0
309
309
 
@@ -311,6 +311,28 @@ class _OpenAiProviderRequest(_ProviderRequest):
311
311
  return has_image, token_count
312
312
  return False, 0
313
313
 
314
+ @staticmethod
315
+ def post_process_request_prompt(content: Union[str, 'list[Any]'], image_type: str, url_subkey: bool) -> bool:
316
+ modified = False
317
+ if isinstance(content, list): # type: ignore
318
+ for item in content:
319
+ type = item.get("type", "")
320
+ if type != image_type:
321
+ continue
322
+
323
+ if url_subkey:
324
+ url = item.get("image_url", {}).get("url", "")
325
+ if url.startswith("data:"):
326
+ item["image_url"]["url"] = _PayiInstrumentor._not_instrumented
327
+ modified = True
328
+ else:
329
+ url = item.get("image_url", "")
330
+ if url.startswith("data:"):
331
+ item["image_url"] = _PayiInstrumentor._not_instrumented
332
+ modified = True
333
+
334
+ return modified
335
+
314
336
  class _OpenAiEmbeddingsProviderRequest(_OpenAiProviderRequest):
315
337
  def __init__(self, instrumentor: _PayiInstrumentor):
316
338
  super().__init__(
@@ -397,7 +419,7 @@ class _OpenAiChatProviderRequest(_OpenAiProviderRequest):
397
419
 
398
420
  if enc:
399
421
  for message in messages:
400
- msg_has_image, msg_prompt_tokens = self.has_image_and_get_texts(enc, message.get('content', ''))
422
+ msg_has_image, msg_prompt_tokens = self.has_image_and_get_texts(enc, message.get('content', ''), image_type="image_url", text_type="text")
401
423
  if msg_has_image:
402
424
  has_image = True
403
425
  estimated_token_count += msg_prompt_tokens
@@ -418,6 +440,13 @@ class _OpenAiChatProviderRequest(_OpenAiProviderRequest):
418
440
  self._include_usage_added = True
419
441
  return True
420
442
 
443
+ @override
444
+ def remove_inline_data(self, prompt: 'dict[str, Any]') -> bool:
445
+ messages = prompt.get("messages", None)
446
+ if not messages:
447
+ return False
448
+ return self.post_process_request_prompt(messages, image_type="image_url", url_subkey=True)
449
+
421
450
  @override
422
451
  def process_synchronous_response(
423
452
  self,
@@ -531,6 +560,21 @@ class _OpenAiResponsesProviderRequest(_OpenAiProviderRequest):
531
560
 
532
561
  return True
533
562
 
563
+ @override
564
+ def remove_inline_data(self, prompt: 'dict[str, Any]') -> bool:
565
+ modified = False
566
+ input = prompt.get("input", [])
567
+ for item in input:
568
+ if not isinstance(item, dict):
569
+ continue
570
+
571
+ for key, value in item.items(): # type: ignore
572
+ if key == "content":
573
+ if isinstance(value, list):
574
+ modified = self.post_process_request_prompt(value, image_type="input_image", url_subkey=False) | modified # type: ignore
575
+
576
+ return modified
577
+
534
578
  @override
535
579
  def process_synchronous_response(
536
580
  self,
payi/lib/VertexRequest.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import math
3
3
  from typing import Any, Optional
4
+ from typing_extensions import override
4
5
 
5
6
  from payi.lib.helpers import PayiCategories
6
7
  from payi.types.ingest_units_params import Units
@@ -62,7 +63,22 @@ class _VertexRequest(_ProviderRequest): # type: ignore
62
63
  ingest = True
63
64
 
64
65
  return _ChunkResult(send_chunk_to_caller=True, ingest=ingest)
65
-
66
+
67
+ @override
68
+ def remove_inline_data(self, prompt: 'dict[str, Any]') -> bool:
69
+ modified = False
70
+
71
+ parts: list[dict[str, Any]] = prompt["contents"].get("parts", [])
72
+ for part in parts:
73
+ inline_data = part.get("inline_data", {})
74
+ if not isinstance(inline_data, dict):
75
+ continue
76
+ if "data" in inline_data:
77
+ inline_data["data"] = _PayiInstrumentor._not_instrumented
78
+ modified = True
79
+
80
+ return modified
81
+
66
82
  def process_response_part_for_function_call(self, part: 'dict[str, Any]') -> None:
67
83
  function = part.get("function_call", {})
68
84
  if not function:
payi/lib/instrument.py CHANGED
@@ -67,6 +67,9 @@ class _ProviderRequest:
67
67
  def process_initial_stream_response(self, response: Any) -> None:
68
68
  pass
69
69
 
70
+ def remove_inline_data(self, prompt: 'dict[str, Any]') -> bool:# noqa: ARG002
71
+ return False
72
+
70
73
  @property
71
74
  def is_aws_client(self) -> bool:
72
75
  return self._is_aws_client if self._is_aws_client is not None else False
@@ -133,6 +136,7 @@ class _ProviderRequest:
133
136
  class PayiInstrumentConfig(TypedDict, total=False):
134
137
  proxy: bool
135
138
  global_instrumentation: bool
139
+ instrument_inline_data: bool
136
140
  limit_ids: Optional["list[str]"]
137
141
  use_case_name: Optional[str]
138
142
  use_case_id: Optional[str]
@@ -183,6 +187,8 @@ class _TrackContext:
183
187
  _instrumentor.__exit__(exc_type, exc_val, exc_tb)
184
188
 
185
189
  class _PayiInstrumentor:
190
+ _not_instrumented: str = "<not_instrumented>"
191
+
186
192
  def __init__(
187
193
  self,
188
194
  payi: Optional[Payi],
@@ -219,6 +225,8 @@ class _PayiInstrumentor:
219
225
  # default is instrument and ingest metrics
220
226
  self._proxy_default: bool = global_config.get("proxy", False)
221
227
 
228
+ self._instrument_inline_data: bool = global_config.get("instrument_inline_data", False)
229
+
222
230
  global_instrumentation = global_config.pop("global_instrumentation", True)
223
231
 
224
232
  if instruments is None or "*" in instruments:
@@ -345,6 +353,18 @@ class _PayiInstrumentor:
345
353
  # convert the function call builder to a list of function calls
346
354
  ingest_units["provider_response_function_calls"] = list(request._function_call_builder.values())
347
355
 
356
+ request_json = ingest_units.get('provider_request_json', "")
357
+ if request_json and self._instrument_inline_data is False:
358
+ try:
359
+ prompt_dict = json.loads(request_json)
360
+ if request.remove_inline_data(prompt_dict):
361
+ self._logger.debug(f"Removed inline data from provider_request_json")
362
+ # store the modified dict back as JSON string
363
+ ingest_units['provider_request_json'] = json.dumps(prompt_dict)
364
+
365
+ except Exception as e:
366
+ self._logger.error(f"Error serializing provider_request_json: {e}")
367
+
348
368
  if int(ingest_units.get("http_status_code") or 0) < 400:
349
369
  units = ingest_units.get("units", {})
350
370
  if not units or all(unit.get("input", 0) == 0 and unit.get("output", 0) == 0 for unit in units.values()):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: payi
3
- Version: 0.1.0a90
3
+ Version: 0.1.0a91
4
4
  Summary: The official Python library for the payi API
5
5
  Project-URL: Homepage, https://github.com/Pay-i/pay-i-python
6
6
  Project-URL: Repository, https://github.com/Pay-i/pay-i-python
@@ -11,7 +11,7 @@ payi/_resource.py,sha256=j2jIkTr8OIC8sU6-05nxSaCyj4MaFlbZrwlyg4_xJos,1088
11
11
  payi/_response.py,sha256=rh9oJAvCKcPwQFm4iqH_iVrmK8bNx--YP_A2a4kN1OU,28776
12
12
  payi/_streaming.py,sha256=Z_wIyo206T6Jqh2rolFg2VXZgX24PahLmpURp0-NssU,10092
13
13
  payi/_types.py,sha256=7jE5MoQQFVoVxw5vVzvZ2Ao0kcjfNOGsBgyJfLBEnMo,6195
14
- payi/_version.py,sha256=tXFgPEeKbWMp7R7RIanP0VvZ88LuzOsIwp2Fn20klHs,165
14
+ payi/_version.py,sha256=wnUMU4k-h1Eu3R8s5wrOCNikJ4l9uxlc072i7sOabGE,165
15
15
  payi/pagination.py,sha256=k2356QGPOUSjRF2vHpwLBdF6P-2vnQzFfRIJQAHGQ7A,1258
16
16
  payi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  payi/_utils/__init__.py,sha256=PNZ_QJuzZEgyYXqkO1HVhGkj5IU9bglVUcw7H-Knjzw,2062
@@ -25,15 +25,15 @@ payi/_utils/_transform.py,sha256=n7kskEWz6o__aoNvhFoGVyDoalNe6mJwp-g7BWkdj88,156
25
25
  payi/_utils/_typing.py,sha256=D0DbbNu8GnYQTSICnTSHDGsYXj8TcAKyhejb0XcnjtY,4602
26
26
  payi/_utils/_utils.py,sha256=ts4CiiuNpFiGB6YMdkQRh2SZvYvsl7mAF-JWHCcLDf4,12312
27
27
  payi/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
28
- payi/lib/AnthropicInstrumentor.py,sha256=rpp1MUP2bvCNvVPAHn1xrzvupuIWR0pm4P8t1dR_m70,13103
29
- payi/lib/BedrockInstrumentor.py,sha256=Q-QggkVWOQPI84O4I30zirIIKJcU4Qj9YhflGznK8cE,17070
28
+ payi/lib/AnthropicInstrumentor.py,sha256=RPboYEubUg396a9irmy7dndXrtq0qy1DD81trJLIIBc,14090
29
+ payi/lib/BedrockInstrumentor.py,sha256=Vx0DUjdYzHWAuYsmK28z9Bxz-mj7UouEESsCN3B9Jn4,17588
30
30
  payi/lib/GoogleGenAiInstrumentor.py,sha256=DLbmlwmyOwooe7FuBkEAO_Z3xjPKpvQyO4VwLBeZnn4,8477
31
- payi/lib/OpenAIInstrumentor.py,sha256=Eiy6dGEuHKjPannndbt1E1dVF0FkDvtJy6tIZoYHY2A,20822
31
+ payi/lib/OpenAIInstrumentor.py,sha256=6HENrdN6kLMUwC00wNSdeGeK4fUDBWo2dCbVC9koi0c,22581
32
32
  payi/lib/Stopwatch.py,sha256=7OJlxvr2Jyb6Zr1LYCYKczRB7rDVKkIR7gc4YoleNdE,764
33
33
  payi/lib/VertexInstrumentor.py,sha256=IdahkOgB6ReBGpdgGCrBHplAFwK3KZ_XaRgFXVTodRU,7136
34
- payi/lib/VertexRequest.py,sha256=xKBURJHx4_bXMM8AijtRAjH8t-QukRLx6AIwsCw33Y0,9986
34
+ payi/lib/VertexRequest.py,sha256=NNH6S86nCHaU54Rb1rxai-b3UiJvatmzKkDKBmqMeKY,10530
35
35
  payi/lib/helpers.py,sha256=FPzNSSHGf9bgD6CanB7yVx_U8t4lm2c0jlZKrsziYlc,4242
36
- payi/lib/instrument.py,sha256=FB0X4y7Fc5q9gtOxkX4FomNiCLP7e7SOioDztX16lEk,62040
36
+ payi/lib/instrument.py,sha256=CiUtYwpTeoFbm7EEBXSbfaE5ZJKNuu7-ZfrXMi3hINM,62954
37
37
  payi/resources/__init__.py,sha256=1rtrPLWbNt8oJGOp6nwPumKLJ-ftez0B6qwLFyfcoP4,2972
38
38
  payi/resources/ingest.py,sha256=Z4WHv-INZoIlBzFw4o1j_PHykDsNACpkkF42Kik0UMg,23758
39
39
  payi/resources/categories/__init__.py,sha256=WeotN_d-0Ri8ohsrNPbve7RyViD9_N0NA9DrV3WYg3w,1701
@@ -145,7 +145,7 @@ payi/types/use_cases/definitions/kpi_retrieve_response.py,sha256=uQXliSvS3k-yDYw
145
145
  payi/types/use_cases/definitions/kpi_update_params.py,sha256=jbawdWAdMnsTWVH0qfQGb8W7_TXe3lq4zjSRu44d8p8,373
146
146
  payi/types/use_cases/definitions/kpi_update_response.py,sha256=zLyEoT0S8d7XHsnXZYT8tM7yDw0Aze0Mk-_Z6QeMtc8,459
147
147
  payi/types/use_cases/definitions/limit_config_create_params.py,sha256=pzQza_16N3z8cFNEKr6gPbFvuGFrwNuGxAYb--Kbo2M,449
148
- payi-0.1.0a90.dist-info/METADATA,sha256=gyCugCALPCJppx1mvByADB01sZ2mbBFbWJTEynnccjg,16333
149
- payi-0.1.0a90.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
150
- payi-0.1.0a90.dist-info/licenses/LICENSE,sha256=CQt03aM-P4a3Yg5qBg3JSLVoQS3smMyvx7tYg_6V7Gk,11334
151
- payi-0.1.0a90.dist-info/RECORD,,
148
+ payi-0.1.0a91.dist-info/METADATA,sha256=kiUf1xwcAkJHwPMPZfNsjuuOuA5nZUNZv1xaiHnHAh0,16333
149
+ payi-0.1.0a91.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
150
+ payi-0.1.0a91.dist-info/licenses/LICENSE,sha256=CQt03aM-P4a3Yg5qBg3JSLVoQS3smMyvx7tYg_6V7Gk,11334
151
+ payi-0.1.0a91.dist-info/RECORD,,