payi 0.1.0a70__py3-none-any.whl → 0.1.0a71__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/lib/instrument.py CHANGED
@@ -4,11 +4,13 @@ import uuid
4
4
  import asyncio
5
5
  import inspect
6
6
  import logging
7
+ import warnings
7
8
  import traceback
8
9
  from abc import abstractmethod
9
10
  from enum import Enum
10
- from typing import Any, Set, Union, Callable, Optional, TypedDict
11
+ from typing import Any, Set, Union, Callable, Optional, Sequence, TypedDict
11
12
  from datetime import datetime, timezone
13
+ from typing_extensions import deprecated
12
14
 
13
15
  import nest_asyncio # type: ignore
14
16
  from wrapt import ObjectProxy # type: ignore
@@ -38,20 +40,26 @@ class _ProviderRequest:
38
40
  return None
39
41
 
40
42
  @abstractmethod
41
- def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
43
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', args: Sequence[Any], kwargs: Any) -> bool:
44
+ ...
45
+
46
+ def process_request_prompt(self, prompt: 'dict[str, Any]', args: Sequence[Any], kwargs: 'dict[str, Any]') -> None:
42
47
  ...
43
48
 
44
49
  def is_bedrock(self) -> bool:
45
50
  return self._category == PayiCategories.aws_bedrock
46
-
51
+
52
+ def is_vertex(self) -> bool:
53
+ return self._category == PayiCategories.google_vertex
54
+
47
55
  def process_exception(self, exception: Exception, kwargs: Any, ) -> bool: # noqa: ARG002
48
- return False
56
+ self.exception_to_semantic_failure(exception)
57
+ return True
49
58
 
50
59
  def exception_to_semantic_failure(self, e: Exception) -> None:
51
60
  exception_str = f"{type(e).__name__}"
52
61
 
53
62
  fields: list[str] = []
54
- # fields += f"args: {e.args}"
55
63
 
56
64
  for attr in dir(e):
57
65
  if not attr.startswith("__"):
@@ -76,8 +84,8 @@ class PayiInstrumentConfig(TypedDict, total=False):
76
84
  proxy: bool
77
85
  global_instrumentation: bool
78
86
  limit_ids: Optional["list[str]"]
79
- experience_name: Optional[str]
80
- experience_id: Optional[str]
87
+ experience_name: Optional[str] = deprecated("experience_name is deprecated, use use_case_name instead") # type: ignore
88
+ experience_id: Optional[str] = deprecated("experience_id is deprecated, use use_case_id instead") # type: ignore
81
89
  use_case_name: Optional[str]
82
90
  use_case_id: Optional[str]
83
91
  use_case_version: Optional[int]
@@ -163,11 +171,12 @@ class _PayiInstrumentor:
163
171
  self._apayi = AsyncPayi()
164
172
 
165
173
  if "use_case_name" not in global_config and caller_filename:
174
+ description = f"Default use case for {caller_filename}.py"
166
175
  try:
167
176
  if self._payi:
168
- self._payi.use_cases.definitions.create(name=caller_filename, description='')
177
+ self._payi.use_cases.definitions.create(name=caller_filename, description=description)
169
178
  elif self._apayi:
170
- self._call_async_use_case_definition_create(use_case_name=caller_filename)
179
+ self._call_async_use_case_definition_create(use_case_name=caller_filename, use_case_description=description)
171
180
  global_config["use_case_name"] = caller_filename
172
181
  except Exception as e:
173
182
  logging.error(f"Error creating default use case definition based on file name {caller_filename}: {e}")
@@ -180,6 +189,7 @@ class _PayiInstrumentor:
180
189
  self._instrument_openai()
181
190
  self._instrument_anthropic()
182
191
  self._instrument_aws_bedrock()
192
+ self._instrument_google_vertex()
183
193
 
184
194
  def _instrument_specific(self, instruments: Set[str]) -> None:
185
195
  if PayiCategories.openai in instruments or PayiCategories.azure_openai in instruments:
@@ -188,6 +198,8 @@ class _PayiInstrumentor:
188
198
  self._instrument_anthropic()
189
199
  if PayiCategories.aws_bedrock in instruments:
