lionagi 0.0.115__py3-none-any.whl → 0.0.204__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. lionagi/__init__.py +1 -2
  2. lionagi/_services/__init__.py +5 -0
  3. lionagi/_services/anthropic.py +79 -0
  4. lionagi/_services/base_service.py +414 -0
  5. lionagi/_services/oai.py +98 -0
  6. lionagi/_services/openrouter.py +44 -0
  7. lionagi/_services/services.py +91 -0
  8. lionagi/_services/transformers.py +46 -0
  9. lionagi/bridge/langchain.py +26 -16
  10. lionagi/bridge/llama_index.py +50 -20
  11. lionagi/configs/oai_configs.py +2 -14
  12. lionagi/configs/openrouter_configs.py +2 -2
  13. lionagi/core/__init__.py +7 -8
  14. lionagi/core/branch/branch.py +589 -0
  15. lionagi/core/branch/branch_manager.py +139 -0
  16. lionagi/core/branch/conversation.py +484 -0
  17. lionagi/core/core_util.py +59 -0
  18. lionagi/core/flow/flow.py +19 -0
  19. lionagi/core/flow/flow_util.py +62 -0
  20. lionagi/core/instruction_set/__init__.py +0 -5
  21. lionagi/core/instruction_set/instruction_set.py +343 -0
  22. lionagi/core/messages/messages.py +176 -0
  23. lionagi/core/sessions/__init__.py +0 -5
  24. lionagi/core/sessions/session.py +428 -0
  25. lionagi/loaders/chunker.py +51 -47
  26. lionagi/loaders/load_util.py +2 -2
  27. lionagi/loaders/reader.py +45 -39
  28. lionagi/models/imodel.py +53 -0
  29. lionagi/schema/async_queue.py +158 -0
  30. lionagi/schema/base_node.py +318 -147
  31. lionagi/schema/base_tool.py +31 -1
  32. lionagi/schema/data_logger.py +74 -38
  33. lionagi/schema/data_node.py +57 -6
  34. lionagi/structures/graph.py +132 -10
  35. lionagi/structures/relationship.py +58 -20
  36. lionagi/structures/structure.py +36 -25
  37. lionagi/tests/test_utils/test_api_util.py +219 -0
  38. lionagi/tests/test_utils/test_call_util.py +785 -0
  39. lionagi/tests/test_utils/test_encrypt_util.py +323 -0
  40. lionagi/tests/test_utils/test_io_util.py +238 -0
  41. lionagi/tests/test_utils/test_nested_util.py +338 -0
  42. lionagi/tests/test_utils/test_sys_util.py +358 -0
  43. lionagi/tools/tool_manager.py +186 -0
  44. lionagi/tools/tool_util.py +266 -3
  45. lionagi/utils/__init__.py +21 -61
  46. lionagi/utils/api_util.py +359 -71
  47. lionagi/utils/call_util.py +839 -264
  48. lionagi/utils/encrypt_util.py +283 -16
  49. lionagi/utils/io_util.py +178 -93
  50. lionagi/utils/nested_util.py +672 -0
  51. lionagi/utils/pd_util.py +57 -0
  52. lionagi/utils/sys_util.py +284 -156
  53. lionagi/utils/url_util.py +55 -0
  54. lionagi/version.py +1 -1
  55. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/METADATA +21 -17
  56. lionagi-0.0.204.dist-info/RECORD +106 -0
  57. lionagi/core/conversations/__init__.py +0 -5
  58. lionagi/core/conversations/conversation.py +0 -107
  59. lionagi/core/flows/__init__.py +0 -8
  60. lionagi/core/flows/flow.py +0 -8
  61. lionagi/core/flows/flow_util.py +0 -62
  62. lionagi/core/instruction_set/instruction_sets.py +0 -7
  63. lionagi/core/sessions/sessions.py +0 -185
  64. lionagi/endpoints/__init__.py +0 -5
  65. lionagi/endpoints/audio.py +0 -17
  66. lionagi/endpoints/chatcompletion.py +0 -54
  67. lionagi/messages/__init__.py +0 -11
  68. lionagi/messages/instruction.py +0 -15
  69. lionagi/messages/message.py +0 -110
  70. lionagi/messages/response.py +0 -33
  71. lionagi/messages/system.py +0 -12
  72. lionagi/objs/__init__.py +0 -11
  73. lionagi/objs/abc_objs.py +0 -39
  74. lionagi/objs/async_queue.py +0 -135
  75. lionagi/objs/messenger.py +0 -85
  76. lionagi/objs/tool_manager.py +0 -253
  77. lionagi/services/__init__.py +0 -11
  78. lionagi/services/base_api_service.py +0 -230
  79. lionagi/services/oai.py +0 -34
  80. lionagi/services/openrouter.py +0 -31
  81. lionagi/tests/test_api_util.py +0 -46
  82. lionagi/tests/test_call_util.py +0 -115
  83. lionagi/tests/test_convert_util.py +0 -202
  84. lionagi/tests/test_encrypt_util.py +0 -33
  85. lionagi/tests/test_flat_util.py +0 -426
  86. lionagi/tests/test_sys_util.py +0 -0
  87. lionagi/utils/convert_util.py +0 -229
  88. lionagi/utils/flat_util.py +0 -599
  89. lionagi-0.0.115.dist-info/RECORD +0 -110
  90. /lionagi/{services → _services}/anyscale.py +0 -0
  91. /lionagi/{services → _services}/azure.py +0 -0
  92. /lionagi/{services → _services}/bedrock.py +0 -0
  93. /lionagi/{services → _services}/everlyai.py +0 -0
  94. /lionagi/{services → _services}/gemini.py +0 -0
  95. /lionagi/{services → _services}/gpt4all.py +0 -0
  96. /lionagi/{services → _services}/huggingface.py +0 -0
  97. /lionagi/{services → _services}/litellm.py +0 -0
  98. /lionagi/{services → _services}/localai.py +0 -0
  99. /lionagi/{services → _services}/mistralai.py +0 -0
  100. /lionagi/{services → _services}/ollama.py +0 -0
  101. /lionagi/{services → _services}/openllm.py +0 -0
  102. /lionagi/{services → _services}/perplexity.py +0 -0
  103. /lionagi/{services → _services}/predibase.py +0 -0
  104. /lionagi/{services → _services}/rungpt.py +0 -0
  105. /lionagi/{services → _services}/vllm.py +0 -0
  106. /lionagi/{services → _services}/xinference.py +0 -0
  107. /lionagi/{endpoints/assistants.py → agents/__init__.py} +0 -0
  108. /lionagi/{tools → agents}/planner.py +0 -0
  109. /lionagi/{tools → agents}/prompter.py +0 -0
  110. /lionagi/{tools → agents}/scorer.py +0 -0
  111. /lionagi/{tools → agents}/summarizer.py +0 -0
  112. /lionagi/{tools → agents}/validator.py +0 -0
  113. /lionagi/{endpoints/embeddings.py → core/branch/__init__.py} +0 -0
  114. /lionagi/{services/anthropic.py → core/branch/cluster.py} +0 -0
  115. /lionagi/{endpoints/finetune.py → core/flow/__init__.py} +0 -0
  116. /lionagi/{endpoints/image.py → core/messages/__init__.py} +0 -0
  117. /lionagi/{endpoints/moderation.py → models/__init__.py} +0 -0
  118. /lionagi/{endpoints/vision.py → models/base_model.py} +0 -0
  119. /lionagi/{objs → schema}/status_tracker.py +0 -0
  120. /lionagi/tests/{test_io_util.py → test_utils/__init__.py} +0 -0
  121. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/LICENSE +0 -0
  122. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/WHEEL +0 -0
  123. {lionagi-0.0.115.dist-info → lionagi-0.0.204.dist-info}/top_level.txt +0 -0
