forked from aayushrautela/EU-Trip-Gen
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi_handler.py
More file actions
126 lines (104 loc) · 4.69 KB
/
Copy pathapi_handler.py
File metadata and controls
126 lines (104 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# api_handler.py
from openai import OpenAI, RateLimitError
import sys
import time
# --- Corrected Wrapper Classes ---
# This class represents the '.completions' level
class _CompletionsWrapper:
def __init__(self, owner):
"""
Initializes the wrapper for the 'completions' object.
Args:
owner: The instance of the main RotatingClient.
"""
self._owner = owner
def create(self, **kwargs):
"""
Forwards the 'create' call to the owner's execution method.
This is the final step in the chain: client.chat.completions.create()
"""
return self._owner._execute_completion_with_rotation(**kwargs)
# This class represents the '.chat' level
class _ChatWrapper:
def __init__(self, owner):
"""
Initializes the wrapper for the 'chat' object. It holds the
'completions' object.
Args:
owner: The instance of the main RotatingClient.
"""
self.completions = _CompletionsWrapper(owner)
class RotatingClient:
"""
A wrapper for the OpenAI client that handles multiple API keys and rotates
them automatically when a rate limit error is encountered.
This version correctly mimics the 'client.chat.completions.create()' structure.
"""
def __init__(self, config):
"""
Initializes the rotating client from the configuration.
"""
provider = config['api_settings'].get('provider')
api_key_name = f'{provider}_key'
if provider == 'openrouter':
self.base_url = "https://openrouter.ai/api/v1"
print("--- Initializing RotatingClient for OpenRouter ---")
elif provider == 'deepseek':
self.base_url = "https://api.deepseek.com/v1"
print("--- Initializing RotatingClient for DeepSeek ---")
else:
print(f"FATAL ERROR: Unknown API provider '{provider}' in config.json. Please use 'openrouter' or 'deepseek'.")
sys.exit(1)
key_string = config['api_settings']['keys'].get(api_key_name)
if not key_string or "YOUR_API_KEY_HERE" in key_string:
print(f"FATAL ERROR: API key(s) for '{provider}' are missing or not set in config.json under '{api_key_name}'.")
print("Please provide keys as a comma-separated string: \"key1,key2,key3\"")
sys.exit(1)
self.keys = [key.strip() for key in key_string.split(',')]
self.current_key_index = 0
# This now correctly creates the client.chat.completions structure
self.chat = _ChatWrapper(self)
print(f"--- Loaded {len(self.keys)} API key(s). ---")
def _get_current_client(self):
"""
Initializes an OpenAI client with the current key.
"""
key = self.keys[self.current_key_index]
return OpenAI(base_url=self.base_url, api_key=key)
def _rotate_key(self):
"""
Moves to the next key in the list.
"""
self.current_key_index = (self.current_key_index + 1) % len(self.keys)
print(f" - Rotated to key #{self.current_key_index + 1}")
def _execute_completion_with_rotation(self, **kwargs):
"""
Executes the API call, attempting with each key until one succeeds
or all have been rate-limited.
"""
start_index = self.current_key_index
for i in range(len(self.keys)):
client = self._get_current_client()
try:
print(f" - Attempting API call with key #{self.current_key_index + 1}...")
response = client.chat.completions.create(**kwargs)
return response
except RateLimitError as e:
print(f" - ❌ Key #{self.current_key_index + 1} is rate-limited.")
self._rotate_key()
if self.current_key_index == start_index:
print(" - ❌ All available API keys are currently rate-limited. Stopping attempt.")
raise e
except Exception as e:
print(f" - ❌ An unexpected API error occurred with key #{self.current_key_index + 1}: {e}")
self._rotate_key()
if self.current_key_index == start_index:
print(" - ❌ All available API keys failed with errors. Stopping attempt.")
raise e
raise Exception("Failed to get a response from the API after trying all available keys.")
def initialize_client(config):
"""
This function acts as a factory, returning an instance of our new
RotatingClient, which will be used throughout the application.
"""
return RotatingClient(config)