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/__init__.py CHANGED
@@ -22,8 +22,7 @@ from .utils import *
22
22
  from .schema import *
23
23
  from .structures import *
24
24
  from .loaders import *
25
- from .messages import *
26
- from .objs import *
25
+ from ._services import *
27
26
  from .tools import *
28
27
  from .core import *
29
28
 
@@ -0,0 +1,5 @@
1
+ from .services import Services
2
+
3
+ __all__ = [
4
+ 'Services'
5
+ ]
@@ -0,0 +1,79 @@
1
+ from os import getenv
2
+ from .base_service import BaseService, PayloadCreation
3
+
4
+ class AnthropicService(BaseService):
5
+ """
6
+ A service to interact with Anthropic's API endpoints.
7
+
8
+ Attributes:
9
+ base_url (str): The base URL for the Anthropic API.
10
+ available_endpoints (list): A list of available API endpoints.
11
+ schema (dict): The schema configuration for the API.
12
+ key_scheme (str): The environment variable name for Anthropic API key.
13
+ token_encoding_name (str): The default token encoding scheme.
14
+
15
+ Examples:
16
+ >>> service = AnthropicService(api_key="your_api_key")
17
+ >>> asyncio.run(service.serve("Hello, world!", "chat/completions"))
18
+ (payload, completion)
19
+ """
20
+
21
+ base_url = "https://api.anthropic.com/v1/"
22
+ available_endpoints = ['chat/completions']
23
+ schema = {} # TODO
24
+ key_scheme = "ANTHROPIC_API_KEY"
25
+ token_encoding_name = "cl100k_base"
26
+
27
+ def __init__(self, api_key = None, key_scheme = None,schema = None, token_encoding_name: str = "cl100k_base", **kwargs):
28
+ key_scheme = key_scheme or self.key_scheme
29
+ super().__init__(
30
+ api_key = api_key or getenv(key_scheme),
31
+ schema = schema or self.schema,
32
+ token_encoding_name=token_encoding_name,
33
+ **kwargs
34
+ )
35
+ self.active_endpoint = []
36
+
37
+ async def serve(self, input_, endpoint="chat/completions", method="post", **kwargs):
38
+ """
39
+ Serves the input using the specified endpoint and method.
40
+
41
+ Args:
42
+ input_: The input text to be processed.
43
+ endpoint: The API endpoint to use for processing.
44
+ method: The HTTP method to use for the request.
45
+ **kwargs: Additional keyword arguments to pass to the payload creation.
46
+
47
+ Returns:
48
+ A tuple containing the payload and the completion response from the API.
49
+ """
50
+ if endpoint not in self.active_endpoint:
51
+ await self. init_endpoint(endpoint)
52
+ if endpoint == "chat/completions":
53
+ return await self.serve_chat(input_, **kwargs)
54
+ else:
55
+ return ValueError(f'{endpoint} is currently not supported')
56
+
57
+ async def serve_chat(self, messages, **kwargs):
58
+ """
59
+ Serves the chat completion request with the given messages.
60
+
61
+ Args:
62
+ messages: The messages to be included in the chat completion.
63
+ **kwargs: Additional keyword arguments for payload creation.
64
+
65
+ Returns:
66
+ A tuple containing the payload and the completion response from the API.
67
+ """
68
+ if "chat/completions" not in self.active_endpoint:
69
+ await self. init_endpoint("chat/completions")
70
+ self.active_endpoint.append("chat/completions")
71
+ payload = PayloadCreation.chat_completion(
72
+ messages, self.endpoints["chat/completions"].config, self.schema["chat/completions"], **kwargs)
73
+
74
+ try:
75
+ completion = await self.call_api(payload, "chat/completions", "post")
76
+ return payload, completion
77
+ except Exception as e:
78
+ self.status_tracker.num_tasks_failed += 1
79
+ raise e
@@ -0,0 +1,414 @@
1
+ import asyncio
2
+ import logging
3
+ import aiohttp
4
+ from abc import ABC
5
+ from dataclasses import dataclass
6
+ from typing import Any, Dict, NoReturn, Optional, Type, List, Union
7
+
8
+ from ..utils import nget, APIUtil
9
+
10
+
11
+ @dataclass
12
+ class StatusTracker:
13
+ """
14
+ Keeps track of various task statuses within a system.
15
+
16
+ Attributes:
17
+ num_tasks_started (int): The number of tasks that have been initiated.
18
+ num_tasks_in_progress (int): The number of tasks currently being processed.
19
+ num_tasks_succeeded (int): The number of tasks that have completed successfully.
20
+ num_tasks_failed (int): The number of tasks that have failed.
21
+ num_rate_limit_errors (int): The number of tasks that failed due to rate limiting.
22
+ num_api_errors (int): The number of tasks that failed due to API errors.
23
+ num_other_errors (int): The number of tasks that failed due to other errors.
24
+
25
+ Examples:
26
+ >>> tracker = StatusTracker()
27
+ >>> tracker.num_tasks_started += 1
28
+ >>> tracker.num_tasks_succeeded += 1
29
+ """
30
+ num_tasks_started: int = 0
31
+ num_tasks_in_progress: int = 0
32
+ num_tasks_succeeded: int = 0
33
+ num_tasks_failed: int = 0
34
+ num_rate_limit_errors: int = 0
35
+ num_api_errors: int = 0
36
+ num_other_errors: int = 0
37
+
38
+
39
+ class BaseRateLimiter(ABC):
40
+ """
41
+ Abstract base class for implementing rate limiters.
42
+
43
+ This class provides the basic structure for rate limiters, including
44
+ the replenishment of request and token capacities at regular intervals.
45
+
46
+ Attributes:
47
+ interval: The time interval in seconds for replenishing capacities.
48
+ max_requests: The maximum number of requests allowed per interval.
49
+ max_tokens: The maximum number of tokens allowed per interval.
50
+ available_request_capacity: The current available request capacity.
51
+ available_token_capacity: The current available token capacity.
52
+ rate_limit_replenisher_task: The asyncio task for replenishing capacities.
53
+ """
54
+
55
+ def __init__(self, max_requests: int, max_tokens: int, interval: int = 60, token_encoding_name=None) -> None:
56
+ self.interval: int = interval
57
+ self.max_requests: int = max_requests
58
+ self.max_tokens: int = max_tokens
59
+ self.available_request_capacity: int = max_requests
60
+ self.available_token_capacity: int = max_tokens
61
+ self.rate_limit_replenisher_task: Optional[asyncio.Task[NoReturn]] = None
62
+ self._stop_replenishing: asyncio.Event = asyncio.Event()
63
+ self._lock: asyncio.Lock = asyncio.Lock()
64
+ self.token_encoding_name = token_encoding_name
65
+
66
+ async def start_replenishing(self) -> NoReturn:
67
+ """Starts the replenishment of rate limit capacities at regular intervals."""
68
+ try:
69
+ while not self._stop_replenishing.is_set():
70
+ await asyncio.sleep(self.interval)
71
+ async with self._lock:
72
+ self.available_request_capacity = self.max_requests
73
+ self.available_token_capacity = self.max_tokens
74
+ except asyncio.CancelledError:
75
+ logging.info("Rate limit replenisher task cancelled.")
76
+ except Exception as e:
77
+ logging.error(f"An error occurred in the rate limit replenisher: {e}")
78
+
79
+ async def stop_replenishing(self) -> None:
80
+ """Stops the replenishment task."""
81
+ if self.rate_limit_replenisher_task:
82
+ self.rate_limit_replenisher_task.cancel()
83
+ await self.rate_limit_replenisher_task
84
+ self._stop_replenishing.set()
85
+
86
+ async def request_permission(self, required_tokens) -> bool:
87
+ """Requests permission to make an API call.
88
+
89
+ Returns True if the request can be made immediately, otherwise False.
90
+ """
91
+ async with self._lock:
92
+ if self.available_request_capacity > 0 and self.available_token_capacity > 0:
93
+ self.available_request_capacity -= 1
94
+ self.available_token_capacity -= required_tokens # Assuming 1 token per request for simplicity
95
+ return True
96
+ return False
97
+
98
+ async def _call_api(
99
+ self,
100
+ http_session,
101
+ endpoint: str,
102
+ base_url: str,
103
+ api_key: str,
104
+ max_attempts: int = 3,
105
+ method: str = "post",
106
+ payload: Dict[str, any]=None
107
+ ) -> Optional[Dict[str, any]]:
108
+ """
109
+ Makes an API call to the specified endpoint using the provided HTTP session.
110
+
111
+ Args:
112
+ http_session: The aiohttp client session to use for the API call.
113
+ endpoint: The API endpoint to call.
114
+ base_url: The base URL of the API.
115
+ api_key: The API key for authentication.
116
+ max_attempts: The maximum number of attempts for the API call.
117
+ method: The HTTP method to use for the API call.
118
+ payload: The payload to send with the API call.
119
+
120
+ Returns:
121
+ The JSON response from the API call if successful, otherwise None.
122
+ """
123
+ endpoint = APIUtil.api_endpoint_from_url(base_url + endpoint)
124
+ while True:
125
+ if self.available_request_capacity < 1 or self.available_token_capacity < 10: # Minimum token count
126
+ await asyncio.sleep(1) # Wait for capacity
127
+ continue
128
+ required_tokens = APIUtil.calculate_num_token(payload, endpoint, self.token_encoding_name)
129
+
130
+ if await self.request_permission(required_tokens):
131
+ request_headers = {"Authorization": f"Bearer {api_key}"}
132
+ attempts_left = max_attempts
133
+
134
+ while attempts_left > 0:
135
+ try:
136
+ method = APIUtil.api_method(http_session, method)
137
+ async with method(
138
+ url=(base_url+endpoint), headers=request_headers, json=payload
139
+ ) as response:
140
+ response_json = await response.json()
141
+
142
+ if "error" in response_json:
143
+ logging.warning(
144
+ f"API call failed with error: {response_json['error']}"
145
+ )
146
+ attempts_left -= 1
147
+
148
+ if "Rate limit" in response_json["error"].get("message", ""):
149
+ await asyncio.sleep(15)
150
+ else:
151
+ return response_json
152
+ except Exception as e:
153
+ logging.warning(f"API call failed with exception: {e}")
154
+ attempts_left -= 1
155
+
156
+ logging.error("API call failed after all attempts.")
157
+ break
158
+ else:
159
+ await asyncio.sleep(1)
160
+
161
+ @classmethod
162
+ async def create(cls, max_requests: int, max_tokens: int, interval: int = 60, token_encoding_name = None) -> 'BaseRateLimiter':
163
+ """
164
+ Creates an instance of BaseRateLimiter and starts the replenisher task.
165
+
166
+ Args:
167
+ max_requests: The maximum number of requests allowed per interval.
168
+ max_tokens: The maximum number of tokens allowed per interval.
169
+ interval: The time interval in seconds for replenishing capacities.
170
+ token_encoding_name: The name of the token encoding to use.
171
+
172
+ Returns:
173
+ An instance of BaseRateLimiter with the replenisher task started.
174
+ """
175
+ instance = cls(max_requests, max_tokens, interval, token_encoding_name)
176
+ instance.rate_limit_replenisher_task = asyncio.create_task(
177
+ instance.start_replenishing()
178
+ )
179
+ return instance
180
+
181
+
182
+ class SimpleRateLimiter(BaseRateLimiter):
183
+ """
184
+ A simple implementation of a rate limiter.
185
+
186
+ Inherits from BaseRateLimiter and provides a basic rate limiting mechanism.
187
+ """
188
+
189
+ def __init__(self, max_requests: int, max_tokens: int, interval: int = 60, token_encoding_name=None) -> None:
190
+ """Initializes the SimpleRateLimiter with the specified parameters."""
191
+ super().__init__(max_requests, max_tokens, interval, token_encoding_name)
192
+
193
+
194
+ class EndPoint:
195
+ """
196
+ Represents an API endpoint with rate limiting capabilities.
197
+
198
+ This class encapsulates the details of an API endpoint, including its rate limiter.
199
+
200
+ Attributes:
201
+ endpoint (str): The API endpoint path.
202
+ rate_limiter_class (Type[li.BaseRateLimiter]): The class used for rate limiting requests to the endpoint.
203
+ max_requests (int): The maximum number of requests allowed per interval.
204
+ max_tokens (int): The maximum number of tokens allowed per interval.
205
+ interval (int): The time interval in seconds for replenishing rate limit capacities.
206
+ config (Dict): Configuration parameters for the endpoint.
207
+ rate_limiter (Optional[li.BaseRateLimiter]): The rate limiter instance for this endpoint.
208
+
209
+ Examples:
210
+ # Example usage of EndPoint with SimpleRateLimiter
211
+ endpoint = EndPoint(
212
+ max_requests=100,
213
+ max_tokens=1000,
214
+ interval=60,
215
+ endpoint_='chat/completions',
216
+ rate_limiter_class=li.SimpleRateLimiter,
217
+ config={'param1': 'value1'}
218
+ )
219
+ asyncio.run(endpoint.init_rate_limiter())
220
+ """
221
+
222
+ def __init__(
223
+ self,
224
+ max_requests: int = 1000,
225
+ max_tokens: int = 100000,
226
+ interval: int = 60,
227
+ endpoint_: Optional[str] = None,
228
+ rate_limiter_class: Type[BaseRateLimiter] = SimpleRateLimiter,
229
+ token_encoding_name=None,
230
+ config: Dict = None,
231
+ ) -> None:
232
+ self.endpoint = endpoint_ or 'chat/completions'
233
+ self.rate_limiter_class = rate_limiter_class
234
+ self.max_requests = max_requests
235
+ self.max_tokens = max_tokens
236
+ self.interval = interval
237
+ self.token_encoding_name = token_encoding_name
238
+ self.config = config or {}
239
+ self.rate_limiter: Optional[BaseRateLimiter] = None
240
+ self._has_initialized = False
241
+
242
+ async def init_rate_limiter(self) -> None:
243
+ """Initializes the rate limiter for the endpoint."""
244
+ self.rate_limiter = await self.rate_limiter_class.create(
245
+ self.max_requests, self.max_tokens, self.interval, self.token_encoding_name
246
+ )
247
+ self._has_initialized = True
248
+
249
+
250
+ class BaseService:
251
+ """
252
+ Base class for services that interact with API endpoints.
253
+
254
+ This class provides a foundation for services that need to make API calls with rate limiting.
255
+
256
+ Attributes:
257
+ api_key (Optional[str]): The API key used for authentication.
258
+ schema (Dict[str, Any]): The schema defining the service's endpoints.
259
+ status_tracker (StatusTracker): The object tracking the status of API calls.
260
+ endpoints (Dict[str, EndPoint]): A dictionary of endpoint objects.
261
+ """
262
+
263
+ base_url: str = ''
264
+ available_endpoints: list = []
265
+
266
+ def __init__(
267
+ self,
268
+ api_key: Optional[str] = None,
269
+ schema: Dict[str, Any] = None,
270
+ token_encoding_name: str = None,
271
+ max_tokens : int = 100_000,
272
+ max_requests : int = 1_000,
273
+ interval: int = 60
274
+ ) -> None:
275
+ self.api_key = api_key
276
+ self.schema = schema or {}
277
+ self.status_tracker = StatusTracker()
278
+ self.endpoints: Dict[str, EndPoint] = {}
279
+ self.token_encoding_name = token_encoding_name
280
+ self.chat_config_rate_limit = {
281
+ 'max_requests': max_requests,
282
+ 'max_tokens': max_tokens,
283
+ 'interval': interval
284
+ }
285
+
286
+
287
+ async def init_endpoint(self, endpoint_: Optional[Union[List[str], List[EndPoint], str, EndPoint]] = None) -> None:
288
+ """
289
+ Initializes the specified endpoint or all endpoints if none is specified.
290
+
291
+ Args:
292
+ endpoint_: The endpoint(s) to initialize. Can be a string, an EndPoint, a list of strings, or a list of EndPoints.
293
+ """
294
+
295
+ if endpoint_:
296
+ if not isinstance(endpoint_, list):
297
+ endpoint_ = [endpoint_]
298
+ for ep in endpoint_:
299
+ if ep not in self.available_endpoints:
300
+ raise ValueError (f"Endpoint {ep} not available for service {self.__class__.__name__}")
301
+
302
+ if ep not in self.endpoints:
303
+ endpoint_config = nget(self.schema, [ep, 'config'])
304
+ self.schema.get(ep, {})
305
+ if isinstance(ep, EndPoint):
306
+ self.endpoints[ep.endpoint] = ep
307
+ else:
308
+ if ep == "chat/completions":
309
+ self.endpoints[ep] = EndPoint(
310
+ max_requests=self.chat_config_rate_limit.get('max_requests', 1000),
311
+ max_tokens=self.chat_config_rate_limit.get('max_tokens', 100000),
312
+ interval=self.chat_config_rate_limit.get('interval', 60),
313
+ endpoint_=ep,
314
+ token_encoding_name=self.token_encoding_name,
315
+ config=endpoint_config,
316
+ )
317
+ else:
318
+ self.endpoints[ep] = EndPoint(
319
+ max_requests=endpoint_config.get('max_requests', 1000) if endpoint_config.get('max_requests', 1000) is not None else 1000,
320
+ max_tokens=endpoint_config.get('max_tokens', 100000) if endpoint_config.get('max_tokens', 100000) is not None else 100000,
321
+ interval=endpoint_config.get('interval', 60) if endpoint_config.get('interval', 60) is not None else 60,
322
+ endpoint_=ep,
323
+ token_encoding_name=self.token_encoding_name,
324
+ config=endpoint_config,
325
+ )
326
+
327
+ if not self.endpoints[ep]._has_initialized:
328
+ await self.endpoints[ep].init_rate_limiter()
329
+
330
+ else:
331
+ for ep in self.available_endpoints:
332
+ endpoint_config = nget(self.schema, [ep, 'config'])
333
+ self.schema.get(ep, {})
334
+ if ep not in self.endpoints:
335
+ self.endpoints[ep] = EndPoint(
336
+ max_requests=endpoint_config.get('max_requests', 1000),
337
+ max_tokens=endpoint_config.get('max_tokens', 100000),
338
+ interval=endpoint_config.get('interval', 60),
339
+ endpoint_=ep,
340
+ token_encoding_name=self.token_encoding_name,
341
+ config=endpoint_config,
342
+ )
343
+ if not self.endpoints[ep]._has_initialized:
344
+ await self.endpoints[ep].init_rate_limiter()
345
+
346
+ async def call_api(self, payload, endpoint, method):
347
+ """
348
+ Calls the specified API endpoint with the given payload and method.
349
+
350
+ Args:
351
+ payload: The payload to send with the API call.
352
+ endpoint: The endpoint to call.
353
+ method: The HTTP method to use for the call.
354
+
355
+ Returns:
356
+ The response from the API call.
357
+
358
+ Raises:
359
+ ValueError: If the endpoint has not been initialized.
360
+ """
361
+ if endpoint not in self.endpoints.keys():
362
+ raise ValueError(f'The endpoint {endpoint} has not initialized.')
363
+ async with aiohttp.ClientSession() as http_session:
364
+ completion = await self.endpoints[endpoint].rate_limiter._call_api(
365
+ http_session=http_session, endpoint=endpoint, base_url=self.base_url, api_key=self.api_key,
366
+ method=method, payload=payload)
367
+ return completion
368
+
369
+
370
+ class PayloadCreation:
371
+
372
+ @classmethod
373
+ def chat_completion(cls, messages, llmconfig, schema, **kwargs):
374
+ """
375
+ Creates a payload for the chat completion operation.
376
+
377
+ Args:
378
+ messages: The messages to include in the chat completion.
379
+ llmconfig: Configuration for the language model.
380
+ schema: The schema describing required and optional fields.
381
+ **kwargs: Additional keyword arguments.
382
+
383
+ Returns:
384
+ The constructed payload.
385
+ """
386
+ return APIUtil._create_payload(
387
+ input_=messages,
388
+ config=llmconfig,
389
+ required_=schema['required'],
390
+ optional_=schema['optional'],
391
+ input_key="messages",
392
+ **kwargs)
393
+
394
+ @classmethod
395
+ def fine_tuning(cls, training_file, llmconfig, schema, **kwargs):
396
+ """
397
+ Creates a payload for the fine-tuning operation.
398
+
399
+ Args:
400
+ training_file: The file containing training data.
401
+ llmconfig: Configuration for the language model.
402
+ schema: The schema describing required and optional fields.
403
+ **kwargs: Additional keyword arguments.
404
+
405
+ Returns:
406
+ The constructed payload.
407
+ """
408
+ return APIUtil._create_payload(
409
+ input_=training_file,
410
+ config=llmconfig,
411
+ required_=schema['required'],
412
+ optional_=schema['optional'],
413
+ input_key="training_file",
414
+ **kwargs)
@@ -0,0 +1,98 @@
1
+ from os import getenv
2
+ from ..configs.oai_configs import oai_schema
3
+ from .base_service import BaseService, PayloadCreation
4
+
5
+ class OpenAIService(BaseService):
6
+ """
7
+ A service to interact with OpenAI's API endpoints.
8
+
9
+ Attributes:
10
+ base_url (str): The base URL for the OpenAI API.
11
+ available_endpoints (list): A list of available API endpoints.
12
+ schema (dict): The schema configuration for the API.
13
+ key_scheme (str): The environment variable name for OpenAI API key.
14
+ token_encoding_name (str): The default token encoding scheme.
15
+
16
+ Examples:
17
+ >>> service = OpenAIService(api_key="your_api_key")
18
+ >>> asyncio.run(service.serve("Hello, world!", "chat/completions"))
19
+ (payload, completion)
20
+
21
+ >>> service = OpenAIService()
22
+ >>> asyncio.run(service.serve("Convert this text to speech.", "audio_speech"))
23
+ """
24
+
25
+ base_url = "https://api.openai.com/v1/"
26
+ available_endpoints = ['chat/completions', 'finetune', 'audio_speech', 'audio_transcriptions', 'audio_translations']
27
+ schema = oai_schema
28
+ key_scheme = "OPENAI_API_KEY"
29
+ token_encoding_name = "cl100k_base"
30
+
31
+ def __init__(self, api_key = None, key_scheme = None,schema = None, token_encoding_name: str = "cl100k_base", **kwargs):
32
+ key_scheme = key_scheme or self.key_scheme
33
+ super().__init__(
34
+ api_key = api_key or getenv(key_scheme),
35
+ schema = schema or self.schema,
36
+ token_encoding_name=token_encoding_name,
37
+ **kwargs
38
+ )
39
+ self.active_endpoint = []
40
+
41
+ async def serve(self, input_, endpoint="chat/completions", method="post", **kwargs):
42
+ """
43
+ Serves the input using the specified endpoint and method.
44
+
45
+ Args:
46
+ input_: The input text to be processed.
47
+ endpoint: The API endpoint to use for processing.
48
+ method: The HTTP method to use for the request.
49
+ **kwargs: Additional keyword arguments to pass to the payload creation.
50
+
51
+ Returns:
52
+ A tuple containing the payload and the completion response from the API.
53
+
54
+ Raises:
55
+ ValueError: If the specified endpoint is not supported.
56
+
57
+ Examples:
58
+ >>> service = OpenAIService(api_key="your_api_key")
59
+ >>> asyncio.run(service.serve("Hello, world!", "chat/completions"))
60
+ (payload, completion)
61
+
62
+ >>> service = OpenAIService()
63
+ >>> asyncio.run(service.serve("Convert this text to speech.", "audio_speech"))
64
+ ValueError: 'audio_speech' is currently not supported
65
+ """
66
+ if endpoint not in self.active_endpoint:
67
+ await self. init_endpoint(endpoint)
68
+ if endpoint == "chat/completions":
69
+ return await self.serve_chat(input_, **kwargs)
70
+ else:
71
+ return ValueError(f'{endpoint} is currently not supported')
72
+
73
+ async def serve_chat(self, messages, **kwargs):
74
+ """
75
+ Serves the chat completion request with the given messages.
76
+
77
+ Args:
78
+ messages: The messages to be included in the chat completion.
79
+ **kwargs: Additional keyword arguments for payload creation.
80
+
81
+ Returns:
82
+ A tuple containing the payload and the completion response from the API.
83
+
84
+ Raises:
85
+ Exception: If the API call fails.
86
+ """
87
+ if "chat/completions" not in self.active_endpoint:
88
+ await self. init_endpoint("chat/completions")
89
+ self.active_endpoint.append("chat/completions")
90
+ payload = PayloadCreation.chat_completion(
91
+ messages, self.endpoints["chat/completions"].config, self.schema["chat/completions"], **kwargs)
92
+
93
+ try:
94
+ completion = await self.call_api(payload, "chat/completions", "post")
95
+ return payload, completion
96
+ except Exception as e:
97
+ self.status_tracker.num_tasks_failed += 1
98
+ raise e
@@ -0,0 +1,44 @@
1
+ from os import getenv
2
+ from ..configs.openrouter_configs import openrouter_schema
3
+ from .base_service import BaseService, PayloadCreation
4
+
5
+ class OpenRouterService(BaseService):
6
+ base_url = "https://openrouter.ai/api/v1/"
7
+ available_endpoints = ['chat/completions']
8
+ schema = openrouter_schema
9
+ key_scheme = "OPENROUTER_API_KEY"
10
+ token_encoding_name = "cl100k_base"
11
+
12
+
13
+ def __init__(self, api_key = None, key_scheme = None,schema = None, token_encoding_name: str = "cl100k_base", **kwargs):
14
+ key_scheme = key_scheme or self.key_scheme
15
+ super().__init__(
16
+ api_key = api_key or getenv(key_scheme),
17
+ schema = schema or self.schema,
18
+ token_encoding_name=token_encoding_name, **kwargs
19
+ )
20
+ self.active_endpoint = []
21
+
22
+ async def serve(self, input_, endpoint="chat/completions", method="post", **kwargs):
23
+ if endpoint not in self.active_endpoint:
24
+ await self. init_endpoint(endpoint)
25
+ if endpoint == "chat/completions":
26
+ return await self.serve_chat(input_, **kwargs)
27
+ else:
28
+ return ValueError(f'{endpoint} is currently not supported')
29
+
30
+ async def serve_chat(self, messages, **kwargs):
31
+ endpoint = "chat/completions"
32
+
33
+ if endpoint not in self.active_endpoint:
34
+ await self. init_endpoint(endpoint)
35
+ self.active_endpoint.append(endpoint)
36
+ payload = PayloadCreation.chat_completion(
37
+ messages, self.endpoints[endpoint].config, self.schema[endpoint], **kwargs)
38
+
39
+ try:
40
+ completion = await self.call_api(payload, endpoint, "post")
41
+ return payload, completion
42
+ except Exception as e:
43
+ self.status_tracker.num_tasks_failed += 1
44
+ raise e