lionagi/utils/api_util.py CHANGED
@@ -1,88 +1,376 @@
1
- # this module has no internal dependency
1
+ import aiohttp
2
+ import asyncio
3
+ import hashlib
4
+ import json
2
5
  import logging
3
6
  import re
4
- from typing import Callable
5
-
6
-
7
- def api_method(http_session, method: str = "post") -> Callable:
7
+ import tiktoken
8
+ from functools import lru_cache
9
+ from aiocache import cached
10
+ from typing import Any, Callable, Dict, Optional
11
+
12
+ from .sys_util import strip_lower
13
+
14
+ class APIUtil:
15
+ """
16
+ A utility class for assisting with common API usage patterns.
8
17
  """
9
- Retrieves the appropriate HTTP method from an HTTP session object.
10
18
 
11
- Parameters:
12
- http_session: The HTTP session object from which to retrieve the method.
13
- method (str): The HTTP method to retrieve. Defaults to 'post'.
19
+ @staticmethod
20
+ def api_method(http_session: aiohttp.ClientSession, method: str = "post") -> Callable:
21
+ """
22
+ Returns the corresponding HTTP method function from the http_session object.
14
23
 
15
- Returns:
16
- Callable: The HTTP method function from the session object.
24
+ Args:
25
+ http_session: The session object from the aiohttp library.
26
+ method: The HTTP method as a string.
17
27
 
18
- Raises:
19
- ValueError: If the provided method is not one of ['post', 'delete', 'head', 'options', 'patch'].
28
+ Returns:
29
+ The callable for the specified HTTP method.
20
30
 
21
- Examples:
22
- api_method = api_methods(session, "post") # Retrieves the 'post' method from the session
23
- """
24
- if method not in ["post", "delete", "head", "options", "patch"]:
25
- raise ValueError("Invalid request, method must be in ['post', 'delete', 'head', 'options', 'patch']")
26
- elif method == "post":
27
- return http_session.post
28
- elif method == "delete":
29
- return http_session.delete
30
- elif method == "head":
31
- return http_session.head
32
- elif method == "options":
33
- return http_session.options
34
- elif method == "patch":
35
- return http_session.patch
36
-
37
- def api_error(response_json: dict) -> bool:
38
- """
39
- Logs a warning and returns True if an error is found in the API response.
31
+ Raises:
32
+ ValueError: If the method is not one of the allowed ones.
40
33
 
41
- Parameters:
42
- response_json (dict): The JSON response from the API call.
34
+ Examples:
35
+ >>> session = aiohttp.ClientSession()
36
+ >>> post_method = APIUtil.api_method(session, "post")
37
+ >>> print(post_method)
38
+ <bound method ClientSession._request of <aiohttp.client.ClientSession object at 0x...>>
39
+ """
40
+ if method not in ["post", "delete", "head", "options", "patch"]:
41
+ raise ValueError("Invalid request, method must be in ['post', 'delete', 'head', 'options', 'patch']")
42
+ return getattr(http_session, method)
43
43
 
