payi 0.1.0a68__py3-none-any.whl → 0.1.0a70__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.

@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Any, Union
2
+ from typing import Any, Union, Optional
3
3
  from typing_extensions import override
4
4
 
5
5
  import tiktoken
@@ -54,7 +54,6 @@ def chat_wrapper(
54
54
  **kwargs: Any,
55
55
  ) -> Any:
56
56
  return instrumentor.chat_wrapper(
57
- "system.anthropic",
58
57
  _AnthropicProviderRequest(instrumentor),
59
58
  _IsStreaming.kwargs,
60
59
  wrapped,
@@ -72,7 +71,6 @@ async def achat_wrapper(
72
71
  **kwargs: Any,
73
72
  ) -> Any:
74
73
  return await instrumentor.achat_wrapper(
75
- "system.anthropic",
76
74
  _AnthropicProviderRequest(instrumentor),
77
75
  _IsStreaming.kwargs,
78
76
  wrapped,
@@ -82,6 +80,9 @@ async def achat_wrapper(
82
80
  )
83
81
 
84
82
  class _AnthropicProviderRequest(_ProviderRequest):
83
+ def __init__(self, instrumentor: _PayiInstrumentor):
84
+ super().__init__(instrumentor=instrumentor, category="system.anthropic")
85
+
85
86
  @override
86
87
  def process_chunk(self, chunk: Any) -> bool:
87
88
  if chunk.type == "message_start":
@@ -135,25 +136,56 @@ class _AnthropicProviderRequest(_ProviderRequest):
135
136
  return None
136
137
 
137
138
  @override
138
- def process_request(self, kwargs: Any) -> None:
139
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
140
+ self._ingest["resource"] = kwargs.get("model", "")
139
141
  messages = kwargs.get("messages")
140
- if not messages or len(messages) == 0:
141
- return
142
-
143
- estimated_token_count = 0
144
- has_image = False
142
+ if messages:
143
+ estimated_token_count = 0
144
+ has_image = False
145
+
146
+ enc = tiktoken.get_encoding("cl100k_base")
147
+
148
+ for message in messages:
149
+ msg_has_image, msg_prompt_tokens = has_image_and_get_texts(enc, message.get('content', ''))
150
+ if msg_has_image:
151
+ has_image = True
152
+ estimated_token_count += msg_prompt_tokens
153
+
154
+ if has_image and estimated_token_count > 0:
155
+ self._estimated_prompt_tokens = estimated_token_count
156
+ return True
157
+
158
+ @override
159
+ def process_exception(self, exception: Exception, kwargs: Any, ) -> bool:
160
+ try:
161
+ status_code: Optional[int] = None
162
+
163
+ if hasattr(exception, "status_code"):
164
+ status_code = getattr(exception, "status_code", None)
165
+ if isinstance(status_code, int):
166
+ self._ingest["http_status_code"] = status_code
167
+
168
+ if not status_code:
169
+ return False
170
+
171
+ if hasattr(exception, "request_id"):
172
+ request_id = getattr(exception, "request_id", None)
173
+ if isinstance(request_id, str):
174
+ self._ingest["provider_response_id"] = request_id
175
+
176
+ if hasattr(exception, "response"):
177
+ response = getattr(exception, "response", None)
178
+ if hasattr(response, "text"):
179
+ text = getattr(response, "text", None)
180
+ if isinstance(text, str):
181
+ self._ingest["provider_response_json"] = text
182
+
183
+ except Exception as e:
184
+ logging.debug(f"Error processing exception: {e}")
185
+ return False
186
+
187
+ return True
145
188
 
146
- enc = tiktoken.get_encoding("cl100k_base")
147
-
148
- for message in messages:
149
- msg_has_image, msg_prompt_tokens = has_image_and_get_texts(enc, message.get('content', ''))
150
- if msg_has_image:
151
- has_image = True
152
- estimated_token_count += msg_prompt_tokens
153
-
154
- if not has_image or estimated_token_count == 0:
155
- return
156
- self._estimated_prompt_tokens = estimated_token_count
157
189
 
158
190
  def has_image_and_get_texts(encoding: tiktoken.Encoding, content: Union[str, 'list[Any]']) -> 'tuple[bool, int]':
159
191
  if isinstance(content, str):
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import json
2
3
  import logging
3
4
  from typing import Any
@@ -6,15 +7,21 @@ from typing_extensions import override
6
7
 
7
8
  from wrapt import ObjectProxy, wrap_function_wrapper # type: ignore
8
9
 
10
+ from payi.lib.helpers import PayiHeaderNames, payi_aws_bedrock_url
9
11
  from payi.types.ingest_units_params import Units, IngestUnitsParams
10
12
  from payi.types.pay_i_common_models_api_router_header_info_param import PayICommonModelsAPIRouterHeaderInfoParam
11
13
 
12
14
  from .instrument import _IsStreaming, _ProviderRequest, _PayiInstrumentor
13
15
 
16
+ _supported_model_prefixes = ["meta.llama3", "anthropic.", "amazon.nova-pro", "amazon.nova-lite", "amazon.nova-micro"]
14
17
 
15
18
  class BedrockInstrumentor:
19
+ _instrumentor: _PayiInstrumentor
20
+
16
21
  @staticmethod
17
22
  def instrument(instrumentor: _PayiInstrumentor) -> None:
23
+ BedrockInstrumentor._instrumentor = instrumentor
24
+
18
25
  try:
19
26
  import boto3 # type: ignore # noqa: F401 I001
20
27
 
@@ -46,12 +53,48 @@ def create_client_wrapper(instrumentor: _PayiInstrumentor, wrapped: Any, instanc
46
53
  client.converse = wrap_converse(instrumentor, client.converse)
47
54
  client.converse_stream = wrap_converse_stream(instrumentor, client.converse_stream)
48
55
 
56
+ if BedrockInstrumentor._instrumentor._proxy_default:
57
+ # Register client callbacks to handle the Pay-i extra_headers parameter in the inference calls and redirect the request to the Pay-i endpoint
58
+ _register_bedrock_client_callbacks(client)
59
+
49
60
  return client
50
61
  except Exception as e:
51
62
  logging.debug(f"Error instrumenting bedrock client: {e}")
52
63
 
53
64
  return wrapped(*args, **kwargs)
65
+
66
+ BEDROCK_REQUEST_NAMES = [
67
+ 'request-created.bedrock-runtime.Converse',
68
+ 'request-created.bedrock-runtime.ConverseStream',
69
+ 'request-created.bedrock-runtime.InvokeModel',
70
+ 'request-created.bedrock-runtime.InvokeModelWithResponseStream',
71
+ ]
72
+
73
+ def _register_bedrock_client_callbacks(client: Any) -> None:
74
+ # Pass a unqiue_id to avoid registering the same callback multiple times in case this cell executed more than once
75
+ # Redirect the request to the Pay-i endpoint after the request has been signed.
76
+ client.meta.events.register_last('request-created', _redirect_to_payi, unique_id=_redirect_to_payi)
77
+
78
+ def _redirect_to_payi(request: Any, event_name: str, **_: 'dict[str, Any]') -> None:
79
+ from urllib3.util import parse_url
80
+ from urllib3.util.url import Url
81
+
82
+ if not event_name in BEDROCK_REQUEST_NAMES:
83
+ return
54
84
 
85
+ parsed_url: Url = parse_url(request.url)
86
+ route_path = parsed_url.path
87
+ request.url = f"{payi_aws_bedrock_url()}{route_path}"
88
+
89
+ request.headers[PayiHeaderNames.api_key] = os.environ.get("PAYI_API_KEY", "")
90
+ request.headers[PayiHeaderNames.provider_base_uri] = parsed_url.scheme + "://" + parsed_url.host # type: ignore
91
+
92
+ extra_headers = BedrockInstrumentor._instrumentor._create_extra_headers()
93
+
94
+ for key, value in extra_headers.items():
95
+ request.headers[key] = value
96
+
97
+
55
98
  class InvokeResponseWrapper(ObjectProxy): # type: ignore
56
99
  def __init__(
57
100
  self,
@@ -67,10 +110,10 @@ class InvokeResponseWrapper(ObjectProxy): # type: ignore
67
110
  self._ingest = ingest
68
111
  self._log_prompt_and_response = log_prompt_and_response
69
112
 
70
- def read(self, amt: Any =None): # type: ignore
113
+ def read(self, amt: Any =None) -> Any: # type: ignore
71
114
  # data is array of bytes
72
- data: Any = self.__wrapped__.read(amt) # type: ignore
73
- response = json.loads(data)
115
+ data: bytes = self.__wrapped__.read(amt) # type: ignore
116
+ response = json.loads(data) # type: ignore
74
117
 
75
118
  resource = self._ingest["resource"]
76
119
  if not resource:
@@ -90,27 +133,29 @@ class InvokeResponseWrapper(ObjectProxy): # type: ignore
90
133
  units["text"] = Units(input=input, output=output)
91
134
 
92
135
  if self._log_prompt_and_response:
93
- self._ingest["provider_response_json"] = data.decode('utf-8')
136
+ self._ingest["provider_response_json"] = data.decode('utf-8') # type: ignore
94
137
 
95
138
  self._instrumentor._ingest_units(self._ingest)
96
139
 
97
- return data
140
+ return data # type: ignore
141
+
142
+ def _is_supported_model(modelId: str) -> bool:
143
+ return any(prefix in modelId for prefix in _supported_model_prefixes)
98
144
 
99
145
  def wrap_invoke(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
100
146
  @wraps(wrapped)
101
147
  def invoke_wrapper(*args: Any, **kwargs: 'dict[str, Any]') -> Any:
102
148
  modelId:str = kwargs.get("modelId", "") # type: ignore
103
149
 
104
- if modelId.startswith("meta.llama3") or modelId.startswith("anthropic."):
150
+ if _is_supported_model(modelId):
105
151
  return instrumentor.chat_wrapper(
106
- "system.aws.bedrock",
107
- _BedrockInvokeSynchronousProviderRequest(instrumentor),
152
+ _BedrockInvokeSynchronousProviderRequest(instrumentor=instrumentor),
108
153
  _IsStreaming.false,
109
154
  wrapped,
110
155
  None,
111
156
  args,
112
157
  kwargs,
113
- )
158
+ )
114
159
  return wrapped(*args, **kwargs)
115
160
 
116
161
  return invoke_wrapper
@@ -118,12 +163,11 @@ def wrap_invoke(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
118
163
  def wrap_invoke_stream(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
119
164
  @wraps(wrapped)
120
165
  def invoke_wrapper(*args: Any, **kwargs: Any) -> Any:
121
- model_id: str = kwargs.get("modelId", "") # type: ignore
166
+ modelId: str = kwargs.get("modelId", "") # type: ignore
122
167
 
123
- if model_id.startswith("meta.llama3") or model_id.startswith("anthropic."):
168
+ if _is_supported_model(modelId):
124
169
  return instrumentor.chat_wrapper(
125
- "system.aws.bedrock",
126
- _BedrockInvokeStreamingProviderRequest(instrumentor, model_id),
170
+ _BedrockInvokeStreamingProviderRequest(instrumentor=instrumentor, model_id=modelId),
127
171
  _IsStreaming.true,
128
172
  wrapped,
129
173
  None,
@@ -139,10 +183,9 @@ def wrap_converse(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
139
183
  def invoke_wrapper(*args: Any, **kwargs: 'dict[str, Any]') -> Any:
140
184
  modelId:str = kwargs.get("modelId", "") # type: ignore
141
185
 
142
- if modelId.startswith("meta.llama3") or modelId.startswith("anthropic."):
186
+ if _is_supported_model(modelId):
143
187
  return instrumentor.chat_wrapper(
144
- "system.aws.bedrock",
145
- _BedrockConverseSynchronousProviderRequest(instrumentor),
188
+ _BedrockConverseSynchronousProviderRequest(instrumentor=instrumentor),
146
189
  _IsStreaming.false,
147
190
  wrapped,
148
191
  None,
@@ -156,12 +199,11 @@ def wrap_converse(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
156
199
  def wrap_converse_stream(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
157
200
  @wraps(wrapped)
158
201
  def invoke_wrapper(*args: Any, **kwargs: Any) -> Any:
159
- model_id: str = kwargs.get("modelId", "") # type: ignore
202
+ modelId: str = kwargs.get("modelId", "") # type: ignore
160
203
 
161
- if model_id.startswith("meta.llama3") or model_id.startswith("anthropic."):
204
+ if _is_supported_model(modelId):
162
205
  return instrumentor.chat_wrapper(
163
- "system.aws.bedrock",
164
- _BedrockConverseStreamingProviderRequest(instrumentor),
206
+ _BedrockConverseStreamingProviderRequest(instrumentor=instrumentor),
165
207
  _IsStreaming.true,
166
208
  wrapped,
167
209
  None,
@@ -172,9 +214,45 @@ def wrap_converse_stream(instrumentor: _PayiInstrumentor, wrapped: Any) -> Any:
172
214
 
173
215
  return invoke_wrapper
174
216
 
175
- class _BedrockInvokeStreamingProviderRequest(_ProviderRequest):
217
+ class _BedrockProviderRequest(_ProviderRequest):
218
+ def __init__(self, instrumentor: _PayiInstrumentor):
219
+ super().__init__(instrumentor=instrumentor, category="system.aws.bedrock")
220
+
221
+ @override
222
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
223
+ # boto3 doesn't allow extra_headers
224
+ kwargs.pop("extra_headers", None)
225
+ self._ingest["resource"] = kwargs.get("modelId", "")
226
+ return True
227
+
228
+ @override
229
+ def process_exception(self, exception: Exception, kwargs: Any, ) -> bool:
230
+ try:
231
+ if hasattr(exception, "response"):
232
+ response: dict[str, Any] = getattr(exception, "response", {})
233
+ status_code: int = response.get('ResponseMetadata', {}).get('HTTPStatusCode', 0)
234
+ if status_code == 0:
235
+ return False
236
+
237
+ self._ingest["http_status_code"] = status_code
238
+
239
+ request_id = response.get('ResponseMetadata', {}).get('RequestId', "")
240
+ if request_id:
241
+ self._ingest["provider_response_id"] = request_id
242
+
243
+ error = response.get('Error', "")
244
+ if error:
245
+ self._ingest["provider_response_json"] = json.dumps(error)
246
+
247
+ return True
248
+
249
+ except Exception as e:
250
+ logging.debug(f"Error processing exception: {e}")
251
+ return False
252
+
253
+ class _BedrockInvokeStreamingProviderRequest(_BedrockProviderRequest):
176
254
  def __init__(self, instrumentor: _PayiInstrumentor, model_id: str):
177
- super().__init__(instrumentor)
255
+ super().__init__(instrumentor=instrumentor)
178
256
  self._is_anthropic: bool = model_id.startswith("anthropic.")
179
257
 
180
258
  @override
@@ -220,7 +298,7 @@ class _BedrockInvokeStreamingProviderRequest(_ProviderRequest):
220
298
 
221
299
  return True
222
300
 
223
- class _BedrockInvokeSynchronousProviderRequest(_ProviderRequest):
301
+ class _BedrockInvokeSynchronousProviderRequest(_BedrockProviderRequest):
224
302
  @override
225
303
  def process_synchronous_response(
226
304
  self,
@@ -246,7 +324,7 @@ class _BedrockInvokeSynchronousProviderRequest(_ProviderRequest):
246
324
 
247
325
  return response
248
326
 
249
- class _BedrockConverseSynchronousProviderRequest(_ProviderRequest):
327
+ class _BedrockConverseSynchronousProviderRequest(_BedrockProviderRequest):
250
328
  @override
251
329
  def process_synchronous_response(
252
330
  self,
@@ -278,7 +356,7 @@ class _BedrockConverseSynchronousProviderRequest(_ProviderRequest):
278
356
 
279
357
  return None
280
358
 
281
- class _BedrockConverseStreamingProviderRequest(_ProviderRequest):
359
+ class _BedrockConverseStreamingProviderRequest(_BedrockProviderRequest):
282
360
  @override
283
361
  def process_chunk(self, chunk: 'dict[str, Any]') -> bool:
284
362
  metadata = chunk.get("metadata", {})
@@ -8,6 +8,7 @@ import tiktoken # type: ignore
8
8
  from wrapt import wrap_function_wrapper # type: ignore
9
9
 
10
10
  from payi.types import IngestUnitsParams
11
+ from payi.lib.helpers import PayiCategories, PayiHeaderNames
11
12
  from payi.types.ingest_units_params import Units
12
13
 
13
14
  from .instrument import _IsStreaming, _ProviderRequest, _PayiInstrumentor
@@ -63,7 +64,6 @@ def embeddings_wrapper(
63
64
  **kwargs: Any,
64
65
  ) -> Any:
65
66
  return instrumentor.chat_wrapper(
66
- "system.openai",
67
67
  _OpenAiEmbeddingsProviderRequest(instrumentor),
68
68
  _IsStreaming.false,
69
69
  wrapped,
@@ -81,7 +81,6 @@ async def aembeddings_wrapper(
81
81
  **kwargs: Any,
82
82
  ) -> Any:
83
83
  return await instrumentor.achat_wrapper(
84
- "system.openai",
85
84
  _OpenAiEmbeddingsProviderRequest(instrumentor),
86
85
  _IsStreaming.false,
87
86
  wrapped,
@@ -99,7 +98,6 @@ def chat_wrapper(
99
98
  **kwargs: Any,
100
99
  ) -> Any:
101
100
  return instrumentor.chat_wrapper(
102
- "system.openai",
103
101
  _OpenAiChatProviderRequest(instrumentor),
104
102
  _IsStreaming.kwargs,
105
103
  wrapped,
@@ -117,7 +115,6 @@ async def achat_wrapper(
117
115
  **kwargs: Any,
118
116
  ) -> Any:
119
117
  return await instrumentor.achat_wrapper(
120
- "system.openai",
121
118
  _OpenAiChatProviderRequest(instrumentor),
122
119
  _IsStreaming.kwargs,
123
120
  wrapped,
@@ -126,7 +123,79 @@ async def achat_wrapper(
126
123
  kwargs,
127
124
  )
128
125
 
129
- class _OpenAiEmbeddingsProviderRequest(_ProviderRequest):
126
+ class _OpenAiProviderRequest(_ProviderRequest):
127
+ def __init__(self, instrumentor: _PayiInstrumentor):
128
+ super().__init__(instrumentor=instrumentor, category="system.openai")
129
+
130
+ @override
131
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
132
+ self._ingest["resource"] = kwargs.get("model", "")
133
+
134
+ if not (instance and hasattr(instance, "_client")) or OpenAiInstrumentor.is_azure(instance) is False:
135
+ return True
136
+
137
+ context = self._instrumentor.get_context_safe()
138
+ route_as_resource = extra_headers.get(PayiHeaderNames.route_as_resource) or context.get("route_as_resource")
139
+ resource_scope = extra_headers.get(PayiHeaderNames.resource_scope) or context.get("resource_scope")
140
+
141
+ if PayiHeaderNames.route_as_resource in extra_headers:
142
+ del extra_headers[PayiHeaderNames.route_as_resource]
143
+ if PayiHeaderNames.resource_scope in extra_headers:
144
+ del extra_headers[PayiHeaderNames.resource_scope]
145
+
146
+ if not route_as_resource:
147
+ logging.error("Azure OpenAI route as resource not found, not ingesting")
148
+ return False
149
+
150
+ if resource_scope:
151
+ if not(resource_scope in ["global", "datazone"] or resource_scope.startswith("region")):
152
+ logging.error("Azure OpenAI invalid resource scope, not ingesting")
153
+ return False
154
+
155
+ self._ingest["resource_scope"] = resource_scope
156
+
157
+ self._category = PayiCategories.azure_openai
158
+
159
+ self._ingest["category"] = self._category
160
+ self._ingest["resource"] = route_as_resource
161
+
162
+ return True
163
+
164
+ @override
165
+ def process_exception(self, exception: Exception, kwargs: Any, ) -> bool:
166
+ try:
167
+ status_code: Optional[int] = None
168
+
169
+ if hasattr(exception, "status_code"):
170
+ status_code = getattr(exception, "status_code", None)
171
+ if isinstance(status_code, int):
172
+ self._ingest["http_status_code"] = status_code
173
+
174
+ if not status_code:
175
+ return False
176
+
177
+ if hasattr(exception, "request_id"):
178
+ request_id = getattr(exception, "request_id", None)
179
+ if isinstance(request_id, str):
180
+ self._ingest["provider_response_id"] = request_id
181
+
182
+ if hasattr(exception, "response"):
183
+ response = getattr(exception, "response", None)
184
+ if hasattr(response, "text"):
185
+ text = getattr(response, "text", None)
186
+ if isinstance(text, str):
187
+ self._ingest["provider_response_json"] = text
188
+
189
+ except Exception as e:
190
+ logging.debug(f"Error processing exception: {e}")
191
+ return False
192
+
193
+ return True
194
+
195
+ class _OpenAiEmbeddingsProviderRequest(_OpenAiProviderRequest):
196
+ def __init__(self, instrumentor: _PayiInstrumentor):
197
+ super().__init__(instrumentor=instrumentor)
198
+
130
199
  @override
131
200
  def process_synchronous_response(
132
201
  self,
@@ -135,9 +204,9 @@ class _OpenAiEmbeddingsProviderRequest(_ProviderRequest):
135
204
  kwargs: Any) -> Any:
136
205
  return process_chat_synchronous_response(response, self._ingest, log_prompt_and_response, self._estimated_prompt_tokens)
137
206
 
138
- class _OpenAiChatProviderRequest(_ProviderRequest):
207
+ class _OpenAiChatProviderRequest(_OpenAiProviderRequest):
139
208
  def __init__(self, instrumentor: _PayiInstrumentor):
140
- super().__init__(instrumentor)
209
+ super().__init__(instrumentor=instrumentor)
141
210
  self._include_usage_added = False
142
211
 
143
212
  @override
@@ -163,39 +232,42 @@ class _OpenAiChatProviderRequest(_ProviderRequest):
163
232
  return send_chunk_to_client
164
233
 
165
234
  @override
166
- def process_request(self, kwargs: Any) -> None: # noqa: ARG001
167
- messages = kwargs.get("messages", None)
168
- if not messages or len(messages) == 0:
169
- return
170
-
171
- estimated_token_count = 0
172
- has_image = False
173
-
174
- try:
175
- enc = tiktoken.encoding_for_model(kwargs.get("model")) # type: ignore
176
- except KeyError:
177
- enc = tiktoken.get_encoding("o200k_base") # type: ignore
235
+ def process_request(self, instance: Any, extra_headers: 'dict[str, str]', kwargs: Any) -> bool:
236
+ result = super().process_request(instance, extra_headers, kwargs)
237
+ if result is False:
238
+ return result
178
239
 
179
- for message in messages:
180
- msg_has_image, msg_prompt_tokens = has_image_and_get_texts(enc, message.get('content', ''))
181
- if msg_has_image:
182
- has_image = True
183
- estimated_token_count += msg_prompt_tokens
184
-
185
- if has_image and estimated_token_count > 0:
186
- self._estimated_prompt_tokens = estimated_token_count
240
+ messages = kwargs.get("messages", None)
241
+ if messages:
242
+ estimated_token_count = 0
243
+ has_image = False
244
+
245
+ try:
246
+ enc = tiktoken.encoding_for_model(kwargs.get("model")) # type: ignore
247
+ except KeyError:
248
+ enc = tiktoken.get_encoding("o200k_base") # type: ignore
249
+
250
+ for message in messages:
251
+ msg_has_image, msg_prompt_tokens = has_image_and_get_texts(enc, message.get('content', ''))
252
+ if msg_has_image:
253
+ has_image = True
254
+ estimated_token_count += msg_prompt_tokens
255
+
256
+ if has_image and estimated_token_count > 0:
257
+ self._estimated_prompt_tokens = estimated_token_count
187
258
 
188
- stream: bool = kwargs.get("stream", False)
189
- if stream:
190
- add_include_usage = True
259
+ stream: bool = kwargs.get("stream", False)
260
+ if stream:
261
+ add_include_usage = True
191
262
 
192
- stream_options: dict[str, Any] = kwargs.get("stream_options", None)
193
- if stream_options and "include_usage" in stream_options:
194
- add_include_usage = stream_options["include_usage"] == False
263
+ stream_options: dict[str, Any] = kwargs.get("stream_options", None)
264
+ if stream_options and "include_usage" in stream_options:
265
+ add_include_usage = stream_options["include_usage"] == False
195
266
 
196
- if add_include_usage:
197
- kwargs['stream_options'] = {"include_usage": True}
198
- self._include_usage_added = True
267
+ if add_include_usage:
268
+ kwargs['stream_options'] = {"include_usage": True}
269
+ self._include_usage_added = True
270
+ return True
199
271
 
200
272
  @override
201
273
  def process_synchronous_response(
payi/lib/helpers.py CHANGED
@@ -15,7 +15,8 @@ class PayiHeaderNames:
15
15
  route_as_resource:str = "xProxy-RouteAs-Resource"
16
16
  provider_base_uri = "xProxy-Provider-BaseUri"
17
17
  resource_scope:str = "xProxy-Resource-Scope"
18
-
18
+ api_key:str = "xProxy-Api-Key"
19
+
19
20
  class PayiCategories:
20
21
  anthropic:str = "system.anthropic"
21
22
  openai:str = "system.openai"