190
200
  self._instrument_aws_bedrock()
201
+ if PayiCategories.google_vertex in instruments:
202
+ self._instrument_google_vertex()
191
203
 
192
204
  def _instrument_openai(self) -> None:
193
205
  from .OpenAIInstrumentor import OpenAiInstrumentor
@@ -216,6 +228,16 @@ class _PayiInstrumentor:
216
228
  except Exception as e:
217
229
  logging.error(f"Error instrumenting AWS bedrock: {e}")
218
230
 
231
+ def _instrument_google_vertex(self) -> None:
232
+ from .VertexInstrumentor import VertexInstrumentor
233
+
234
+ try:
235
+ VertexInstrumentor.instrument(self)
236
+
237
+ except Exception as e:
238
+ logging.error(f"Error instrumenting Google Vertex: {e}")
239
+
240
+
219
241
  def _process_ingest_units(self, ingest_units: IngestUnitsParams, log_data: 'dict[str, str]') -> bool:
220
242
  if int(ingest_units.get("http_status_code") or 0) < 400:
221
243
  units = ingest_units.get("units", {})
@@ -285,7 +307,7 @@ class _PayiInstrumentor:
285
307
 
286
308
  return None
287
309
 
288
- def _call_async_use_case_definition_create(self, use_case_name: str) -> None:
310
+ def _call_async_use_case_definition_create(self, use_case_name: str, use_case_description: str) -> None:
289
311
  if not self._apayi:
290
312
  return
291
313
 
@@ -297,10 +319,10 @@ class _PayiInstrumentor:
297
319
  try:
298
320
  if loop and loop.is_running():
299
321
  nest_asyncio.apply(loop) # type: ignore
300
- asyncio.run(self._apayi.use_cases.definitions.create(name=use_case_name, description=""))
322
+ asyncio.run(self._apayi.use_cases.definitions.create(name=use_case_name, description=use_case_description))
301
323
  else:
302
324
  # When there's no running loop, create a new one
303
- asyncio.run(self._apayi.use_cases.definitions.create(name=use_case_name, description=""))
325
+ asyncio.run(self._apayi.use_cases.definitions.create(name=use_case_name, description=use_case_description))
304
326
  except Exception as e:
305
327
  logging.error(f"Error calling async use_cases.definitions.create synchronously: {e}")
306
328
 
@@ -531,9 +553,10 @@ class _PayiInstrumentor:
531
553
 
532
554
  def _prepare_ingest(
533
555
  self,
534
- ingest: IngestUnitsParams,
556
+ request: _ProviderRequest,
535
557
  ingest_extra_headers: "dict[str, str]", # do not coflict potential kwargs["extra_headers"]
536
- kwargs: Any,
558
+ args: Sequence[Any],
559
+ kwargs: 'dict[str, Any]',
537
560
  ) -> None:
538
561
  limit_ids = ingest_extra_headers.pop(PayiHeaderNames.limit_ids, None)
539
562
  request_tags = ingest_extra_headers.pop(PayiHeaderNames.request_tags, None)
@@ -545,24 +568,24 @@ class _PayiInstrumentor:
545
568
  user_id = ingest_extra_headers.pop(PayiHeaderNames.user_id, None)
546
569
 
547
570
  if limit_ids:
548
- ingest["limit_ids"] = limit_ids.split(",")
571
+ request._ingest["limit_ids"] = limit_ids.split(",")
549
572
  if request_tags:
550
- ingest["request_tags"] = request_tags.split(",")
573
+ request._ingest["request_tags"] = request_tags.split(",")
551
574
  if experience_name:
552
- ingest["experience_name"] = experience_name
575
+ request._ingest["experience_name"] = experience_name
553
576
  if experience_id:
554
- ingest["experience_id"] = experience_id
577
+ request._ingest["experience_id"] = experience_id
555
578
  if use_case_name:
556
- ingest["use_case_name"] = use_case_name
579
+ request._ingest["use_case_name"] = use_case_name
557
580
  if use_case_id:
558
- ingest["use_case_id"] = use_case_id
581
+ request._ingest["use_case_id"] = use_case_id
559
582
  if use_case_version:
560
- ingest["use_case_version"] = int(use_case_version)
583
+ request._ingest["use_case_version"] = int(use_case_version)
561
584
  if user_id:
562
- ingest["user_id"] = user_id
585
+ request._ingest["user_id"] = user_id
563
586
 
564
587
  if len(ingest_extra_headers) > 0:
565
- ingest["provider_request_headers"] = [PayICommonModelsAPIRouterHeaderInfoParam(name=k, value=v) for k, v in ingest_extra_headers.items()]
588
+ request._ingest["provider_request_headers"] = [PayICommonModelsAPIRouterHeaderInfoParam(name=k, value=v) for k, v in ingest_extra_headers.items()]
566
589
 
567
590
  provider_prompt: "dict[str, Any]" = {}
568
591
  for k, v in kwargs.items():
@@ -572,24 +595,29 @@ class _PayiInstrumentor:
572
595
  pass
573
596
  else:
574
597
  try:
575
- json.dumps(v)
576
- provider_prompt[k] = v
577
- except (TypeError, ValueError):
598
+ if hasattr(v, "to_dict"):
599
+ provider_prompt[k] = v.to_dict()
600
+ else:
601
+ json.dumps(v)
602
+ provider_prompt[k] = v
603
+ except Exception as _e:
578
604
  pass
579
605
 
606
+ request.process_request_prompt(provider_prompt, args, kwargs)
607
+
580
608
  if self._log_prompt_and_response:
581
- ingest["provider_request_json"] = json.dumps(provider_prompt)
609
+ request._ingest["provider_request_json"] = json.dumps(provider_prompt)
582
610
 
583
- ingest["event_timestamp"] = datetime.now(timezone.utc)
611
+ request._ingest["event_timestamp"] = datetime.now(timezone.utc)
584
612
 
