payi 0.1.0a46__py3-none-any.whl → 0.1.0a48__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/_models.py CHANGED
@@ -426,10 +426,16 @@ def construct_type(*, value: object, type_: object) -> object:
426
426
 
427
427
  If the given value does not match the expected type then it is returned as-is.
428
428
  """
429
+
430
+ # store a reference to the original type we were given before we extract any inner
431
+ # types so that we can properly resolve forward references in `TypeAliasType` annotations
432
+ original_type = None
433
+
429
434
  # we allow `object` as the input type because otherwise, passing things like
430
435
  # `Literal['value']` will be reported as a type error by type checkers
431
436
  type_ = cast("type[object]", type_)
432
437
  if is_type_alias_type(type_):
438
+ original_type = type_ # type: ignore[unreachable]
433
439
  type_ = type_.__value__ # type: ignore[unreachable]
434
440
 
435
441
  # unwrap `Annotated[T, ...]` -> `T`
@@ -446,7 +452,7 @@ def construct_type(*, value: object, type_: object) -> object:
446
452
 
447
453
  if is_union(origin):
448
454
  try:
449
- return validate_type(type_=cast("type[object]", type_), value=value)
455
+ return validate_type(type_=cast("type[object]", original_type or type_), value=value)
450
456
  except Exception:
451
457
  pass
452
458
 
payi/_utils/_transform.py CHANGED
@@ -25,7 +25,7 @@ from ._typing import (
25
25
  is_annotated_type,
26
26
  strip_annotated_type,
27
27
  )
28
- from .._compat import model_dump, is_typeddict
28
+ from .._compat import get_origin, model_dump, is_typeddict
29
29
 
30
30
  _T = TypeVar("_T")
31
31
 
@@ -164,9 +164,14 @@ def _transform_recursive(
164
164
  inner_type = annotation
165
165
 
166
166
  stripped_type = strip_annotated_type(inner_type)
167
+ origin = get_origin(stripped_type) or stripped_type
167
168
  if is_typeddict(stripped_type) and is_mapping(data):
168
169
  return _transform_typeddict(data, stripped_type)
169
170
 
171
+ if origin == dict and is_mapping(data):
172
+ items_type = get_args(stripped_type)[1]
173
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
174
+
170
175
  if (
171
176
  # List[T]
172
177
  (is_list_type(stripped_type) and is_list(data))
@@ -307,9 +312,14 @@ async def _async_transform_recursive(
307
312
  inner_type = annotation
308
313
 
309
314
  stripped_type = strip_annotated_type(inner_type)
315
+ origin = get_origin(stripped_type) or stripped_type
310
316
  if is_typeddict(stripped_type) and is_mapping(data):
311
317
  return await _async_transform_typeddict(data, stripped_type)
312
318
 
319
+ if origin == dict and is_mapping(data):
320
+ items_type = get_args(stripped_type)[1]
321
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
322
+
313
323
  if (
314
324
  # List[T]
315
325
  (is_list_type(stripped_type) and is_list(data))
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.46" # x-release-please-version
4
+ __version__ = "0.1.0-alpha.48" # x-release-please-version
payi/lib/helpers.py CHANGED
@@ -1,14 +1,22 @@
1
- # Step 1: Define the new methods outside of the Payi class definition
2
1
  from typing import Dict, List, Union
3
2
 
4
3
 
4
+ class PayiHeaderNames:
5
+ limit_ids:str = "xProxy-Limit-IDs"
6
+ request_tags:str = "xProxy-Request-Tags"
7
+ experience_id:str = "xProxy-Experience-ID"
8
+ experience_name:str = "xProxy-Experience-Name"
9
+ user_id:str = "xProxy-User-ID"
10
+ route_as_resource:str = "xProxy-RouteAs-Resource"
11
+ provider_base_uri = "xProxy-Provider-BaseUri"
12
+
5
13
  def create_limit_header_from_ids(limit_ids: List[str]) -> Dict[str, str]:
6
14
  if not isinstance(limit_ids, list): # type: ignore
7
15
  raise TypeError("limit_ids must be a list")
8
16
 
9
17
  valid_ids = [id.strip() for id in limit_ids if isinstance(id, str) and id.strip()] # type: ignore
10
18
 
11
- return {"xProxy-Limit-IDs": ",".join(valid_ids)} if valid_ids else {}
19
+ return { PayiHeaderNames.limit_ids: ",".join(valid_ids) } if valid_ids else {}
12
20
 
13
21
 
14
22
  def create_request_header_from_tags(request_tags: List[str]) -> Dict[str, str]:
@@ -17,7 +25,7 @@ def create_request_header_from_tags(request_tags: List[str]) -> Dict[str, str]:
17
25
 
18
26
  valid_tags = [tag.strip() for tag in request_tags if isinstance(tag, str) and tag.strip()] # type: ignore
19
27
 
20
- return {"xProxy-Request-Tags": ",".join(valid_tags)} if valid_tags else {}
28
+ return { PayiHeaderNames.request_tags: ",".join(valid_tags) } if valid_tags else {}
21
29
 
22
30
 
23
31
  def create_headers(
@@ -34,10 +42,10 @@ def create_headers(
34
42
  if request_tags:
35
43
  headers.update(create_request_header_from_tags(request_tags))
36
44
  if user_id:
37
- headers.update({"xProxy-User-ID": user_id})
45
+ headers.update({ PayiHeaderNames.user_id: user_id})
38
46
  if experience_id:
39
- headers.update({"xProxy-Experience-Id": experience_id})
47
+ headers.update({ PayiHeaderNames.experience_id: experience_id})
40
48
  if experience_name:
41
- headers.update({"xProxy-Experience-Name": experience_name})
49
+ headers.update({ PayiHeaderNames.experience_name: experience_name})
42
50
 
43
51
  return headers
payi/lib/instrument.py CHANGED
@@ -5,13 +5,14 @@ import inspect
5
5
  import logging
6
6
  import traceback
7
7
  from enum import Enum
8
- from typing import Any, Set, Union, Callable, Optional
8
+ from typing import Any, Set, Union, Callable, Optional, TypedDict
9
9
 
10
10
  import nest_asyncio # type: ignore
11
11
  from wrapt import ObjectProxy # type: ignore
12
12
 
13
13
  from payi import Payi, AsyncPayi
14
14
  from payi.types import IngestUnitsParams
15
+ from payi.lib.helpers import PayiHeaderNames
15
16
  from payi.types.ingest_response import IngestResponse
16
17
  from payi.types.ingest_units_params import Units
17
18
  from payi.types.pay_i_common_models_api_router_header_info_param import PayICommonModelsAPIRouterHeaderInfoParam
@@ -20,6 +21,14 @@ from .Stopwatch import Stopwatch
20
21
  from .Instruments import Instruments
21
22
 
22
23
 
24
+ class Context(TypedDict, total=False):
25
+ proxy: bool
26
+ experience_name: Optional[str]
27
+ experience_id: Optional[str]
28
+ limit_ids: Optional['list[str]']
29
+ request_tags: Optional['list[str]']
30
+ user_id: Optional[str]
31
+
23
32
  class IsStreaming(Enum):
24
33
  false = 0
25
34
  true = 1
@@ -40,7 +49,7 @@ class PayiInstrumentor:
40
49
  ):
41
50
  self._payi: Optional[Payi] = payi
42
51
  self._apayi: Optional[AsyncPayi] = apayi
43
- self._context_stack: list[dict[str, Any]] = [] # Stack of context dictionaries
52
+ self._context_stack: list[Context] = [] # Stack of context dictionaries
44
53
  self._log_prompt_and_response: bool = log_prompt_and_response
45
54
  self._prompt_and_response_logger: Optional[Callable[[str, dict[str, str]], None]] = prompt_and_response_logger
46
55
 
@@ -212,22 +221,23 @@ class PayiInstrumentor:
212
221
 
213
222
  def _setup_call_func(
214
223
  self
215
- ) -> 'tuple[dict[str, Any], Optional[str], Optional[str]]':
224
+ ) -> 'tuple[Context, Optional[str], Optional[str]]':
225
+ context: Context = {}
226
+
216
227
  if len(self._context_stack) > 0:
217
228
  # copy current context into the upcoming context
218
229
  context = self._context_stack[-1].copy()
219
- context.pop("proxy", None)
220
- previous_experience_name = context["experience_name"]
221
- previous_experience_id = context["experience_id"]
230
+ context.pop("proxy")
231
+ previous_experience_name = context.get("experience_name", None)
232
+ previous_experience_id = context.get("experience_id", None)
222
233
  else:
223
- context = {}
224
234
  previous_experience_name = None
225
235
  previous_experience_id = None
226
236
  return (context, previous_experience_name, previous_experience_id)
227
237
 
228
238
  def _init_context(
229
239
  self,
230
- context: "dict[str, Any]",
240
+ context: Context,
231
241
  previous_experience_name: Optional[str],
232
242
  previous_experience_id: Optional[str],
233
243
  proxy: bool,
@@ -329,12 +339,12 @@ class PayiInstrumentor:
329
339
  if self._context_stack:
330
340
  self._context_stack.pop()
331
341
 
332
- def set_context(self, context: "dict[str, Any]") -> None:
342
+ def set_context(self, context: Context) -> None:
333
343
  # Update the current top of the stack with the provided context
334
344
  if self._context_stack:
335
345
  self._context_stack[-1].update(context)
336
346
 
337
- def get_context(self) -> Optional["dict[str, Any]"]:
347
+ def get_context(self) -> Optional[Context]:
338
348
  # Return the current top of the stack
339
349
  return self._context_stack[-1] if self._context_stack else None
340
350
 
@@ -345,11 +355,11 @@ class PayiInstrumentor:
345
355
  ingest_extra_headers: "dict[str, str]", # do not coflict potential kwargs["extra_headers"]
346
356
  **kwargs: Any,
347
357
  ) -> None:
348
- limit_ids = ingest_extra_headers.pop("xProxy-Limit-IDs", None)
349
- request_tags = ingest_extra_headers.pop("xProxy-Request-Tags", None)
350
- experience_name = ingest_extra_headers.pop("xProxy-Experience-Name", None)
351
- experience_id = ingest_extra_headers.pop("xProxy-Experience-ID", None)
352
- user_id = ingest_extra_headers.pop("xProxy-User-ID", None)
358
+ limit_ids = ingest_extra_headers.pop(PayiHeaderNames.limit_ids, None)
359
+ request_tags = ingest_extra_headers.pop(PayiHeaderNames.request_tags, None)
360
+ experience_name = ingest_extra_headers.pop(PayiHeaderNames.experience_name, None)
361
+ experience_id = ingest_extra_headers.pop(PayiHeaderNames.experience_id, None)
362
+ user_id = ingest_extra_headers.pop(PayiHeaderNames.user_id, None)
353
363
 
354
364
  if limit_ids:
355
365
  ingest["limit_ids"] = limit_ids.split(",")
@@ -414,7 +424,7 @@ class PayiInstrumentor:
414
424
  from .OpenAIInstrumentor import OpenAiInstrumentor # noqa: I001
415
425
 
416
426
  if OpenAiInstrumentor.is_azure(instance):
417
- resource = extra_headers.pop("xProxy-RouteAs-Resource", None)
427
+ resource = extra_headers.pop(PayiHeaderNames.route_as_resource, None)
418
428
  if not resource:
419
429
  logging.error("Azure OpenAI route as resource not found, not ingesting")
420
430
  return await wrapped(*args, **kwargs)
@@ -532,7 +542,7 @@ class PayiInstrumentor:
532
542
  if category == "system.openai" and instance and hasattr(instance, "_client"):
533
543
  from .OpenAIInstrumentor import OpenAiInstrumentor
534
544
  if OpenAiInstrumentor.is_azure(instance):
535
- resource = extra_headers.pop("xProxy-RouteAs-Resource", None)
545
+ resource = extra_headers.pop(PayiHeaderNames.route_as_resource, None)
536
546
  if not resource:
537
547
  logging.error("Azure OpenAI route as resource not found, not ingesting")
538
548
  return wrapped(*args, **kwargs)
@@ -614,7 +624,7 @@ class PayiInstrumentor:
614
624
 
615
625
  @staticmethod
616
626
  def _update_headers(
617
- context: "dict[str, Any]",
627
+ context: Context,
618
628
  extra_headers: "dict[str, str]",
619
629
  ) -> None:
620
630
  limit_ids: Optional[list[str]] = context.get("limit_ids")
@@ -625,38 +635,48 @@ class PayiInstrumentor:
625
635
 
626
636
  # Merge limits from the decorator and extra headers
627
637
  if limit_ids is not None:
628
- existing_limit_ids = extra_headers.get("xProxy-Limit-IDs", None)
638
+ existing_limit_ids = extra_headers.get(PayiHeaderNames.limit_ids, None)
629
639
 
630
640
  if not existing_limit_ids:
631
- extra_headers["xProxy-Limit-IDs"] = ",".join(limit_ids)
641
+ extra_headers[PayiHeaderNames.limit_ids] = ",".join(limit_ids)
632
642
  else:
633
643
  existing_ids = existing_limit_ids.split(',')
634
644
  combined_ids = list(set(existing_ids + limit_ids))
635
- extra_headers["xProxy-Limit-IDs"] = ",".join(combined_ids)
645
+ extra_headers[PayiHeaderNames.limit_ids] = ",".join(combined_ids)
636
646
 
637
647
  # Merge request from the decorator and extra headers
638
648
  if request_tags is not None:
639
- existing_request_tags = extra_headers.get("xProxy-Request-Tags", None)
649
+ existing_request_tags = extra_headers.get(PayiHeaderNames.request_tags, None)
640
650
 
641
651
  if not existing_request_tags:
642
- extra_headers["xProxy-Request-Tags"] = ",".join(request_tags)
652
+ extra_headers[PayiHeaderNames.request_tags] = ",".join(request_tags)
643
653
  else:
644
654
  existing_tags = existing_request_tags.split(',')
645
655
  combined_tags = list(set(existing_tags + request_tags))
646
- extra_headers["xProxy-Request-Tags"] = ",".join(combined_tags)
656
+ extra_headers[PayiHeaderNames.request_tags] = ",".join(combined_tags)
647
657
 
648
658
  # inner extra_headers user_id takes precedence over outer decorator user_id
649
- if user_id is not None and extra_headers.get("xProxy-User-ID", None) is None:
650
- extra_headers["xProxy-User-ID"] = user_id
659
+ if user_id is not None and extra_headers.get(PayiHeaderNames.user_id, None) is None:
660
+ extra_headers[PayiHeaderNames.user_id] = user_id
651
661
 
652
662
  # inner extra_headers experience_name and experience_id take precedence over outer decorator experience_name and experience_id
653
663
  # if either inner value is specified, ignore outer decorator values
654
- if extra_headers.get("xProxy-Experience-Name", None) is None and extra_headers.get("xProxy-Experience-ID", None) is None:
655
- if experience_name is not None:
656
- extra_headers["xProxy-Experience-Name"] = experience_name
664
+ if PayiHeaderNames.experience_name not in extra_headers and PayiHeaderNames.experience_id not in extra_headers:
657
665
 
666
+ # use both decorator values
667
+ if experience_name is not None:
668
+ extra_headers[PayiHeaderNames.experience_name] = experience_name
658
669
  if experience_id is not None:
659
- extra_headers["xProxy-Experience-ID"] = experience_id
670
+ extra_headers[PayiHeaderNames.experience_id] = experience_id
671
+
672
+ elif PayiHeaderNames.experience_id in extra_headers and PayiHeaderNames.experience_name not in extra_headers:
673
+ # use the decorator experience name and the inner experience id
674
+ if experience_name is not None:
675
+ extra_headers[PayiHeaderNames.experience_name] = experience_name
676
+
677
+ else:
678
+ # use the inner experience name and id as-is
679
+ ...
660
680
 
661
681
  @staticmethod
662
682
  def update_for_vision(input: int, units: 'dict[str, Units]') -> int:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: payi
3
- Version: 0.1.0a46
3
+ Version: 0.1.0a48
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
@@ -5,13 +5,13 @@ payi/_compat.py,sha256=VWemUKbj6DDkQ-O4baSpHVLJafotzeXmCQGJugfVTIw,6580
5
5
  payi/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
6
6
  payi/_exceptions.py,sha256=ItygKNrNXIVY0H6LsGVZvFuAHB3Vtm_VZXmWzCnpHy0,3216
7
7
  payi/_files.py,sha256=mf4dOgL4b0ryyZlbqLhggD3GVgDf6XxdGFAgce01ugE,3549
8
- payi/_models.py,sha256=uZvPAaaeDwCYwvB-yq7nxZnZ70I2Na_KjSAqaPQWfh0,28659
8
+ payi/_models.py,sha256=PDLSNsn3Umxm3UMZPgyBiyN308rRzzPX6F9NO9FU2vs,28943
9
9
  payi/_qs.py,sha256=AOkSz4rHtK4YI3ZU_kzea-zpwBUgEY8WniGmTPyEimc,4846
10
10
  payi/_resource.py,sha256=j2jIkTr8OIC8sU6-05nxSaCyj4MaFlbZrwlyg4_xJos,1088
11
11
  payi/_response.py,sha256=CfrNS_3wbL8o9dRyRVfZQ5E1GUlA4CUIUEK8olmfGqE,28777
12
12
  payi/_streaming.py,sha256=Z_wIyo206T6Jqh2rolFg2VXZgX24PahLmpURp0-NssU,10092
13
13
  payi/_types.py,sha256=2mbMK86K3W1aMTW7sOGQ-VND6-A2IuXKm8p4sYFztBU,6141
14
- payi/_version.py,sha256=ml75PCr3anMJW3fkTojqOUY2T3iVxRGV0E2gWIEhotQ,165
14
+ payi/_version.py,sha256=GtYbN-yExify_mgm02kxxAg7YrHlP8HnvMwXvPXKAn4,165
15
15
  payi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  payi/_utils/__init__.py,sha256=PNZ_QJuzZEgyYXqkO1HVhGkj5IU9bglVUcw7H-Knjzw,2062
17
17
  payi/_utils/_logs.py,sha256=fmnf5D9TOgkgZKfgYmSa3PiUc3SZgkchn6CzJUeo0SQ,768
@@ -19,7 +19,7 @@ payi/_utils/_proxy.py,sha256=z3zsateHtb0EARTWKk8QZNHfPkqJbqwd1lM993LBwGE,1902
19
19
  payi/_utils/_reflection.py,sha256=ZmGkIgT_PuwedyNBrrKGbxoWtkpytJNU1uU4QHnmEMU,1364
20
20
  payi/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289
21
21
  payi/_utils/_sync.py,sha256=jJl-iCEaZZUAkq4IUtzN1-aMsKTUFaNoNbeYnnpQjIQ,2438
22
- payi/_utils/_transform.py,sha256=Dkkyr7OveGmOolepcvXmVJWE3kqim4b0nM0h7yWbgeY,13468
22
+ payi/_utils/_transform.py,sha256=tsSFOIZ7iczaUsMSGBD_iSFOOdUyT2xtkcq1xyF0L9o,13986
23
23
  payi/_utils/_typing.py,sha256=nTJz0jcrQbEgxwy4TtAkNxuU0QHHlmc6mQtA6vIR8tg,4501
24
24
  payi/_utils/_utils.py,sha256=8UmbPOy_AAr2uUjjFui-VZSrVBHRj6bfNEKRp5YZP2A,12004
25
25
  payi/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
@@ -28,8 +28,8 @@ payi/lib/BedrockInstrumentor.py,sha256=B5deT8TcUvOsRdIewbbHtsxTS7_oHJ05ZUIdFziWf
28
28
  payi/lib/Instruments.py,sha256=bapmVS9jbHtFknXCKDzsFFWvf5XLtzEpdlvI7iEWY-o,147
29
29
  payi/lib/OpenAIInstrumentor.py,sha256=rxu5wOUBZaGwFjUoUhk5UDeTcYSmmO6BVgnXL4yYb08,6613
30
30
  payi/lib/Stopwatch.py,sha256=7OJlxvr2Jyb6Zr1LYCYKczRB7rDVKkIR7gc4YoleNdE,764
31
- payi/lib/helpers.py,sha256=ZgkY8UE2YRc7ok2Pmxg_T9UMqKI8D8542JY3CP8RZCM,1597
32
- payi/lib/instrument.py,sha256=rQCBCyFicTtOjuASwTtCEGvDoffUUgYQoYcuiSlVUlk,35356
31
+ payi/lib/helpers.py,sha256=RRJ0esC8T-b6n9Bc_WbUHMlucdv_ARhiV3oiAAUg-KU,1915
32
+ payi/lib/instrument.py,sha256=HgKyy7nAWf0X66VuBJtc87J5eokGjnGcm-Hq0IOmgzA,36185
33
33
  payi/resources/__init__.py,sha256=isHGXSl9kOrZDduKrX3UenTwrdTpuKJVBjw6NYSBV20,3592
34
34
  payi/resources/billing_models.py,sha256=5w3RfGXtGlyq5vbTw6hQrx1UlzRBtlq8ArcFlf5e3TY,20152
35
35
  payi/resources/ingest.py,sha256=xdVD0fHGkVpsgqxNTkly_X7UdLyWLNb9_uU8tzWB5Jc,18430
@@ -113,7 +113,7 @@ payi/types/requests/request_result.py,sha256=phYQiqhwNaR9igP-Fhs34Y-__dlT7L4wq-r
113
113
  payi/types/shared/__init__.py,sha256=-xz5dxK5LBjLnsi2LpLq5btaGDFp-mSjJ0y2qKy0Yus,264
114
114
  payi/types/shared/evaluation_response.py,sha256=ejEToMA57PUu1SldEtJ5z9r4fAO3U0tvdjbsyIoVX1s,214
115
115
  payi/types/shared/pay_i_common_models_budget_management_cost_details_base.py,sha256=XmIzJXy4zAi-mfrDvEXiYjO3qF1EvugGUl-Gijj4TA4,268
116
- payi-0.1.0a46.dist-info/METADATA,sha256=4jSFupUbO3KTCTYi_UCy_-lvzGZ3hGi57g-J57Wi_1E,12660
117
- payi-0.1.0a46.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
- payi-0.1.0a46.dist-info/licenses/LICENSE,sha256=CQt03aM-P4a3Yg5qBg3JSLVoQS3smMyvx7tYg_6V7Gk,11334
119
- payi-0.1.0a46.dist-info/RECORD,,
116
+ payi-0.1.0a48.dist-info/METADATA,sha256=gjbFkoar2_1p61b6hr0bDBVmtEHaaNKdw5LGrqLwLiE,12660
117
+ payi-0.1.0a48.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
+ payi-0.1.0a48.dist-info/licenses/LICENSE,sha256=CQt03aM-P4a3Yg5qBg3JSLVoQS3smMyvx7tYg_6V7Gk,11334
119
+ payi-0.1.0a48.dist-info/RECORD,,