44
- Returns:
45
- bool: True if an error is present in the response, False otherwise.
44
+ @staticmethod
45
+ def api_error(response_json: Dict[str, Any]) -> bool:
46
+ """
47
+ Checks if the given response_json dictionary contains an "error" key.
46
48
 
47
- Examples:
48
- if api_error(response):
49
- # Handle the error
50
- """
51
- if "error" in response_json:
52
- logging.warning(f"API call failed with error: {response_json['error']}")
53
- return True
54
- return False
55
-
56
- def api_rate_limit_error(response_json: dict) -> bool:
57
- """
58
- Checks if the API response indicates a rate limit error.
49
+ Args:
50
+ response_json: The JSON response as a dictionary.
59
51
 
60
- Parameters:
61
- response_json (dict): The JSON response from the API call.
52
+ Returns:
53
+ True if there is an error, False otherwise.
62
54
 
63
- Returns:
64
- bool: True if the response contains a rate limit error message, False otherwise.
55
+ Examples:
56
+ >>> response_json_with_error = {"error": "Something went wrong"}
57
+ >>> APIUtil.api_error(response_json_with_error)
58
+ True
59
+ >>> response_json_without_error = {"result": "Success"}
60
+ >>> APIUtil.api_error(response_json_without_error)
61
+ False
62
+ """
63
+ if "error" in response_json:
64
+ logging.warning(f"API call failed with error: {response_json['error']}")
65
+ return True
66
+ return False
65
67
 
66
- Examples:
67
- if rate_limit_error(response):
68
- # Handle the rate limit error
69
- """
70
- return "Rate limit" in response_json["error"].get("message", "")
68
+ @staticmethod
69
+ def api_rate_limit_error(response_json: Dict[str, Any]) -> bool:
70
+ """
71
+ Checks if the error message in the response_json dictionary contains the phrase "Rate limit".
72
+
73
+ Args:
74
+ response_json: The JSON response as a dictionary.
71
75
 