585
- async def achat_wrapper(
613
+ async def async_invoke_wrapper(
586
614
  self,
587
615
  request: _ProviderRequest,
588
616
  is_streaming: _IsStreaming,
589
617
  wrapped: Any,
590
618
  instance: Any,
591
- args: Any,
592
- kwargs: Any,
619
+ args: Sequence[Any],
620
+ kwargs: 'dict[str, Any]',
593
621
  ) -> Any:
594
622
  context = self.get_context()
595
623
 
@@ -615,7 +643,7 @@ class _PayiInstrumentor:
615
643
 
616
644
  request._ingest['properties'] = { 'system.stack_trace': json.dumps(stack) }
617
645
 
618
- if request.process_request(instance, extra_headers, kwargs) is False:
646
+ if request.process_request(instance, extra_headers, args, kwargs) is False:
619
647
  return await wrapped(*args, **kwargs)
620
648
 
621
649
  sw = Stopwatch()
@@ -629,7 +657,7 @@ class _PayiInstrumentor:
629
657
  stream = False
630
658
 
631
659
  try:
632
- self._prepare_ingest(request._ingest, extra_headers, kwargs)
660
+ self._prepare_ingest(request, extra_headers, args, kwargs)
633
661
  sw.start()
634
662
  response = await wrapped(*args, **kwargs)
635
663
 
@@ -644,6 +672,15 @@ class _PayiInstrumentor:
644
672
  raise e
645
673
 
646
674
  if stream:
675
+ if request.is_vertex():
676
+ return _GeneratorWrapper(
677
+ generator=response,
678
+ instance=instance,
679
+ instrumentor=self,
680
+ stopwatch=sw,
681
+ request=request,
682
+ log_prompt_and_response=self._log_prompt_and_response)
683
+
647
684
  stream_result = ChatStreamWrapper(
648
685
  response=response,
649
686
  instance=instance,
@@ -672,14 +709,14 @@ class _PayiInstrumentor:
672
709
 
673
710
  return response
674
711
 
675
- def chat_wrapper(
712
+ def invoke_wrapper(
676
713
  self,
677
714
  request: _ProviderRequest,
678
715
  is_streaming: _IsStreaming,
679
716
  wrapped: Any,
680
717
  instance: Any,
681
- args: Any,
682
- kwargs: Any,
718
+ args: Sequence[Any],
719
+ kwargs: 'dict[str, Any]',
683
720
  ) -> Any:
684
721
  context = self.get_context()
685
722
 
@@ -711,7 +748,7 @@ class _PayiInstrumentor:
711
748
 
712
749
  request._ingest['properties'] = { 'system.stack_trace': json.dumps(stack) }
713
750
 
714
- if request.process_request(instance, extra_headers, kwargs) is False:
751
+ if request.process_request(instance, extra_headers, args, kwargs) is False:
715
752
  return wrapped(*args, **kwargs)
716
753
 
717
754
  sw = Stopwatch()
@@ -725,10 +762,10 @@ class _PayiInstrumentor:
725
762
  stream = False
726
763
 
727
764
  try:
728
- self._prepare_ingest(request._ingest, extra_headers, kwargs)
765
+ self._prepare_ingest(request, extra_headers, args, kwargs)
729
766
  sw.start()
730
767
  response = wrapped(*args, **kwargs)
731
-
768
+
732
769
  except Exception as e: # pylint: disable=broad-except
733
770
  sw.stop()
734
771
  duration = sw.elapsed_ms_int()
@@ -740,6 +777,15 @@ class _PayiInstrumentor:
740
777
  raise e
741
778
 
742
779
  if stream:
780
+ if request.is_vertex():
781
+ return _GeneratorWrapper(
782
+ generator=response,
783
+ instance=instance,
784
+ instrumentor=self,
785
+ stopwatch=sw,
786
+ request=request,
787
+ log_prompt_and_response=self._log_prompt_and_response)
788
+
743
789
  stream_result = ChatStreamWrapper(
744
790
  response=response,
745
791
  instance=instance,
@@ -1046,6 +1092,92 @@ class ChatStreamWrapper(ObjectProxy): # type: ignore
1046
1092
  # assume dict
1047
1093
  return json.dumps(chunk)
1048
1094
 
1095
+ class _GeneratorWrapper: # type: ignore
1096
+ def __init__(
1097
+ self,
1098
+ generator: Any,
1099
+ instance: Any,
1100
+ instrumentor: _PayiInstrumentor,
1101
+ stopwatch: Stopwatch,
1102
+ request: _ProviderRequest,
1103
+ log_prompt_and_response: bool = True,
1104
+ ) -> None:
1105
+ super().__init__() # type: ignore
1106
+
1107
+ self._generator = generator
1108
+ self._instance = instance
1109
+ self._instrumentor = instrumentor
1110
+ self._stopwatch: Stopwatch = stopwatch
1111
+ self._log_prompt_and_response: bool = log_prompt_and_response
1112
+ self._responses: list[str] = []
1113
+ self._request: _ProviderRequest = request
1114
+ self._first_token: bool = True
1115
+ self._done: bool = False
1116
+
1117
+ def __iter__(self) -> Any:
1118
+ return self
1119
+
1120
+ def __aiter__(self) -> Any:
1121
+ return self
1122
+
1123
+ def __next__(self) -> Any:
1124
+ if self._done:
1125
+ raise StopIteration
1126
+
1127
+ try:
1128
+ chunk = next(self._generator)
1129
+ return self._process_chunk(chunk)
1130
+
1131
+ except StopIteration as stop_exception:
1132
+ self._process_stop_iteration()
1133
+ raise stop_exception
1134
+
1135
+ async def __anext__(self) -> Any:
1136
+ if self._done:
1137
+ raise StopAsyncIteration
1138
+
1139
+ try:
1140
+ chunk = await anext(self._generator) # type: ignore
1141
+ return self._process_chunk(chunk)
1142
+
1143
+ except StopAsyncIteration as stop_exception:
1144
+ await self._process_async_stop_iteration()
1145
+ raise stop_exception
1146
+
1147
+ def _process_chunk(self, chunk: Any) -> Any:
1148
+ if self._first_token:
1149
+ self._request._ingest["time_to_first_token_ms"] = self._stopwatch.elapsed_ms_int()
1150
+ self._first_token = False
1151
+
1152
+ if self._log_prompt_and_response:
1153
+ dict = chunk.to_dict() # type: ignore
1154
+ self._responses.append(json.dumps(dict))
1155
+
1156
+ self._request.process_chunk(chunk)
1157
+ return chunk
1158
+
1159
+ def _process_stop_iteration(self) -> None:
1160
+ self._stopwatch.stop()
1161
+ self._request._ingest["end_to_end_latency_ms"] = self._stopwatch.elapsed_ms_int()
1162
+ self._request._ingest["http_status_code"] = 200
1163
+
1164
+ if self._log_prompt_and_response:
1165
+ self._request._ingest["provider_response_json"] = self._responses
1166
+
1167
+ self._instrumentor._ingest_units(self._request._ingest)
1168
+ self._done = True
1169
+
1170
+ async def _process_async_stop_iteration(self) -> None:
1171
+ self._stopwatch.stop()
1172
+ self._request._ingest["end_to_end_latency_ms"] = self._stopwatch.elapsed_ms_int()
1173
+ self._request._ingest["http_status_code"] = 200
1174
+
1175
+ if self._log_prompt_and_response:
1176
+ self._request._ingest["provider_response_json"] = self._responses
1177
+
1178
+ await self._instrumentor._aingest_units(self._request._ingest)
1179
+ self._done = True
1180
+
1049
1181
  global _instrumentor
1050
1182
  _instrumentor: Optional[_PayiInstrumentor] = None
1051
1183
 
@@ -1057,7 +1189,7 @@ def payi_instrument(
1057
1189
  config: Optional[PayiInstrumentConfig] = None,
1058
1190
  ) -> None:
1059
1191
  global _instrumentor
1060
- if _instrumentor:
1192
+ if (_instrumentor):
1061
1193
  return
1062
1194
 
1063
1195
  payi_param: Optional[Payi] = None
@@ -1141,8 +1273,6 @@ def track(
1141
1273
 
1142
1274
  def track_context(
1143
1275
  limit_ids: Optional["list[str]"] = None,
1144
- experience_name: Optional[str] = None,
1145
- experience_id: Optional[str] = None,
1146
1276
  use_case_name: Optional[str] = None,
1147
1277
  use_case_id: Optional[str] = None,
1148
1278
  use_case_version: Optional[int] = None,
@@ -1152,14 +1282,9 @@ def track_context(
1152
1282
  resource_scope: Optional[str] = None,
1153
1283
  proxy: Optional[bool] = None,
1154
1284
  ) -> _TrackContext:
1155
- if not _instrumentor:
1156
- raise RuntimeError("Pay-i instrumentor not initialized. Use payi_instrument() to initialize.")
1157
-
1158
1285
  # Create a new context for tracking
1159
1286
  context: _Context = {}
1160
1287
  context["limit_ids"] = limit_ids
1161
- context["experience_name"] = experience_name
1162
- context["experience_id"] = experience_id
1163
1288
  context["use_case_name"] = use_case_name
1164
1289
  context["use_case_id"] = use_case_id
1165
1290
  context["use_case_version"] = use_case_version
@@ -1171,6 +1296,8 @@ def track_context(
1171
1296
 
1172
1297
  return _TrackContext(context)
1173
1298
 
1299
+
1300
+ @deprecated("@ingest() is deprecated. Use @track() instead")
1174
1301
  def ingest(
1175
1302
  limit_ids: Optional["list[str]"] = None,
1176
1303
  experience_name: Optional[str] = None,
@@ -1180,6 +1307,13 @@ def ingest(
1180
1307
  use_case_version: Optional[int] = None,
1181
1308
  user_id: Optional[str] = None,
1182
1309
  ) -> Any:
1310
+ warnings.warn(
1311
+ "@ingest is deprecated and will be removed in a future version. Use @track instead.",
1312
+ DeprecationWarning,
1313
+ stacklevel=2
1314
+
1315
+ )
1316
+
1183
1317
  def _ingest(func: Any) -> Any:
1184
1318
  import asyncio
1185
1319
  if asyncio.iscoroutinefunction(func):
@@ -1219,8 +1353,10 @@ def ingest(
1219
1353
  **kwargs,
1220
1354
  )
1221
1355
  return wrapper
1356
+
1222
1357
  return _ingest
1223
1358
 
1359
+ @deprecated("@proxy() is deprecated. Use @track() instead")
1224
1360
  def proxy(
1225
1361
  limit_ids: Optional["list[str]"] = None,
1226
1362
  experience_name: Optional[str] = None,
@@ -1230,6 +1366,13 @@ def proxy(
1230
1366
  use_case_version: Optional[int] = None,
1231
1367
  user_id: Optional[str] = None,
1232
1368
  ) -> Any:
1369
+ warnings.warn(
1370
+ "@proxy is deprecated and will be removed in a future version. Use @track instead.",
1371
+ DeprecationWarning,
1372
+ stacklevel=2
1373
+
1374
+ )
1375
+
1233
1376
  def _proxy(func: Any) -> Any:
1234
1377
  import asyncio
1235
1378
  if asyncio.iscoroutinefunction(func):
payi/resources/ingest.py CHANGED
@@ -7,6 +7,8 @@ from datetime import datetime
7
7
 
8
8
  import httpx
9
9
 
10
+ from payi._utils._utils import is_given
11
+
10
12
  from ..types import ingest_units_params
11
13
  from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
12
14
  from .._utils import maybe_transform, strip_not_given, async_maybe_transform
@@ -22,6 +24,7 @@ from .._base_client import make_request_options
22
24
  from ..types.ingest_response import IngestResponse
23
25
  from ..types.ingest_event_param import IngestEventParam
24
26
  from ..types.bulk_ingest_response import BulkIngestResponse
27
+ from ..types.shared_params.ingest_units import IngestUnits
25
28
  from ..types.pay_i_common_models_api_router_header_info_param import PayICommonModelsAPIRouterHeaderInfoParam
26
29
 
27
30
  __all__ = ["IngestResource", "AsyncIngestResource"]
@@ -84,7 +87,7 @@ class IngestResource(SyncAPIResource):
84
87
  *,
85
88
  category: str,
86
89
  resource: str,
87
- units: Dict[str, ingest_units_params.Units],
90
+ units: Dict[str, IngestUnits],
88
91
  end_to_end_latency_ms: Optional[int] | NotGiven = NOT_GIVEN,
89
92
  event_timestamp: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
90
93
  experience_properties: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
@@ -104,6 +107,7 @@ class IngestResource(SyncAPIResource):
104
107
  experience_name: Optional[str] | NotGiven = NOT_GIVEN,
105
108
  use_case_id: Optional[str] | NotGiven = NOT_GIVEN,
106
109
  use_case_name: Optional[str] | NotGiven = NOT_GIVEN,
110
+ use_case_step: Optional[str] | NotGiven = NOT_GIVEN,
107
111
  use_case_version: Optional[int] | NotGiven = NOT_GIVEN,
108
112
  user_id: Optional[str] | NotGiven = NOT_GIVEN,
109
113
  resource_scope: Optional[str] | NotGiven = NOT_GIVEN,
@@ -140,6 +144,8 @@ class IngestResource(SyncAPIResource):
140
144
 
141
145
  use_case_id (str, optional): The use case instance id
142
146
 
147
+ use_case_step (str, optional): The use case step
148
+
143
149
  use_case_version (int, optional): The use case instance version
144
150
 
145
151
  user_id (str, optional): The user id
@@ -184,6 +190,9 @@ class IngestResource(SyncAPIResource):
184
190
 
185
191
  if use_case_name is None or isinstance(use_case_name, NotGiven):
186
192
  use_case_name = NOT_GIVEN
193
+
194
+ if use_case_step is None or isinstance(use_case_step, NotGiven):
195
+ use_case_step = NOT_GIVEN
187
196
 
188
197
  if use_case_id is None or isinstance(use_case_id, NotGiven):
189
198
  use_case_id = NOT_GIVEN
@@ -196,18 +205,26 @@ class IngestResource(SyncAPIResource):
196
205
  if user_id is None or isinstance(user_id, NotGiven):
197
206
  user_id = NOT_GIVEN
198
207
 
208
+ if resource_scope is None or isinstance(resource_scope, NotGiven):
209
+ resource_scope = NOT_GIVEN
210
+
199
211
  extra_headers = {
200
- **{key: value for key, value in strip_not_given({
201
- "xProxy-Limit-IDs": valid_ids_str,
202
- "xProxy-Request-Tags": valid_tags_str,
203
- "xProxy-Experience-Name": experience_name,
204
- "xProxy-Experience-ID": experience_id,
205
- "xProxy-UseCase-ID": use_case_id,
206
- "xProxy-UseCase-Name": use_case_name,
207
- "xProxy-UseCase-Version": use_case_version_str,
208
- "xProxy-User-ID": user_id,
209
- "xProxy-Resource-Scope": resource_scope,
210
- }).items() if value is not None}, # Ensure no 'None' values are included
212
+ **strip_not_given(
213
+ {
214
+ "xProxy-Experience-ID": experience_id,
215
+ "xProxy-Experience-Name": experience_name,
216
+ "xProxy-Limit-IDs": valid_ids_str,
217
+ "xProxy-Request-Tags": valid_tags_str,
218
+ "xProxy-UseCase-ID": use_case_id,
219
+ "xProxy-UseCase-Name": use_case_name,
220
+ "xProxy-UseCase-Step": use_case_step,
221
+ "xProxy-UseCase-Version": use_case_version_str
222
+ if is_given(use_case_version)
223
+ else NOT_GIVEN,
224
+ "xProxy-User-ID": user_id,
225
+ "xProxy-Resource-Scope": resource_scope,
226
+ }
227
+ ),
211
228
  **(extra_headers or {}),
212
229
  }
213
230
 
@@ -298,7 +315,7 @@ class AsyncIngestResource(AsyncAPIResource):
298
315
  *,
299
316
  category: str,
300
317
  resource: str,
301
- units: Dict[str, ingest_units_params.Units],
318
+ units: Dict[str, IngestUnits],
302
319
  end_to_end_latency_ms: Optional[int] | NotGiven = NOT_GIVEN,
303
320
  event_timestamp: Union[str, datetime, None] | NotGiven = NOT_GIVEN,
304
321
  experience_properties: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
@@ -318,6 +335,7 @@ class AsyncIngestResource(AsyncAPIResource):
318
335
  experience_id: Optional[str] | NotGiven = NOT_GIVEN,
319
336
  use_case_id: Optional[str] | NotGiven = NOT_GIVEN,
320
337
  use_case_name: Optional[str] | NotGiven = NOT_GIVEN,
338
+ use_case_step: Optional[str] | NotGiven = NOT_GIVEN,
321
339
  use_case_version: Optional[int] | NotGiven = NOT_GIVEN,
322
340
  user_id: Optional[str] | NotGiven = NOT_GIVEN,
323
341
  resource_scope: Union[str, None] | NotGiven = NOT_GIVEN,
@@ -351,6 +369,8 @@ class AsyncIngestResource(AsyncAPIResource):
351
369
 
352
370
  use_case_name (str, optional): The use case name
353
371
 
372
+ use_case_step (str, optional): The use case step
373
+
354
374
  use_case_id (str, optional): The use case instance id
355
375
 
356
376
  use_case_version (int, optional): The use case instance version
@@ -398,6 +418,9 @@ class AsyncIngestResource(AsyncAPIResource):
398
418
  if use_case_name is None or isinstance(use_case_name, NotGiven):
399
419
  use_case_name = NOT_GIVEN
400
420
 
421
+ if use_case_step is None or isinstance(use_case_step, NotGiven):
422
+ use_case_step = NOT_GIVEN
423
+
401
424
  if use_case_id is None or isinstance(use_case_id, NotGiven):
402
425
  use_case_id = NOT_GIVEN
403
426
 
@@ -409,18 +432,26 @@ class AsyncIngestResource(AsyncAPIResource):
409
432
  if user_id is None or isinstance(user_id, NotGiven):
410
433
  user_id = NOT_GIVEN
411
434
 
435
+ if resource_scope is None or isinstance(resource_scope, NotGiven):
436
+ resource_scope = NOT_GIVEN
437
+
412
438
  extra_headers = {
413
- **{key: value for key, value in strip_not_given({
414
- "xProxy-Limit-IDs": valid_ids_str,
415
- "xProxy-Request-Tags": valid_tags_str,
416
- "xProxy-Experience-Name": experience_name,
417
- "xProxy-Experience-ID": experience_id,
418
- "xProxy-UseCase-ID": use_case_id,
419
- "xProxy-UseCase-Name": use_case_name,
420
- "xProxy-UseCase-Version": use_case_version_str,
421
- "xProxy-User-ID": user_id,
422
- "xProxy-Resource-Scope": resource_scope,
423
- }).items() if value is not None}, # Ensure no 'None' values are included
439
+ **strip_not_given(
440
+ {
441
+ "xProxy-Experience-ID": experience_id,
442
+ "xProxy-Experience-Name": experience_name,
443
+ "xProxy-Limit-IDs": valid_ids_str,
444
+ "xProxy-Request-Tags": valid_tags_str,
445
+ "xProxy-UseCase-ID": use_case_id,
446
+ "xProxy-UseCase-Name": use_case_name,
447
+ "xProxy-UseCase-Step": use_case_step,
448
+ "xProxy-UseCase-Version": use_case_version_str
449
+ if is_given(use_case_version)
450
+ else NOT_GIVEN,
451
+ "xProxy-User-ID": user_id,
452
+ "xProxy-Resource-Scope": resource_scope,
453
+ }
454
+ ),
424
455
  **(extra_headers or {}),
425
456
  }
426
457
  return await self._post(
@@ -66,8 +66,6 @@ class LimitsResource(SyncAPIResource):
66
66
  *,
67
67
  limit_name: str,
68
68
  max: float,
69
- billing_model_id: Optional[str] | NotGiven = NOT_GIVEN,
70
- limit_basis: Literal["base", "billed"] | NotGiven = NOT_GIVEN,
71
69
  limit_tags: Optional[List[str]] | NotGiven = NOT_GIVEN,
72
70
  limit_type: Literal["block", "allow"] | NotGiven = NOT_GIVEN,
73
71
  threshold: Optional[float] | NotGiven = NOT_GIVEN,
@@ -96,8 +94,6 @@ class LimitsResource(SyncAPIResource):
96
94
  {
97
95
  "limit_name": limit_name,
98
96
  "max": max,
99
- "billing_model_id": billing_model_id,
100
- "limit_basis": limit_basis,
101
97
  "limit_tags": limit_tags,
102
98
  "limit_type": limit_type,
103
99
  "threshold": threshold,
@@ -335,8 +331,6 @@ class AsyncLimitsResource(AsyncAPIResource):
335
331
  *,
336
332
  limit_name: str,
337
333
  max: float,
338
- billing_model_id: Optional[str] | NotGiven = NOT_GIVEN,
339
- limit_basis: Literal["base", "billed"] | NotGiven = NOT_GIVEN,
340
334
  limit_tags: Optional[List[str]] | NotGiven = NOT_GIVEN,
341
335
  limit_type: Literal["block", "allow"] | NotGiven = NOT_GIVEN,
342
336
  threshold: Optional[float] | NotGiven = NOT_GIVEN,
@@ -365,8 +359,6 @@ class AsyncLimitsResource(AsyncAPIResource):
365
359
  {
366
360
  "limit_name": limit_name,
367
361
  "max": max,
368
- "billing_model_id": billing_model_id,
369
- "limit_basis": limit_basis,
370
362
  "limit_tags": limit_tags,
371
363
  "limit_type": limit_type,
372
364
  "threshold": threshold,
payi/types/__init__.py CHANGED
@@ -3,6 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from .shared import (
6
+ IngestUnits as IngestUnits,
7
+ XproxyError as XproxyError,
8
+ XproxyResult as XproxyResult,
6
9
  PropertiesResponse as PropertiesResponse,
7
10
  PayICommonModelsBudgetManagementCostDetailsBase as PayICommonModelsBudgetManagementCostDetailsBase,
8
11
  PayICommonModelsBudgetManagementCreateLimitBase as PayICommonModelsBudgetManagementCreateLimitBase,