72
- # credit to OpenAI for this method
73
- def api_endpoint_from_url(request_url: str) -> str:
74
- """
75
- Extracts the API endpoint from a given URL.
76
+ Returns:
77
+ True if the phrase "Rate limit" is found, False otherwise.
76
78
 
77
- Parameters:
78
- request_url (str): The URL from which to extract the API endpoint.
79
+ Examples:
80
+ >>> response_json_with_rate_limit = {"error": {"message": "Rate limit exceeded"}}
81
+ >>> api_rate_limit_error(response_json_with_rate_limit)
82
+ True
83
+ >>> response_json_without_rate_limit = {"error": {"message": "Another error"}}
84
+ >>> api_rate_limit_error(response_json_without_rate_limit)
85
+ False
86
+ """
87
+ return "Rate limit" in response_json.get("error", {}).get("message", "")
79
88
 
80
- Returns:
81
- str: The extracted API endpoint, or an empty string if no match is found.
89
+ @staticmethod
90
+ @lru_cache(maxsize=128)
91
+ def api_endpoint_from_url(request_url: str) -> str:
92
+ """
93
+ Extracts the API endpoint from a given URL using a regular expression.
82
94
 
83
- Examples:
84
- endpoint = api_endpoint_from_url("https://api.example.com/v1/users")
85
- # endpoint will be 'users'
86
- """
87
- match = re.search(r"^https://[^/]+/v\d+/(.+)$", request_url)
88
- return match.group(1) if match else ""
95
+ Args:
96
+ request_url: The full URL to the API endpoint.
97
+
98
+ Returns:
99
+ The extracted endpoint or an empty string if the pattern does not match.
100
+
101
+ Examples:
102
+ >>> valid_url = "https://api.example.com/v1/users"
103
+ >>> api_endpoint_from_url(valid_url)
104
+ 'users'
105
+ >>> invalid_url = "https://api.example.com/users"
106
+ >>> api_endpoint_from_url(invalid_url)
107
+ ''
108
+ """
109
+ match = re.search(r"^https://[^/]+(/.+)?/v\d+/(.+)$", request_url)
110
+ return match.group(2) if match else ""
111
+
112
+ @staticmethod
113
+ async def unified_api_call(http_session: aiohttp.ClientSession, method: str, url: str, **kwargs) -> Any:
114
+ """
115
+ Makes an API call and automatically retries on rate limit error.
116
+
117
+ Args:
118
+ http_session: The session object from the aiohttp library.
119
+ method: The HTTP method as a string.
120
+ url: The URL to which the request is made.
121
+ **kwargs: Additional keyword arguments to pass to the API call.
122
+
123
+ Returns:
124
+ The JSON response as a dictionary.
125
+
126
+ Examples:
127
+ >>> session = aiohttp.ClientSession()
128
+ >>> success_url = "https://api.example.com/v1/success"
129
+ >>> print(await unified_api_call(session, 'get', success_url))
130
+ {'result': 'Success'}
131
+ >>> rate_limit_url = "https://api.example.com/v1/rate_limit"
132
+ >>> print(await unified_api_call(session, 'get', rate_limit_url))
133
+ {'error': {'message': 'Rate limit exceeded'}}
134
+ """
135
+ api_call = APIUtil.api_method(http_session, method)
136
+ retry_count = 3
137
+ retry_delay = 5 # seconds
138
+
139
+ for attempt in range(retry_count):
140
+ async with api_call(url, **kwargs) as response:
141
+ response_json = await response.json()
142
+
143
+ if not APIUtil.api_error(response_json):
144
+ return response_json
145
+
146
+ if APIUtil.api_rate_limit_error(response_json) and attempt < retry_count - 1:
147
+ logging.warning(f"Rate limit error detected. Retrying in {retry_delay} seconds...")
148
+ await asyncio.sleep(retry_delay)
149
+ else:
150
+ break
151
+
152
+ return response_json
153
+
154
+ @staticmethod
155
+ def get_cache_key(url: str, params: Optional[Dict[str, Any]]) -> str:
156
+ """
157
+ Creates a unique cache key based on the URL and parameters.
158
+ """
159
+ param_str = json.dumps(params, sort_keys=True) if params else ""
160
+ return hashlib.md5((url + param_str).encode('utf-8')).hexdigest()
161
+
162
+ @staticmethod
163
+ async def retry_api_call(http_session: aiohttp.ClientSession, url: str, retries: int = 3, backoff_factor: float = 0.5, **kwargs) -> Any:
164
+ """
165
+ Retries an API call on failure, with exponential backoff.
166
+
167
+ Args:
168
+ http_session: The aiohttp client session.
169
+ url: The URL to make the API call.
170
+ retries: The number of times to retry.
171
+ backoff_factor: The backoff factor for retries.
172
+ **kwargs: Additional arguments for the API call.
173
+
174
+ Returns:
175
+ The response from the API call, if successful; otherwise, None.
176
+ """
177
+ for attempt in range(retries):
178
+ try:
179
+ async with http_session.get(url, **kwargs) as response:
180
+ response.raise_for_status()
181
+ return await response.json()
182
+ except aiohttp.ClientError:
183
+ if attempt < retries - 1:
184
+ delay = backoff_factor * (2 ** attempt)
185
+ logging.info(f"Retrying {url} in {delay} seconds...")
186
+ await asyncio.sleep(delay)
187
+ else:
188
+ logging.error(f"Failed to retrieve data from {url} after {retries} attempts.")
189
+ return None
190
+
191
+ @staticmethod
192
+ async def upload_file_with_retry(http_session: aiohttp.ClientSession, url: str, file_path: str, param_name: str = 'file', additional_data: Dict[str, Any] = None, retries: int = 3) -> Any:
193
+ """
194
+ Uploads a file to a specified URL with a retry mechanism for handling failures.
195
+
196
+ Args:
197
+ http_session: The HTTP session object to use for making the request.
198
+ url: The URL to which the file will be uploaded.
199
+ file_path: The path to the file that will be uploaded.
200
+ param_name: The name of the parameter expected by the server for the file upload.
201
+ additional_data: Additional data to be sent with the upload.
202
+ retries: The number of times to retry the upload in case of failure.
203
+
204
+ Returns:
205
+ The HTTP response object.
206
+
207
+ Examples:
208
+ >>> session = aiohttp.ClientSession()
209
+ >>> response = await APIUtil.upload_file_with_retry(session, 'http://example.com/upload', 'path/to/file.txt')
210
+ >>> response.status
211
+ 200
212
+ """
213
+ for attempt in range(retries):
214
+ try:
215
+ with open(file_path, 'rb') as file:
216
+ files = {param_name: file}
217
+ additional_data = additional_data if additional_data else {}
218
+ async with http_session.post(url, data={**files, **additional_data}) as response:
219
+ response.raise_for_status()
220
+ return await response.json()
221
+ except aiohttp.ClientError as e:
222
+ if attempt == retries - 1:
223
+ raise e
224
+ backoff = 2 ** attempt
225
+ logging.info(f"Retrying {url} in {backoff} seconds...")
226
+ await asyncio.sleep(backoff)
227
+
228
+ @staticmethod
229
+ @cached(ttl=10 * 60) # Cache the result for 10 minutes
230
+ async def get_oauth_token_with_cache(http_session: aiohttp.ClientSession, auth_url: str, client_id: str, client_secret: str, scope: str) -> str:
231
+ """
232
+ Retrieves an OAuth token from the authentication server and caches it to avoid unnecessary requests.
233
+
234
+ Args:
235
+ http_session: The HTTP session object to use for making the request.
236
+ auth_url: The URL of the authentication server.
237
+ client_id: The client ID for OAuth authentication.
238
+ client_secret: The client secret for OAuth authentication.
239
+ scope: The scope for which the OAuth token is requested.
240
+
241
+ Returns:
242
+ The OAuth token as a string.
243
+
244
+ Examples:
245
+ >>> session = aiohttp.ClientSession()
246
+ >>> token = await APIUtil.get_oauth_token_with_cache(session, 'http://auth.example.com', 'client_id', 'client_secret', 'read')
247
+ >>> token
248
+ 'mock_access_token'
249
+ """
250
+ async with http_session.post(auth_url, data={
251
+ 'grant_type': 'client_credentials',
252
+ 'client_id': client_id,
253
+ 'client_secret': client_secret,
254
+ 'scope': scope
255
+ }) as auth_response:
256
+ auth_response.raise_for_status()
257
+ return (await auth_response.json()).get('access_token')
258
+
259
+ @staticmethod
260
+ @cached(ttl=10 * 60)
261
+ async def cached_api_call(http_session: aiohttp.ClientSession, url: str, **kwargs) -> Any:
262
+ """
263
+ Makes an API call.
264
+
265
+ Args:
266
+ http_session: The aiohttp client session.
267
+ url: The URL for the API call.
268
+ **kwargs: Additional arguments for the API call.
269
+
270
+ Returns:
271
+ The response from the API call, if successful; otherwise, None.
272
+ """
273
+ try:
274
+ async with http_session.get(url, **kwargs) as response:
275
+ response.raise_for_status()
276
+ return await response.json()
277
+ except aiohttp.ClientError as e:
278
+ logging.error(f"API call to {url} failed: {e}")
279
+ return None
280
+
281
+ @staticmethod
282
+ # @lru_cache(maxsize=1024)
283
+ def calculate_num_token(
284
+ payload: Dict[str, Any] = None,
285
+ api_endpoint: str = None,
286
+ token_encoding_name: str = None,
287
+ ) -> int:
288
+ """
289
+ Calculates the number of tokens required for a request based on the payload and API endpoint.
290
+
291
+ The token calculation logic might vary based on different API endpoints and payload content.
292
+ This method should be implemented in a subclass to provide the specific calculation logic
293
+ for the OpenAI API.
294
+
295
+ Parameters:
296
+ payload (Dict[str, Any]): The payload of the request.
297
+
298
+ api_endpoint (str): The specific API endpoint for the request.
299
+
300
+ token_encoding_name (str): The name of the token encoding method.
301
+
302
+ Returns:
303
+ int: The estimated number of tokens required for the request.
304
+
305
+ Example:
306
+ >>> rate_limiter = OpenAIRateLimiter(100, 200)
307
+ >>> payload = {'prompt': 'Translate the following text:', 'max_tokens': 50}
308
+ >>> rate_limiter.calculate_num_token(payload, 'completions')
309
+ # Expected token calculation for the given payload and endpoint.
310
+ """
311
+
312
+ encoding = tiktoken.get_encoding(token_encoding_name)
313
+ if api_endpoint.endswith("completions"):
314
+ max_tokens = payload.get("max_tokens", 15)
315
+ n = payload.get("n", 1)
316
+ completion_tokens = n * max_tokens
317
+
318
+ # chat completions
319
+ if api_endpoint.startswith("chat/"):
320
+ num_tokens = 0
321
+ for message in payload["messages"]:
322
+ num_tokens += 4 # every message follows <im_start>{role/name}\n{content}<im_end>\n
323
+ for key, value in message.items():
324
+ num_tokens += len(encoding.encode(value))
325
+ if key == "name": # if there's a name, the role is omitted
326
+ num_tokens -= (
327
+ 1 # role is always required and always 1 token
328
+ )
329
+ num_tokens += 2 # every reply is primed with <im_start>assistant
330
+ return num_tokens + completion_tokens
331
+ # normal completions
332
+ else:
333
+ prompt = payload["prompt"]
334
+ if isinstance(prompt, str): # single prompt
335
+ prompt_tokens = len(encoding.encode(prompt))
336
+ num_tokens = prompt_tokens + completion_tokens
337
+ return num_tokens
338
+ elif isinstance(prompt, list): # multiple prompts
339
+ prompt_tokens = sum([len(encoding.encode(p)) for p in prompt])
340
+ num_tokens = prompt_tokens + completion_tokens * len(prompt)
341
+ return num_tokens
342
+ else:
343
+ raise TypeError(
344
+ 'Expecting either string or list of strings for "prompt" field in completion request'
345
+ )
346
+ elif api_endpoint == "embeddings":
347
+ input = payload["input"]
348
+ if isinstance(input, str): # single input
349
+ num_tokens = len(encoding.encode(input))
350
+ return num_tokens
351
+ elif isinstance(input, list): # multiple inputs
352
+ num_tokens = sum([len(encoding.encode(i)) for i in input])
353
+ return num_tokens
354
+ else:
355
+ raise TypeError(
356
+ 'Expecting either string or list of strings for "inputs" field in embedding request'
357
+ )
358
+ else:
359
+ raise NotImplementedError(
360
+ f'API endpoint "{api_endpoint}" not implemented in this script'
361
+ )
362
+
363
+ @staticmethod
364
+ def _create_payload(input_, config, required_, optional_, input_key,**kwargs):
365
+ config = {**config, **kwargs}
366
+ payload = {input_key: input_}
367
+
368
+ for key in required_:
369
+ payload.update({key: config[key]})
370
+
371
+ for key in optional_:
372
+ if bool(config[key]) is True and strip_lower(config[key]) != "none":
373
+ payload.update({key: config[key]})
374
+
375
+ return payload
376
+