Upload 103 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- __pycache__/init_pool.cpython-311.pyc +0 -0
- __pycache__/init_pool.cpython-312.pyc +0 -0
- api/__pycache__/telemetry.cpython-311.pyc +0 -0
- api/__pycache__/telemetry.cpython-312.pyc +0 -0
- api/chat/__init__.py +4 -0
- api/chat/__pycache__/__init__.cpython-311.pyc +0 -0
- api/chat/__pycache__/__init__.cpython-312.pyc +0 -0
- api/chat/__pycache__/chat_api.cpython-311.pyc +0 -0
- api/chat/__pycache__/chat_api.cpython-312.pyc +0 -0
- api/chat/__pycache__/chat_config.cpython-311.pyc +0 -0
- api/chat/__pycache__/chat_config.cpython-312.pyc +0 -0
- api/chat/chat_api.py +276 -0
- api/chat/chat_config.py +21 -0
- api/telemetry.py +92 -0
- auth/__pycache__/jwt_handler.cpython-311.pyc +0 -0
- auth/__pycache__/jwt_handler.cpython-312.pyc +0 -0
- auth/__pycache__/login.cpython-312.pyc +0 -0
- auth/__pycache__/register.cpython-312.pyc +0 -0
- auth/jwt_handler.py +41 -0
- auth/login.py +27 -0
- auth/register.py +33 -0
- boot.sh +17 -0
- config/__pycache__/api_keys.cpython-311.pyc +0 -0
- config/__pycache__/api_keys.cpython-312.pyc +0 -0
- config/__pycache__/constants.cpython-311.pyc +0 -0
- config/__pycache__/constants.cpython-312.pyc +0 -0
- config/__pycache__/models.cpython-311.pyc +0 -0
- config/__pycache__/models.cpython-312.pyc +0 -0
- config/api_keys.py +62 -0
- config/api_keys.xml +2 -0
- config/constants.py +96 -0
- config/models.py +93 -0
- docker.txt +9 -0
- gen_account.py +232 -0
- init_pool.py +21 -0
- main.py +119 -0
- managers/__pycache__/chat_manager.cpython-311.pyc +0 -0
- managers/__pycache__/chat_manager.cpython-312.pyc +0 -0
- managers/__pycache__/global_chat_manager.cpython-311.pyc +0 -0
- managers/__pycache__/global_chat_manager.cpython-312.pyc +0 -0
- managers/chat_manager.py +32 -0
- managers/global_chat_manager.py +106 -0
- managers/queue/__pycache__/base.cpython-311.pyc +0 -0
- managers/queue/__pycache__/base.cpython-312.pyc +0 -0
- managers/queue/__pycache__/deque_queue.cpython-311.pyc +0 -0
- managers/queue/__pycache__/deque_queue.cpython-312.pyc +0 -0
- managers/queue/__pycache__/rabbitmq_queue.cpython-312.pyc +0 -0
- managers/queue/__pycache__/redis_queue.cpython-312.pyc +0 -0
- managers/queue/base.py +16 -0
- managers/queue/deque_queue.py +21 -0
__pycache__/init_pool.cpython-311.pyc
ADDED
|
Binary file (972 Bytes). View file
|
|
|
__pycache__/init_pool.cpython-312.pyc
ADDED
|
Binary file (830 Bytes). View file
|
|
|
api/__pycache__/telemetry.cpython-311.pyc
ADDED
|
Binary file (5.04 kB). View file
|
|
|
api/__pycache__/telemetry.cpython-312.pyc
ADDED
|
Binary file (4.76 kB). View file
|
|
|
api/chat/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .chat_config import ChatConfig
|
| 2 |
+
from .chat_api import ChatAPI
|
| 3 |
+
|
| 4 |
+
__all__ = ['ChatConfig', 'ChatAPI']
|
api/chat/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (319 Bytes). View file
|
|
|
api/chat/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (283 Bytes). View file
|
|
|
api/chat/__pycache__/chat_api.cpython-311.pyc
ADDED
|
Binary file (14 kB). View file
|
|
|
api/chat/__pycache__/chat_api.cpython-312.pyc
ADDED
|
Binary file (13.3 kB). View file
|
|
|
api/chat/__pycache__/chat_config.cpython-311.pyc
ADDED
|
Binary file (1.1 kB). View file
|
|
|
api/chat/__pycache__/chat_config.cpython-312.pyc
ADDED
|
Binary file (982 Bytes). View file
|
|
|
api/chat/chat_api.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import binascii
|
| 2 |
+
import json
|
| 3 |
+
from typing import List, Optional, AsyncGenerator
|
| 4 |
+
from time import time
|
| 5 |
+
|
| 6 |
+
# Remove aioredis import
|
| 7 |
+
# from aioredis import Redis
|
| 8 |
+
|
| 9 |
+
# import tiktoken
|
| 10 |
+
from api.chat import ChatConfig
|
| 11 |
+
from api.telemetry import TelemetryAPI
|
| 12 |
+
from auth.jwt_handler import JWTHandler
|
| 13 |
+
from config.constants import ENCRYPTION_KEY
|
| 14 |
+
from utils.encrypt import encrypt
|
| 15 |
+
from utils.http import HTTPClient
|
| 16 |
+
from config.constants import (
|
| 17 |
+
APP_LANGUAGE,
|
| 18 |
+
APP_NAME,
|
| 19 |
+
APP_VERSION,
|
| 20 |
+
DISPLAY_NAME,
|
| 21 |
+
HADWARE_INFO,
|
| 22 |
+
INFERENCE_URL,
|
| 23 |
+
SYSTEM_INFO,
|
| 24 |
+
)
|
| 25 |
+
from utils.compression import decompress_chunks
|
| 26 |
+
|
| 27 |
+
from protos import request_pb2, response_pb2
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class ChatAPI:
|
| 31 |
+
def __init__(self, api_key: str, http_client: HTTPClient = HTTPClient()):
|
| 32 |
+
self.api_key = api_key
|
| 33 |
+
self.jwt_token = None
|
| 34 |
+
self.jwt_token_timestamp = 0
|
| 35 |
+
self.http_client = http_client
|
| 36 |
+
|
| 37 |
+
async def renew_jwt_token(self):
|
| 38 |
+
"""Renew JWT token asynchronously if it's expired or missing"""
|
| 39 |
+
current_time = time()
|
| 40 |
+
|
| 41 |
+
# Check if token is still valid (within 2500 seconds)
|
| 42 |
+
if self.jwt_token and current_time - self.jwt_token_timestamp < 2500:
|
| 43 |
+
return
|
| 44 |
+
|
| 45 |
+
jwt_handler = JWTHandler(api_key=self.api_key, http_client=self.http_client)
|
| 46 |
+
jwt_token = await jwt_handler.get_jwt_token()
|
| 47 |
+
tele = TelemetryAPI(api_key=self.api_key)
|
| 48 |
+
await tele.do_telemetry()
|
| 49 |
+
|
| 50 |
+
if jwt_token:
|
| 51 |
+
self.jwt_token = jwt_token
|
| 52 |
+
self.jwt_token_timestamp = current_time
|
| 53 |
+
|
| 54 |
+
async def _create_chat_request(
|
| 55 |
+
self,
|
| 56 |
+
messages: List[dict],
|
| 57 |
+
config: ChatConfig,
|
| 58 |
+
system_prompt: str = "You are a helpful assistant.",
|
| 59 |
+
) -> request_pb2.ChatRequestMessage:
|
| 60 |
+
try:
|
| 61 |
+
await self.renew_jwt_token()
|
| 62 |
+
except Exception as e:
|
| 63 |
+
print(e)
|
| 64 |
+
...
|
| 65 |
+
|
| 66 |
+
msg = request_pb2.ChatRequestMessage()
|
| 67 |
+
|
| 68 |
+
# Set client info
|
| 69 |
+
self._set_client_info(msg)
|
| 70 |
+
|
| 71 |
+
# Set system prompt and model config
|
| 72 |
+
msg.system_prompt = system_prompt
|
| 73 |
+
msg.model_id = config.model_id.value
|
| 74 |
+
msg.idk13.idk13nn = 1
|
| 75 |
+
msg.idk_id = 5
|
| 76 |
+
self._set_model_config(msg, config)
|
| 77 |
+
|
| 78 |
+
# Set tool config
|
| 79 |
+
# self._set_tool_config(msg)
|
| 80 |
+
|
| 81 |
+
# Convert messages
|
| 82 |
+
self._add_messages(msg, messages)
|
| 83 |
+
|
| 84 |
+
return msg
|
| 85 |
+
|
| 86 |
+
def _set_client_info(self, msg: request_pb2.ChatRequestMessage) -> None:
|
| 87 |
+
"""Set client information in the request message"""
|
| 88 |
+
msg.client_info.api_key = self.api_key
|
| 89 |
+
msg.client_info.user_jwt = self.jwt_token
|
| 90 |
+
msg.client_info.locale = APP_LANGUAGE
|
| 91 |
+
msg.client_info.extension_name = APP_NAME
|
| 92 |
+
msg.client_info.ide_name = APP_NAME
|
| 93 |
+
msg.client_info.extension_version = APP_VERSION
|
| 94 |
+
msg.client_info.os = SYSTEM_INFO
|
| 95 |
+
msg.client_info.ide_version = DISPLAY_NAME
|
| 96 |
+
msg.client_info.hardware = HADWARE_INFO
|
| 97 |
+
|
| 98 |
+
def _set_model_config(
|
| 99 |
+
self, msg: request_pb2.ChatRequestMessage, config: ChatConfig
|
| 100 |
+
) -> None:
|
| 101 |
+
"""Set model configuration"""
|
| 102 |
+
msg.model_config.parallel_stream = 1
|
| 103 |
+
msg.model_config.max_tokens = config.max_tokens
|
| 104 |
+
msg.model_config.temperature = config.temperature
|
| 105 |
+
msg.model_config.top_k = config.top_k
|
| 106 |
+
msg.model_config.top_P = config.top_p
|
| 107 |
+
|
| 108 |
+
def _set_special_tokens(self, msg: request_pb2.ChatRequestMessage) -> None:
|
| 109 |
+
msg.model_config.special_tokens.extend(
|
| 110 |
+
[
|
| 111 |
+
"<|user|>",
|
| 112 |
+
"<|bot|>",
|
| 113 |
+
"<|context_request|>",
|
| 114 |
+
"<|endoftext|>",
|
| 115 |
+
"<|end_of_turn|>",
|
| 116 |
+
]
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
def _set_tool_config(self, msg: request_pb2.ChatRequestMessage) -> None:
|
| 120 |
+
"""Set tool configuration"""
|
| 121 |
+
msg.tool_use.mode = "auto"
|
| 122 |
+
msg.tool_config.tool_name = "do_not_call"
|
| 123 |
+
msg.tool_config.description = "Do not call this tool."
|
| 124 |
+
msg.tool_config.schema = '{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"additionalProperties":false,"type":"object"}'
|
| 125 |
+
|
| 126 |
+
def _add_messages(
|
| 127 |
+
self, msg: request_pb2.ChatRequestMessage, messages: List[dict]
|
| 128 |
+
) -> None:
|
| 129 |
+
"""Add chat messages to the request"""
|
| 130 |
+
role_map = {"user": 1, "assistant": 2, "system": 3}
|
| 131 |
+
|
| 132 |
+
for chat_msg in messages:
|
| 133 |
+
role = role_map.get(chat_msg["role"], 1)
|
| 134 |
+
content = chat_msg["content"]
|
| 135 |
+
|
| 136 |
+
# Override system prompt
|
| 137 |
+
if role == 3:
|
| 138 |
+
if isinstance(content, str):
|
| 139 |
+
msg.system_prompt = content
|
| 140 |
+
|
| 141 |
+
elif isinstance(content, list):
|
| 142 |
+
for item in content:
|
| 143 |
+
if item.get("type", "") == "text" and "text" in item and isinstance(item["text"], str):
|
| 144 |
+
msg.system_prompt = item["text"]
|
| 145 |
+
break
|
| 146 |
+
continue
|
| 147 |
+
|
| 148 |
+
if isinstance(content, list):
|
| 149 |
+
pb_msg = self._create_multipart_message(role, content)
|
| 150 |
+
else:
|
| 151 |
+
pb_msg = self._create_text_message(role, content)
|
| 152 |
+
|
| 153 |
+
msg.chat_messages.append(pb_msg)
|
| 154 |
+
|
| 155 |
+
def _create_multipart_message(
|
| 156 |
+
self, role: int, content: List[dict]
|
| 157 |
+
) -> request_pb2.ChatMessage:
|
| 158 |
+
"""Create a message with multiple parts (text and images)"""
|
| 159 |
+
text_parts = []
|
| 160 |
+
image_parts = []
|
| 161 |
+
|
| 162 |
+
for item in content:
|
| 163 |
+
if item["type"] == "text":
|
| 164 |
+
text_parts.append(item["text"])
|
| 165 |
+
elif item["type"] == "image_url":
|
| 166 |
+
image_url = item["image_url"]["url"]
|
| 167 |
+
if image_url.startswith("data:image/") and "base64," in image_url:
|
| 168 |
+
prefix, image_data = image_url.split("base64,", 1)
|
| 169 |
+
mime_type = prefix.split("data:")[1].split(";")[0]
|
| 170 |
+
image_parts.append(
|
| 171 |
+
request_pb2.ImagePart(
|
| 172 |
+
image_data=image_data, image_mime_type=mime_type
|
| 173 |
+
)
|
| 174 |
+
)
|
| 175 |
+
|
| 176 |
+
return self._create_message(role, " ".join(text_parts), image_parts)
|
| 177 |
+
|
| 178 |
+
def _create_text_message(self, role: int, content: str) -> request_pb2.ChatMessage:
|
| 179 |
+
"""Create a simple text message"""
|
| 180 |
+
return self._create_message(role, content)
|
| 181 |
+
|
| 182 |
+
def _create_message(
|
| 183 |
+
self, role: int, content: str, image_parts: List[request_pb2.ImagePart] = None
|
| 184 |
+
) -> request_pb2.ChatMessage:
|
| 185 |
+
"""Create a chat message with common attributes"""
|
| 186 |
+
pb_msg = request_pb2.ChatMessage(
|
| 187 |
+
role=role, content=content
|
| 188 |
+
)
|
| 189 |
+
if role == 1: pb_msg.idk2 = 1
|
| 190 |
+
# pb_msg.cache_control.prompt_caching = 1
|
| 191 |
+
|
| 192 |
+
if image_parts:
|
| 193 |
+
pb_msg.image_parts.extend(image_parts)
|
| 194 |
+
return pb_msg
|
| 195 |
+
|
| 196 |
+
async def _process_chat_response(self, type: int, data: bytes) -> tuple[str, int]:
|
| 197 |
+
"""Process a single chat response chunk and return (message, count)"""
|
| 198 |
+
if type == 3: # end of message
|
| 199 |
+
try:
|
| 200 |
+
response = json.loads(data)
|
| 201 |
+
return (encrypt(str(response), ENCRYPTION_KEY), 0) if response else ("", 0)
|
| 202 |
+
except Exception as e:
|
| 203 |
+
raise e
|
| 204 |
+
|
| 205 |
+
try:
|
| 206 |
+
search_response = response_pb2.ChatResponse()
|
| 207 |
+
search_response.ParseFromString(data)
|
| 208 |
+
return (search_response.message, search_response.count) if search_response.message else ("", 0)
|
| 209 |
+
except:
|
| 210 |
+
return ("", 0)
|
| 211 |
+
|
| 212 |
+
async def _handle_stream_response(self, chunk_iterator) -> AsyncGenerator[tuple[str, int], None]:
|
| 213 |
+
"""Handle streaming response chunks"""
|
| 214 |
+
async for chunk in chunk_iterator:
|
| 215 |
+
for type, data in decompress_chunks(chunk):
|
| 216 |
+
result = await self._process_chat_response(type, data)
|
| 217 |
+
yield result
|
| 218 |
+
|
| 219 |
+
async def _handle_response(self, chunk) -> AsyncGenerator[tuple[str, int], None]:
|
| 220 |
+
"""Handle non-streaming response chunks"""
|
| 221 |
+
for type, data in decompress_chunks(chunk):
|
| 222 |
+
result = await self._process_chat_response(type, data)
|
| 223 |
+
yield result
|
| 224 |
+
|
| 225 |
+
async def send_message(
|
| 226 |
+
self,
|
| 227 |
+
messages: List[dict],
|
| 228 |
+
config: Optional[ChatConfig] = None,
|
| 229 |
+
system_prompt: str = "You are a helpful assistant.",
|
| 230 |
+
stream: bool = False,
|
| 231 |
+
) -> AsyncGenerator[tuple[str, int], None]:
|
| 232 |
+
"""Send chat messages and yield response chunks"""
|
| 233 |
+
if config is None:
|
| 234 |
+
config = ChatConfig()
|
| 235 |
+
|
| 236 |
+
request = await self._create_chat_request(messages, config, system_prompt)
|
| 237 |
+
|
| 238 |
+
headers = {
|
| 239 |
+
"User-Agent": "connect-go/1.16.2 (go1.23.2 X:nocoverageredesign)",
|
| 240 |
+
"Connect-Accept-Encoding": "gzip",
|
| 241 |
+
"Connect-Content-Encoding": "gzip",
|
| 242 |
+
"Connect-Protocol-Version": "1",
|
| 243 |
+
"Content-Type": "application/connect+proto",
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
url = f"{INFERENCE_URL}/exa.api_server_pb.ApiServerService/GetChatMessage"
|
| 247 |
+
request_data = request.SerializeToString()
|
| 248 |
+
|
| 249 |
+
if stream:
|
| 250 |
+
stream_iterator = self.http_client.stream_post(
|
| 251 |
+
url=url,
|
| 252 |
+
data=request_data,
|
| 253 |
+
headers=headers,
|
| 254 |
+
compress=True,
|
| 255 |
+
)
|
| 256 |
+
async for result in self._handle_stream_response(stream_iterator):
|
| 257 |
+
yield result
|
| 258 |
+
else:
|
| 259 |
+
response = await self.http_client.post(
|
| 260 |
+
url=url,
|
| 261 |
+
data=request_data,
|
| 262 |
+
headers=headers,
|
| 263 |
+
compress=True,
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
if response.status_code != 200:
|
| 267 |
+
raise Exception(f"Chat request failed: {response.status_code}")
|
| 268 |
+
|
| 269 |
+
if response.headers.get("connect-content-encoding") == "gzip":
|
| 270 |
+
async for result in self._handle_response(response.content):
|
| 271 |
+
yield result
|
| 272 |
+
else:
|
| 273 |
+
search_response = response_pb2.ChatResponse()
|
| 274 |
+
search_response.ParseFromString(response.content)
|
| 275 |
+
if search_response.message:
|
| 276 |
+
yield (search_response.message, search_response.count)
|
api/chat/chat_config.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from dataclasses import dataclass
|
| 3 |
+
|
| 4 |
+
from config.models import ModelID
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@dataclass
|
| 8 |
+
class ChatConfig:
|
| 9 |
+
def __init__(
|
| 10 |
+
self,
|
| 11 |
+
model_id: ModelID = ModelID.MODEL_CLAUDE_3_5_HAIKU_20241022,
|
| 12 |
+
temperature: float = 0.7,
|
| 13 |
+
max_tokens: int = 4096,
|
| 14 |
+
top_p: float = 1.0,
|
| 15 |
+
top_k: int = 50
|
| 16 |
+
):
|
| 17 |
+
self.model_id = model_id
|
| 18 |
+
self.temperature = temperature
|
| 19 |
+
self.max_tokens = max_tokens
|
| 20 |
+
self.top_p = top_p
|
| 21 |
+
self.top_k = top_k
|
api/telemetry.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import httpx
|
| 3 |
+
import uuid
|
| 4 |
+
from config.constants import (
|
| 5 |
+
APP_LANGUAGE,
|
| 6 |
+
BASE_URL,
|
| 7 |
+
APP_NAME,
|
| 8 |
+
APP_VERSION,
|
| 9 |
+
DISPLAY_NAME,
|
| 10 |
+
HADWARE_INFO,
|
| 11 |
+
SYSTEM_INFO,
|
| 12 |
+
)
|
| 13 |
+
from protos import telemetry_pb2
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class TelemetryAPI:
|
| 17 |
+
def __init__(self, api_key: str):
|
| 18 |
+
self.api_key = api_key
|
| 19 |
+
self.client = httpx.AsyncClient(verify=False, timeout=3)
|
| 20 |
+
|
| 21 |
+
async def do_telemetry(self):
|
| 22 |
+
await self.send_telemetry()
|
| 23 |
+
await self.send_ping()
|
| 24 |
+
await self.send_unleash_request()
|
| 25 |
+
|
| 26 |
+
async def send_telemetry(self):
|
| 27 |
+
telemetry = telemetry_pb2.TelemetryData()
|
| 28 |
+
|
| 29 |
+
# System info
|
| 30 |
+
system_info = telemetry.system_info
|
| 31 |
+
system_info.api_key = self.api_key
|
| 32 |
+
system_info.locale = APP_LANGUAGE
|
| 33 |
+
system_info.extension_name = APP_NAME
|
| 34 |
+
system_info.ide_name = APP_NAME
|
| 35 |
+
system_info.extension_version = APP_VERSION
|
| 36 |
+
system_info.os = SYSTEM_INFO
|
| 37 |
+
system_info.ide_version = DISPLAY_NAME
|
| 38 |
+
system_info.hardware = HADWARE_INFO
|
| 39 |
+
|
| 40 |
+
system_info.session_id = str(uuid.uuid4())
|
| 41 |
+
|
| 42 |
+
response = await self.client.post(
|
| 43 |
+
url=f"{BASE_URL}/exa.api_server_pb.ApiServerService/RecordAsyncTelemetry",
|
| 44 |
+
headers={
|
| 45 |
+
"Content-Type": "application/proto",
|
| 46 |
+
},
|
| 47 |
+
content=telemetry.SerializeToString(),
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
return response.status_code == 200
|
| 51 |
+
|
| 52 |
+
async def send_ping(self):
|
| 53 |
+
url = f"{BASE_URL}/exa.api_server_pb.ApiServerService/Ping"
|
| 54 |
+
|
| 55 |
+
headers = {
|
| 56 |
+
'User-Agent': 'connect-go/1.16.2 (go1.23.2 X:nocoverageredesign)',
|
| 57 |
+
'Accept-Encoding': 'gzip',
|
| 58 |
+
'Connect-Protocol-Version': '1',
|
| 59 |
+
'Content-Type': 'application/proto'
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
try:
|
| 63 |
+
response = await self.client.post(url, headers=headers)
|
| 64 |
+
print(f"Ping Status Code: {response.status_code}")
|
| 65 |
+
except httpx.RequestError as e:
|
| 66 |
+
print(f"请求发生错误: {e}")
|
| 67 |
+
|
| 68 |
+
async def send_unleash_request(self):
|
| 69 |
+
url = f"{BASE_URL}/exa.api_server_pb.ApiServerService/GetUnleashContextFields"
|
| 70 |
+
|
| 71 |
+
headers = {
|
| 72 |
+
'User-Agent': 'connect-go/1.16.2 (go1.23.2 X:nocoverageredesign)',
|
| 73 |
+
'Accept-Encoding': 'gzip',
|
| 74 |
+
'Connect-Protocol-Version': '1',
|
| 75 |
+
'Content-Type': 'application/proto'
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
try:
|
| 79 |
+
response = await self.client.post(url, headers=headers)
|
| 80 |
+
|
| 81 |
+
print(f"GetUnleashContextFields Status Code: {response.status_code}")
|
| 82 |
+
print(f"地区:")
|
| 83 |
+
print(f"{response.text}")
|
| 84 |
+
|
| 85 |
+
except httpx.RequestError as e:
|
| 86 |
+
print(f"请求发生错误: {e}")
|
| 87 |
+
|
| 88 |
+
async def __aenter__(self):
|
| 89 |
+
return self
|
| 90 |
+
|
| 91 |
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
| 92 |
+
await self.client.aclose()
|
auth/__pycache__/jwt_handler.cpython-311.pyc
ADDED
|
Binary file (2.49 kB). View file
|
|
|
auth/__pycache__/jwt_handler.cpython-312.pyc
ADDED
|
Binary file (2.33 kB). View file
|
|
|
auth/__pycache__/login.cpython-312.pyc
ADDED
|
Binary file (1.03 kB). View file
|
|
|
auth/__pycache__/register.cpython-312.pyc
ADDED
|
Binary file (1.58 kB). View file
|
|
|
auth/jwt_handler.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from utils.http import HTTPClient
|
| 2 |
+
from config.constants import APP_LANGUAGE, BASE_URL, APP_NAME, APP_VERSION, DISPLAY_NAME
|
| 3 |
+
from protos import jwt_pb2
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class JWTHandler:
|
| 7 |
+
def __init__(self, api_key: str, server_url: str = BASE_URL, http_client: HTTPClient = HTTPClient()):
|
| 8 |
+
self.server_url = server_url
|
| 9 |
+
self.api_key = api_key
|
| 10 |
+
self.jwt_token = None
|
| 11 |
+
self.http_client = http_client
|
| 12 |
+
|
| 13 |
+
async def get_jwt_token(self) -> str:
|
| 14 |
+
"""Get JWT token from server"""
|
| 15 |
+
request = jwt_pb2.jwt_request(
|
| 16 |
+
surfwind_jwt_request=jwt_pb2.surfwind_jwt_request(
|
| 17 |
+
app_name=APP_NAME,
|
| 18 |
+
version=APP_VERSION,
|
| 19 |
+
api_key=self.api_key,
|
| 20 |
+
language=APP_LANGUAGE,
|
| 21 |
+
display_name=DISPLAY_NAME,
|
| 22 |
+
app_identifier=APP_NAME,
|
| 23 |
+
)
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
response = await self.http_client.post(
|
| 27 |
+
url=f"{self.server_url}/exa.auth_pb.AuthService/GetUserJwt",
|
| 28 |
+
headers={
|
| 29 |
+
"Content-Type": "application/proto",
|
| 30 |
+
},
|
| 31 |
+
data=request.SerializeToString(),
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
if response.status_code == 200:
|
| 35 |
+
jwt_response = jwt_pb2.jwt_response()
|
| 36 |
+
jwt_response.ParseFromString(response.content)
|
| 37 |
+
self.jwt_token = jwt_response.jwt_token
|
| 38 |
+
return self.jwt_token
|
| 39 |
+
|
| 40 |
+
print(response.content)
|
| 41 |
+
raise Exception(f"Failed to get JWT token: {response.status_code}")
|
auth/login.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
+
import webbrowser
|
| 3 |
+
from config.constants import WINDSURF_ID
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def open_auth_url() -> str:
|
| 7 |
+
"""Open authentication URL in browser"""
|
| 8 |
+
state = str(uuid.uuid4())
|
| 9 |
+
base_url = "https://www.codeium.com/windsurf/signin"
|
| 10 |
+
|
| 11 |
+
auth_url = (
|
| 12 |
+
f"{base_url}?"
|
| 13 |
+
f"response_type=token&"
|
| 14 |
+
f"client_id={WINDSURF_ID}&"
|
| 15 |
+
f"redirect_uri=show-auth-token&"
|
| 16 |
+
f"state={state}&"
|
| 17 |
+
f"prompt=login&"
|
| 18 |
+
f"redirect_parameters_type=query&"
|
| 19 |
+
f"workflow="
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
try:
|
| 23 |
+
webbrowser.open(auth_url)
|
| 24 |
+
return state
|
| 25 |
+
except Exception as e:
|
| 26 |
+
print(f"Failed to open browser: {e}")
|
| 27 |
+
return None
|
auth/register.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from utils.http import HTTPClient
|
| 2 |
+
from config.constants import API_URL
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class Registration:
|
| 6 |
+
@staticmethod
|
| 7 |
+
async def register_user(firebase_token: str) -> str:
|
| 8 |
+
"""
|
| 9 |
+
Register user with firebase token and return API key
|
| 10 |
+
"""
|
| 11 |
+
headers = {
|
| 12 |
+
"User-Agent": "Go-http-client/1.1",
|
| 13 |
+
"Content-Type": "application/json",
|
| 14 |
+
"Accept-Encoding": "gzip",
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
data = {"firebase_id_token": firebase_token}
|
| 18 |
+
|
| 19 |
+
response = await HTTPClient().post(
|
| 20 |
+
f"{API_URL}/register_user/", headers=headers, default_headers=False, json=data, verify=False
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
if response.status_code != 200:
|
| 24 |
+
print(response.text)
|
| 25 |
+
raise Exception(f"Registration failed: {response.status_code}")
|
| 26 |
+
|
| 27 |
+
response_json = response.json()
|
| 28 |
+
api_key = response_json.get("api_key")
|
| 29 |
+
|
| 30 |
+
if not api_key:
|
| 31 |
+
raise Exception("No API key in response")
|
| 32 |
+
|
| 33 |
+
return api_key
|
boot.sh
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# start.sh
|
| 4 |
+
|
| 5 |
+
# 初始化聊天队列
|
| 6 |
+
# pypy/bin/python init_pool.py
|
| 7 |
+
|
| 8 |
+
# 启动 Gunicorn 服务器
|
| 9 |
+
|
| 10 |
+
pypy/bin/gunicorn service.server:app \
|
| 11 |
+
--worker-class uvicorn.workers.UvicornWorker \
|
| 12 |
+
--workers 16 \
|
| 13 |
+
--bind 0.0.0.0:8000 \
|
| 14 |
+
--timeout 30 \
|
| 15 |
+
--keep-alive 5 \
|
| 16 |
+
--access-logfile - \
|
| 17 |
+
--error-logfile -
|
config/__pycache__/api_keys.cpython-311.pyc
ADDED
|
Binary file (4.61 kB). View file
|
|
|
config/__pycache__/api_keys.cpython-312.pyc
ADDED
|
Binary file (4.07 kB). View file
|
|
|
config/__pycache__/constants.cpython-311.pyc
ADDED
|
Binary file (3.05 kB). View file
|
|
|
config/__pycache__/constants.cpython-312.pyc
ADDED
|
Binary file (3.1 kB). View file
|
|
|
config/__pycache__/models.cpython-311.pyc
ADDED
|
Binary file (4.04 kB). View file
|
|
|
config/__pycache__/models.cpython-312.pyc
ADDED
|
Binary file (3.95 kB). View file
|
|
|
config/api_keys.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import xml.etree.ElementTree as ET
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from typing import Optional, List
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class APIKeyManager:
|
| 7 |
+
def __init__(self, config_path: str = "config/api_keys.xml"):
|
| 8 |
+
self.config_path = Path(config_path)
|
| 9 |
+
self._api_keys: List[str] = []
|
| 10 |
+
self._load_keys()
|
| 11 |
+
|
| 12 |
+
def _load_keys(self):
|
| 13 |
+
"""Load API keys from XML file"""
|
| 14 |
+
if not self.config_path.exists():
|
| 15 |
+
self._create_default_config()
|
| 16 |
+
|
| 17 |
+
tree = ET.parse(self.config_path)
|
| 18 |
+
root = tree.getroot()
|
| 19 |
+
|
| 20 |
+
self._api_keys = [key.text for key in root.findall("key")]
|
| 21 |
+
|
| 22 |
+
def _create_default_config(self):
|
| 23 |
+
"""Create default XML config file"""
|
| 24 |
+
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
| 25 |
+
|
| 26 |
+
root = ET.Element("api_keys")
|
| 27 |
+
tree = ET.ElementTree(root)
|
| 28 |
+
tree.write(self.config_path, encoding="utf-8", xml_declaration=True)
|
| 29 |
+
|
| 30 |
+
def get_key(self, index: int = 0) -> Optional[str]:
|
| 31 |
+
"""Get API key by index"""
|
| 32 |
+
try:
|
| 33 |
+
return self._api_keys[index]
|
| 34 |
+
except IndexError:
|
| 35 |
+
return None
|
| 36 |
+
|
| 37 |
+
def add_key(self, api_key: str):
|
| 38 |
+
"""Add new API key"""
|
| 39 |
+
root = ET.parse(self.config_path).getroot()
|
| 40 |
+
new_key = ET.SubElement(root, "key")
|
| 41 |
+
new_key.text = api_key
|
| 42 |
+
|
| 43 |
+
tree = ET.ElementTree(root)
|
| 44 |
+
tree.write(self.config_path, encoding="utf-8", xml_declaration=True)
|
| 45 |
+
|
| 46 |
+
self._api_keys.append(api_key)
|
| 47 |
+
|
| 48 |
+
def remove_key(self, index: int):
|
| 49 |
+
"""Remove API key by index"""
|
| 50 |
+
root = ET.parse(self.config_path).getroot()
|
| 51 |
+
keys = root.findall("key")
|
| 52 |
+
|
| 53 |
+
if 0 <= index < len(keys):
|
| 54 |
+
root.remove(keys[index])
|
| 55 |
+
tree = ET.ElementTree(root)
|
| 56 |
+
tree.write(self.config_path, encoding="utf-8", xml_declaration=True)
|
| 57 |
+
|
| 58 |
+
self._api_keys.pop(index)
|
| 59 |
+
|
| 60 |
+
def list_keys(self) -> List[str]:
|
| 61 |
+
"""List all API keys"""
|
| 62 |
+
return self._api_keys.copy()
|
config/api_keys.xml
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version='1.0' encoding='utf-8'?>
|
| 2 |
+
<api_keys><key>33cbbd76-bdb4-4d28-bc65-38c56dca3768</key><key>7e5ab17a-ec08-4037-8b30-186e86d8595a</key><key>637b9327-cfef-4808-bdeb-3d83e8ecca1b</key><key>82651d96-5d2c-4387-886f-70d501865994</key></api_keys>
|
config/constants.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# API endpoints
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
from config.models import ModelID
|
| 5 |
+
|
| 6 |
+
DEFAULT_API_ACCOUNT_EXPIRATION_SECONDS = 4 * 24 * 60 * 60
|
| 7 |
+
|
| 8 |
+
ENCRYPTION_KEY = "1923a821"
|
| 9 |
+
BASE_URL = "https://server.codeium.com"
|
| 10 |
+
API_URL = "https://api.codeium.com"
|
| 11 |
+
INFERENCE_URL = "https://inference.codeium.com"
|
| 12 |
+
|
| 13 |
+
# API keys and identifiers
|
| 14 |
+
WINDSURF_ID = "3GUryQ7ldAeKEuD2obYnppsnmj58eP5u"
|
| 15 |
+
|
| 16 |
+
# Application info
|
| 17 |
+
APP_NAME = "windsurf"
|
| 18 |
+
APP_VERSION = "1.30.0"
|
| 19 |
+
DISPLAY_NAME = "Windsurf 1.94.0"
|
| 20 |
+
APP_LANGUAGE = "en"
|
| 21 |
+
|
| 22 |
+
HADWARE_INFO = json.dumps(
|
| 23 |
+
{
|
| 24 |
+
"NumSockets": 1,
|
| 25 |
+
"NumCores": 8,
|
| 26 |
+
"NumThreads": 16,
|
| 27 |
+
"VendorID": "GenuineIntel",
|
| 28 |
+
"Family": "6",
|
| 29 |
+
"Model": "158",
|
| 30 |
+
"ModelName": "Intel(R) Core(TM) i9-12900K CPU @ 3.60GHz",
|
| 31 |
+
"Memory": 34359738368,
|
| 32 |
+
}
|
| 33 |
+
)
|
| 34 |
+
SYSTEM_INFO = json.dumps(
|
| 35 |
+
{
|
| 36 |
+
"Os": "windows",
|
| 37 |
+
"Arch": "amd64",
|
| 38 |
+
"Version": "11",
|
| 39 |
+
"ProductName": "Windows 11 Pro",
|
| 40 |
+
"MajorVersionNumber": 11,
|
| 41 |
+
"MinorVersionNumber": 0,
|
| 42 |
+
"Build": "26100",
|
| 43 |
+
}
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
# HTTP headers
|
| 47 |
+
DEFAULT_HEADERS = {
|
| 48 |
+
"User-Agent": "connect-go/1.16.2 (go1.23.2 X:nocoverageredesign)",
|
| 49 |
+
"Connect-Protocol-Version": "1",
|
| 50 |
+
"Accept-Encoding": "identity",
|
| 51 |
+
"Connection": "keep-alive",
|
| 52 |
+
"Keep-Alive": "timeout=120, max=10000"
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
REDIS_URL = "redis://localhost:6379"
|
| 56 |
+
REDIS_PASSWORD = "$546a"
|
| 57 |
+
JWT_SECRET_INFO = "0982d83c-969a-4a53-8243-9d215a2fe7a2"
|
| 58 |
+
ALLOWED_HOSTS = ["*"]
|
| 59 |
+
BLOCK_DURATION = 3600
|
| 60 |
+
OPTIONS_DURATION = 600
|
| 61 |
+
THREE_MIN_LIMIT = 90
|
| 62 |
+
HOURLY_LIMIT = 550
|
| 63 |
+
BLOCK_LIMIT = 600
|
| 64 |
+
MAX_UPLOAD_SIZE = 1048576 * 100 # 100 MB
|
| 65 |
+
MAX_REQUEST_SIZE = 1048576 * 8 # 8 MB
|
| 66 |
+
|
| 67 |
+
# Model mappings can also be placed here if applicable
|
| 68 |
+
MODEL_MAPPING = {
|
| 69 |
+
# OpenAI Models
|
| 70 |
+
"gpt-3.5-turbo": ModelID.MODEL_CHAT_3_5_TURBO,
|
| 71 |
+
"gpt-4": ModelID.MODEL_CHAT_GPT_4,
|
| 72 |
+
"gpt-4o": ModelID.MODEL_CHAT_GPT_4O_2024_08_06,
|
| 73 |
+
"gpt-4o-2024-08-06": ModelID.MODEL_CHAT_GPT_4O_2024_08_06,
|
| 74 |
+
"gpt-4o-mini-2024-07-18": ModelID.MODEL_CHAT_GPT_4O_MINI_2024_07_18,
|
| 75 |
+
"gpt-4o-mini": ModelID.MODEL_CHAT_GPT_4O_MINI_2024_07_18,
|
| 76 |
+
"gpt-4-turbo-preview": ModelID.MODEL_CHAT_GPT_4_1106_PREVIEW,
|
| 77 |
+
# Claude Models
|
| 78 |
+
"claude-3-opus-20240229": ModelID.MODEL_CLAUDE_3_OPUS_20240229,
|
| 79 |
+
"claude-3-opus": ModelID.MODEL_CLAUDE_3_OPUS_20240229,
|
| 80 |
+
# "claude-3-sonnet-20240229": ModelID.MODEL_CLAUDE_3_SONNET_20240229,
|
| 81 |
+
"claude-3.5-sonnet-20240620": ModelID.MODEL_CLAUDE_3_5_SONNET_20240620,
|
| 82 |
+
"claude-3.5-sonnet-20241022": ModelID.MODEL_CLAUDE_3_5_SONNET_20241022,
|
| 83 |
+
"claude-3.5-sonnet": ModelID.MODEL_CLAUDE_3_5_SONNET_20241022,
|
| 84 |
+
"claude-3.5-haiku": ModelID.MODEL_CLAUDE_3_5_HAIKU_20241022,
|
| 85 |
+
"claude-3-haiku": ModelID.MODEL_CLAUDE_3_HAIKU_20240307,
|
| 86 |
+
"claude": ModelID.MODEL_CLAUDE_3_5_SONNET_20241022,
|
| 87 |
+
# O1 Models
|
| 88 |
+
"o1-mini": ModelID.MODEL_CHAT_O1_MINI,
|
| 89 |
+
"o1-preview": ModelID.MODEL_CHAT_O1_PREVIEW,
|
| 90 |
+
# "o1": ModelID.MODEL_CHAT_O1,
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
# Authorization keys for API access
|
| 94 |
+
AUTH_KEYS = [
|
| 95 |
+
"sk-upuyyds"
|
| 96 |
+
]
|
config/models.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class ModelID(Enum):
|
| 5 |
+
MODEL_UNSPECIFIED = 0
|
| 6 |
+
MODEL_EMBED_6591 = 20
|
| 7 |
+
MODEL_8341 = 33
|
| 8 |
+
MODEL_8528 = 42
|
| 9 |
+
MODEL_9024 = 41
|
| 10 |
+
MODEL_14602 = 112
|
| 11 |
+
MODEL_15133 = 115
|
| 12 |
+
MODEL_15302 = 119
|
| 13 |
+
MODEL_15335 = 121
|
| 14 |
+
MODEL_15336 = 122
|
| 15 |
+
MODEL_15931 = 167
|
| 16 |
+
MODEL_QUERY_9905 = 48
|
| 17 |
+
MODEL_QUERY_11791 = 66
|
| 18 |
+
MODEL_CHAT_11120 = 57
|
| 19 |
+
MODEL_CHAT_11121 = 58
|
| 20 |
+
MODEL_CHAT_12119 = 70
|
| 21 |
+
MODEL_CHAT_12121 = 69
|
| 22 |
+
MODEL_CHAT_12437 = 74
|
| 23 |
+
MODEL_CHAT_12491 = 76
|
| 24 |
+
MODEL_CHAT_12623 = 78
|
| 25 |
+
MODEL_CHAT_12950 = 79
|
| 26 |
+
MODEL_CHAT_12968 = 101
|
| 27 |
+
MODEL_CHAT_13404 = 102
|
| 28 |
+
MODEL_CHAT_13566 = 103
|
| 29 |
+
MODEL_CHAT_13930 = 108
|
| 30 |
+
MODEL_CHAT_14255 = 110
|
| 31 |
+
MODEL_CHAT_14256 = 111
|
| 32 |
+
MODEL_CHAT_14942 = 114
|
| 33 |
+
MODEL_CHAT_15305 = 120
|
| 34 |
+
MODEL_CHAT_15600 = 123
|
| 35 |
+
MODEL_CHAT_16718 = 175
|
| 36 |
+
MODEL_CHAT_15729 = 168
|
| 37 |
+
MODEL_CHAT_16579 = 173
|
| 38 |
+
MODEL_CHAT_16579_CRUSOE = 174
|
| 39 |
+
MODEL_DRAFT_11408 = 65
|
| 40 |
+
MODEL_DRAFT_CHAT_11883 = 67
|
| 41 |
+
MODEL_DRAFT_CHAT_12196 = 72
|
| 42 |
+
MODEL_DRAFT_CHAT_12413 = 73
|
| 43 |
+
MODEL_DRAFT_CHAT_13175 = 104
|
| 44 |
+
MODEL_CHAT_3_5_TURBO = 28
|
| 45 |
+
MODEL_CHAT_GPT_4 = 30
|
| 46 |
+
MODEL_CHAT_GPT_4_1106_PREVIEW = 37
|
| 47 |
+
MODEL_TEXT_EMBEDDING_OPENAI_ADA = 91
|
| 48 |
+
MODEL_TEXT_EMBEDDING_OPENAI_3_SMALL = 163
|
| 49 |
+
MODEL_TEXT_EMBEDDING_OPENAI_3_LARGE = 164
|
| 50 |
+
MODEL_CHAT_GPT_4O_2024_05_13 = 71
|
| 51 |
+
MODEL_CHAT_GPT_4O_2024_08_06 = 109
|
| 52 |
+
MODEL_CHAT_GPT_4O_MINI_2024_07_18 = 113
|
| 53 |
+
MODEL_CHAT_O1_PREVIEW = 117
|
| 54 |
+
MODEL_CHAT_O1_MINI = 118
|
| 55 |
+
MODEL_CHAT_O1 = 170
|
| 56 |
+
MODEL_GOOGLE_GEMINI_1_0_PRO = 61
|
| 57 |
+
MODEL_GOOGLE_GEMINI_1_5_PRO = 62
|
| 58 |
+
MODEL_CLAUDE_3_OPUS_20240229 = 63
|
| 59 |
+
MODEL_CLAUDE_3_SONNET_20240229 = 64
|
| 60 |
+
MODEL_CLAUDE_3_5_SONNET_20240620 = 80
|
| 61 |
+
MODEL_CLAUDE_3_5_SONNET_20241022 = 166
|
| 62 |
+
MODEL_CLAUDE_3_5_HAIKU_20241022 = 171
|
| 63 |
+
MODEL_CLAUDE_3_HAIKU_20240307 = 172
|
| 64 |
+
MODEL_TOGETHERAI_TEXT_EMBEDDING_M2_BERT = 81
|
| 65 |
+
MODEL_TOGETHERAI_LLAMA_3_1_8B_INSTRUCT = 165
|
| 66 |
+
MODEL_HUGGING_FACE_TEXT_EMBEDDING_M2_BERT = 82
|
| 67 |
+
MODEL_HUGGING_FACE_TEXT_EMBEDDING_UAE_CODE = 83
|
| 68 |
+
MODEL_HUGGING_FACE_TEXT_EMBEDDING_BGE = 84
|
| 69 |
+
MODEL_HUGGING_FACE_TEXT_EMBEDDING_BLADE = 85
|
| 70 |
+
MODEL_HUGGING_FACE_TEXT_EMBEDDING_ARCTIC_LARGE = 86
|
| 71 |
+
MODEL_HUGGING_FACE_TEXT_EMBEDDING_E5_BASE = 87
|
| 72 |
+
MODEL_HUGGING_FACE_TEXT_EMBEDDING_MXBAI = 88
|
| 73 |
+
MODEL_LLAMA_3_1_8B_INSTRUCT = 106
|
| 74 |
+
MODEL_LLAMA_3_1_70B_INSTRUCT = 107
|
| 75 |
+
MODEL_LLAMA_3_1_405B_INSTRUCT = 105
|
| 76 |
+
MODEL_LLAMA_3_1_70B_INSTRUCT_LONG_CONTEXT = 116
|
| 77 |
+
MODEL_NOMIC_TEXT_EMBEDDING_V1 = 89
|
| 78 |
+
MODEL_NOMIC_TEXT_EMBEDDING_V1_5 = 90
|
| 79 |
+
MODEL_MISTRAL_7B = 77
|
| 80 |
+
MODEL_SALESFORCE_EMBEDDING_2R = 99
|
| 81 |
+
MODEL_TEI_BGE_M3 = 92
|
| 82 |
+
MODEL_TEI_NOMIC_EMBED_TEXT_V1 = 93
|
| 83 |
+
MODEL_TEI_INTFLOAT_E5_LARGE_INSTRUCT = 94
|
| 84 |
+
MODEL_TEI_SNOWFLAKE_ARCTIC_EMBED_L = 95
|
| 85 |
+
MODEL_TEI_UAE_CODE_LARGE_V1 = 96
|
| 86 |
+
MODEL_TEI_B1ADE = 97
|
| 87 |
+
MODEL_TEI_WHEREISAI_UAE_LARGE_V1 = 98
|
| 88 |
+
MODEL_TEI_WHEREISAI_UAE_CODE_LARGE_V1 = 100
|
| 89 |
+
MODEL_OPENAI_COMPATIBLE = 200
|
| 90 |
+
MODEL_ANTHROPIC_COMPATIBLE = 201
|
| 91 |
+
MODEL_VERTEX_COMPATIBLE = 202
|
| 92 |
+
MODEL_BEDROCK_COMPATIBLE = 203
|
| 93 |
+
MODEL_AZURE_COMPATIBLE = 204
|
docker.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
docker run -d \
|
| 2 |
+
-p 127.0.0.1:9000:8080 \
|
| 3 |
+
-v open-webui:/app/backend/data \
|
| 4 |
+
-e OPENAI_API_BASE_URLS="http://host.docker.internal:8000/v1" \
|
| 5 |
+
-e OPENAI_API_KEYS="Ga_test_key1" \
|
| 6 |
+
--add-host=host.docker.internal:host-gateway \
|
| 7 |
+
--name open-webui \
|
| 8 |
+
--restart unless-stopped \
|
| 9 |
+
ghcr.io/open-webui/open-webui:main
|
gen_account.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from time import time
|
| 2 |
+
import httpx
|
| 3 |
+
import asyncio
|
| 4 |
+
from faker import Faker
|
| 5 |
+
import random
|
| 6 |
+
import string
|
| 7 |
+
import base64
|
| 8 |
+
import json
|
| 9 |
+
from datetime import datetime
|
| 10 |
+
from rich.console import Console
|
| 11 |
+
from rich.panel import Panel
|
| 12 |
+
from rich.table import Table
|
| 13 |
+
from rich import print as rprint
|
| 14 |
+
from typing import List
|
| 15 |
+
|
| 16 |
+
from auth.register import Registration
|
| 17 |
+
from config.api_keys import APIKeyManager
|
| 18 |
+
from protos import request_pb2, response_pb2
|
| 19 |
+
|
| 20 |
+
console = Console()
|
| 21 |
+
|
| 22 |
+
async def generate_firebase_client():
|
| 23 |
+
# Create heartbeat data with current date
|
| 24 |
+
heartbeat_data = {
|
| 25 |
+
"version": 2,
|
| 26 |
+
"heartbeats": [{
|
| 27 |
+
"agent": "fire-core/0.10.2 fire-core-esm2017/0.10.2 fire-js/ fire-auth/1.7.2 fire-auth-esm2017/1.7.2 fire-js-all-app/10.11.1",
|
| 28 |
+
"dates": [datetime.now().strftime("%Y-%m-%d")]
|
| 29 |
+
}]
|
| 30 |
+
}
|
| 31 |
+
return base64.b64encode(json.dumps(heartbeat_data).encode()).decode()
|
| 32 |
+
|
| 33 |
+
async def generate_account():
|
| 34 |
+
faker = Faker()
|
| 35 |
+
|
| 36 |
+
# Generate random email and password
|
| 37 |
+
email = faker.email(safe=True, domain='outlook.com')
|
| 38 |
+
password = ''.join(random.choices(string.ascii_letters + string.digits, k=random.randint(9, 15)))
|
| 39 |
+
|
| 40 |
+
firebase_client = await generate_firebase_client()
|
| 41 |
+
|
| 42 |
+
async with httpx.AsyncClient() as client:
|
| 43 |
+
# Step 1: Firebase Sign Up
|
| 44 |
+
signup_response = await client.post(
|
| 45 |
+
'https://identitytoolkit.googleapis.com/v1/accounts:signUp',
|
| 46 |
+
params={'key': 'AIzaSyDsOl-1XpT5err0Tcnx8FFod1H8gVGIycY'},
|
| 47 |
+
headers={
|
| 48 |
+
'X-Firebase-Client': firebase_client,
|
| 49 |
+
'Content-Type': 'application/json',
|
| 50 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36'
|
| 51 |
+
},
|
| 52 |
+
json={
|
| 53 |
+
'returnSecureToken': True,
|
| 54 |
+
'email': email,
|
| 55 |
+
'password': password,
|
| 56 |
+
'clientType': 'CLIENT_TYPE_WEB'
|
| 57 |
+
}
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
id_token = signup_response.json()['idToken']
|
| 61 |
+
|
| 62 |
+
# Step 2: Get Current User
|
| 63 |
+
current_user = request_pb2.CurrentUser()
|
| 64 |
+
current_user.jwt = id_token
|
| 65 |
+
current_user.f1 = 1
|
| 66 |
+
current_user.f2 = 1
|
| 67 |
+
|
| 68 |
+
current_user_response = await client.post(
|
| 69 |
+
'https://web-backend.codeium.com/exa.seat_management_pb.SeatManagementService/GetCurrentUser',
|
| 70 |
+
headers={
|
| 71 |
+
'Content-Type': 'application/proto',
|
| 72 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36',
|
| 73 |
+
'Accept-Language': 'en',
|
| 74 |
+
'Sec-Ch-Ua-Platform': '"Windows"',
|
| 75 |
+
'Sec-Ch-Ua': '"Not?A_Brand";v="99", "Chromium";v="130"',
|
| 76 |
+
'Sec-Ch-Ua-Mobile': '?0',
|
| 77 |
+
'Connect-Protocol-Version': '1',
|
| 78 |
+
'Accept': '*/*',
|
| 79 |
+
'Origin': 'https://codeium.com',
|
| 80 |
+
'Sec-Fetch-Site': 'same-site',
|
| 81 |
+
'Sec-Fetch-Mode': 'cors',
|
| 82 |
+
'Sec-Fetch-Dest': 'empty',
|
| 83 |
+
'Referer': 'https://codeium.com/',
|
| 84 |
+
"X-Auth-Token": id_token,
|
| 85 |
+
'Accept-Encoding': 'gzip, deflate, br',
|
| 86 |
+
},
|
| 87 |
+
content=current_user.SerializeToString()
|
| 88 |
+
)
|
| 89 |
+
assert current_user_response.status_code == 200
|
| 90 |
+
response_proto = response_pb2.CurrentUserResponse()
|
| 91 |
+
response_proto.ParseFromString(current_user_response.content)
|
| 92 |
+
console.print(Panel(f"[bold green]User Created[/bold green]\nName: {response_proto.user.name}\nStatus: {response_proto.status.status}"))
|
| 93 |
+
|
| 94 |
+
# Step 3: Get Preapproval For User
|
| 95 |
+
get_preapproval_for_user = request_pb2.GetPreapprovalForUser()
|
| 96 |
+
get_preapproval_for_user.jwt = id_token
|
| 97 |
+
get_preapproval_for_user_response = await client.post(
|
| 98 |
+
'https://web-backend.codeium.com/exa.seat_management_pb.SeatManagementService/GetPreapprovalForUser',
|
| 99 |
+
headers={
|
| 100 |
+
'Content-Type': 'application/proto',
|
| 101 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36',
|
| 102 |
+
'Accept-Language': 'en',
|
| 103 |
+
'Sec-Ch-Ua-Platform': '"Windows"',
|
| 104 |
+
'Sec-Ch-Ua': '"Not?A_Brand";v="99", "Chromium";v="130"',
|
| 105 |
+
'Sec-Ch-Ua-Mobile': '?0',
|
| 106 |
+
'Connect-Protocol-Version': '1',
|
| 107 |
+
'Accept': '*/*',
|
| 108 |
+
'Origin': 'https://codeium.com',
|
| 109 |
+
'Sec-Fetch-Site': 'same-site',
|
| 110 |
+
'Sec-Fetch-Mode': 'cors',
|
| 111 |
+
'Sec-Fetch-Dest': 'empty',
|
| 112 |
+
'Referer': 'https://codeium.com/',
|
| 113 |
+
'Accept-Encoding': 'gzip, deflate, br',
|
| 114 |
+
"X-Auth-Token": id_token,
|
| 115 |
+
'Priority': 'u=1, i'
|
| 116 |
+
},
|
| 117 |
+
content=get_preapproval_for_user.SerializeToString()
|
| 118 |
+
)
|
| 119 |
+
assert get_preapproval_for_user_response.status_code == 200
|
| 120 |
+
|
| 121 |
+
# Step 4: Update Name using Protobuf
|
| 122 |
+
change_name = request_pb2.ChangeName()
|
| 123 |
+
change_name.jwt = id_token
|
| 124 |
+
change_name.name = faker.name()
|
| 125 |
+
|
| 126 |
+
name_response = await client.post(
|
| 127 |
+
'https://web-backend.codeium.com/exa.seat_management_pb.SeatManagementService/UpdateName',
|
| 128 |
+
headers={
|
| 129 |
+
'Content-Type': 'application/proto',
|
| 130 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36',
|
| 131 |
+
'Accept-Language': 'en',
|
| 132 |
+
'Sec-Ch-Ua-Platform': '"Windows"',
|
| 133 |
+
'Sec-Ch-Ua': '"Not?A_Brand";v="99", "Chromium";v="130"',
|
| 134 |
+
'Sec-Ch-Ua-Mobile': '?0',
|
| 135 |
+
'Connect-Protocol-Version': '1',
|
| 136 |
+
'Accept': '*/*',
|
| 137 |
+
'Origin': 'https://codeium.com',
|
| 138 |
+
'Sec-Fetch-Site': 'same-site',
|
| 139 |
+
'Sec-Fetch-Mode': 'cors',
|
| 140 |
+
'Sec-Fetch-Dest': 'empty',
|
| 141 |
+
'Referer': 'https://codeium.com/',
|
| 142 |
+
'Accept-Encoding': 'gzip, deflate, br',
|
| 143 |
+
'Priority': 'u=1, i'
|
| 144 |
+
},
|
| 145 |
+
content=change_name.SerializeToString()
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
return {
|
| 149 |
+
'email': email,
|
| 150 |
+
'password': password,
|
| 151 |
+
'id_token': id_token,
|
| 152 |
+
'name_response': name_response.status_code
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
async def generate_multiple_accounts(num_accounts: int):
|
| 156 |
+
tasks = []
|
| 157 |
+
for _ in range(num_accounts):
|
| 158 |
+
tasks.append(generate_account())
|
| 159 |
+
|
| 160 |
+
results = await asyncio.gather(*tasks)
|
| 161 |
+
return results
|
| 162 |
+
|
| 163 |
+
async def main():
|
| 164 |
+
# 获取命令行参数,默认创建3个账号
|
| 165 |
+
num_accounts = 3
|
| 166 |
+
|
| 167 |
+
with console.status(f"[bold green]Generating {num_accounts} accounts...", spinner="dots"):
|
| 168 |
+
results = await generate_multiple_accounts(num_accounts)
|
| 169 |
+
|
| 170 |
+
for result in results:
|
| 171 |
+
id_token = result['id_token']
|
| 172 |
+
email = result['email']
|
| 173 |
+
name_response = result['name_response']
|
| 174 |
+
|
| 175 |
+
if name_response != 200:
|
| 176 |
+
console.print(f"[bold red]Failed to update name. Status code: {name_response}[/bold red]")
|
| 177 |
+
continue
|
| 178 |
+
|
| 179 |
+
key_manager = APIKeyManager()
|
| 180 |
+
|
| 181 |
+
with console.status("[bold green]Registering user...", spinner="dots"):
|
| 182 |
+
api_key = await Registration.register_user(id_token.strip())
|
| 183 |
+
|
| 184 |
+
# Add GetCurrentUser call after registration
|
| 185 |
+
async with httpx.AsyncClient() as client:
|
| 186 |
+
current_user = request_pb2.CurrentUser()
|
| 187 |
+
current_user.jwt = id_token
|
| 188 |
+
current_user.f1 = 1
|
| 189 |
+
current_user.f2 = 1
|
| 190 |
+
|
| 191 |
+
current_user_response = await client.post(
|
| 192 |
+
'https://web-backend.codeium.com/exa.seat_management_pb.SeatManagementService/GetCurrentUser',
|
| 193 |
+
headers={
|
| 194 |
+
'Content-Type': 'application/proto',
|
| 195 |
+
'X-Auth-Token': id_token,
|
| 196 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36',
|
| 197 |
+
'Accept-Language': 'en',
|
| 198 |
+
'Sec-Ch-Ua-Platform': '"Windows"',
|
| 199 |
+
'Sec-Ch-Ua': '"Not?A_Brand";v="99", "Chromium";v="130"',
|
| 200 |
+
'Sec-Ch-Ua-Mobile': '?0',
|
| 201 |
+
'Connect-Protocol-Version': '1',
|
| 202 |
+
'Accept': '*/*',
|
| 203 |
+
'Origin': 'https://codeium.com',
|
| 204 |
+
'Sec-Fetch-Site': 'same-site',
|
| 205 |
+
'Sec-Fetch-Mode': 'cors',
|
| 206 |
+
'Sec-Fetch-Dest': 'empty',
|
| 207 |
+
'Referer': 'https://codeium.com/',
|
| 208 |
+
'Accept-Encoding': 'gzip, deflate, br',
|
| 209 |
+
},
|
| 210 |
+
content=current_user.SerializeToString()
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
response_proto = response_pb2.CurrentUserResponse()
|
| 214 |
+
response_proto.ParseFromString(current_user_response.content)
|
| 215 |
+
|
| 216 |
+
key_manager.add_key(api_key)
|
| 217 |
+
|
| 218 |
+
# Update table to include password
|
| 219 |
+
table = Table(title=f"Registration Results - {email}")
|
| 220 |
+
table.add_column("Field", style="cyan")
|
| 221 |
+
table.add_column("Value", style="green")
|
| 222 |
+
|
| 223 |
+
table.add_row("Email", email)
|
| 224 |
+
table.add_row("Password", result['password'])
|
| 225 |
+
table.add_row("API Key", api_key)
|
| 226 |
+
table.add_row("Status", str(response_proto.status.status))
|
| 227 |
+
|
| 228 |
+
console.print(table)
|
| 229 |
+
console.print("\n") # Add a blank line between tables
|
| 230 |
+
|
| 231 |
+
if __name__ == "__main__":
|
| 232 |
+
asyncio.run(main())
|
init_pool.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from managers.global_chat_manager import GlobalChatManager
|
| 2 |
+
import uvicorn
|
| 3 |
+
import signal
|
| 4 |
+
import sys
|
| 5 |
+
import multiprocessing
|
| 6 |
+
import asyncio
|
| 7 |
+
from rich.console import Console
|
| 8 |
+
|
| 9 |
+
console = Console()
|
| 10 |
+
|
| 11 |
+
async def init_chat_queue():
|
| 12 |
+
# 初始化全局chat manager
|
| 13 |
+
chat_manager = GlobalChatManager(
|
| 14 |
+
console=console,
|
| 15 |
+
queue_type="deque",
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
await chat_manager.initialize_queue()
|
| 19 |
+
|
| 20 |
+
if __name__ == "__main__":
|
| 21 |
+
asyncio.run(init_chat_queue())
|
main.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from managers.chat_manager import ChatManager
|
| 2 |
+
from api.telemetry import TelemetryAPI
|
| 3 |
+
from config.api_keys import APIKeyManager
|
| 4 |
+
from config.models import ModelID
|
| 5 |
+
from auth.register import Registration
|
| 6 |
+
from auth.login import open_auth_url
|
| 7 |
+
from api.chat.chat_api import ChatAPI, ChatConfig
|
| 8 |
+
|
| 9 |
+
import urllib3
|
| 10 |
+
import asyncio
|
| 11 |
+
|
| 12 |
+
urllib3.disable_warnings()
|
| 13 |
+
|
| 14 |
+
from rich.console import Console
|
| 15 |
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
| 16 |
+
|
| 17 |
+
console = Console()
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def select_api_key(key_manager: APIKeyManager) -> str:
|
| 21 |
+
"""Let user select an API key from available keys"""
|
| 22 |
+
keys = key_manager.list_keys()
|
| 23 |
+
if not keys:
|
| 24 |
+
return None
|
| 25 |
+
|
| 26 |
+
console.print("\n[bold cyan]Available API keys:[/]")
|
| 27 |
+
for i, key in enumerate(keys):
|
| 28 |
+
console.print(f"[green]{i + 1}[/]. {key}")
|
| 29 |
+
|
| 30 |
+
while True:
|
| 31 |
+
try:
|
| 32 |
+
choice = int(input("\nSelect API key (number) or 0 for new login: "))
|
| 33 |
+
if choice == 0:
|
| 34 |
+
return None
|
| 35 |
+
if 1 <= choice <= len(keys):
|
| 36 |
+
return keys[choice - 1]
|
| 37 |
+
except ValueError:
|
| 38 |
+
return keys[0]
|
| 39 |
+
print("Invalid selection. Please try again.")
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
async def main():
|
| 43 |
+
key_manager = APIKeyManager()
|
| 44 |
+
chat_manager = ChatManager()
|
| 45 |
+
api_key = select_api_key(key_manager)
|
| 46 |
+
|
| 47 |
+
# If no API key selected or available, do registration
|
| 48 |
+
if api_key is None:
|
| 49 |
+
_ = open_auth_url()
|
| 50 |
+
firebase_token = input("Please paste your firebase token: ")
|
| 51 |
+
api_key = await Registration.register_user(firebase_token.strip())
|
| 52 |
+
key_manager.add_key(api_key)
|
| 53 |
+
print(f"Registered successfully. API key: {api_key}")
|
| 54 |
+
|
| 55 |
+
with Progress(
|
| 56 |
+
SpinnerColumn(),
|
| 57 |
+
TextColumn("[progress.description]{task.description}"),
|
| 58 |
+
console=console,
|
| 59 |
+
) as progress:
|
| 60 |
+
progress.add_task("Initializing Chat API...", total=None)
|
| 61 |
+
tele = TelemetryAPI(api_key=api_key)
|
| 62 |
+
await tele.do_telemetry()
|
| 63 |
+
|
| 64 |
+
await chat_manager.add_chat(api_key)
|
| 65 |
+
|
| 66 |
+
progress.stop()
|
| 67 |
+
|
| 68 |
+
with Progress(
|
| 69 |
+
SpinnerColumn(),
|
| 70 |
+
TextColumn("[progress.description]{task.description}"),
|
| 71 |
+
console=console,
|
| 72 |
+
) as progress:
|
| 73 |
+
_ = progress.add_task("Sending message...", total=None)
|
| 74 |
+
|
| 75 |
+
config = ChatConfig(
|
| 76 |
+
model_id=ModelID.MODEL_CLAUDE_3_OPUS_20240229,
|
| 77 |
+
temperature=0.7,
|
| 78 |
+
max_tokens=8192,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
message = [
|
| 82 |
+
{"role": "user", "content": "1234567890"},
|
| 83 |
+
{"role": "assistant", "content": "1234567890"},
|
| 84 |
+
{"role": "user", "content": "1234567890"},
|
| 85 |
+
{"role": "assistant", "content": "1234567890"},
|
| 86 |
+
{"role": "user", "content": "1234567890"},
|
| 87 |
+
{"role": "assistant", "content": "1234567890"},
|
| 88 |
+
{"role": "user", "content": "1234567890"},
|
| 89 |
+
{"role": "assistant", "content": "1234567890"},
|
| 90 |
+
{"role": "user", "content": "1234567890"},
|
| 91 |
+
{"role": "assistant", "content": "1234567890"},
|
| 92 |
+
{"role": "user", "content": "tell me a joke"},
|
| 93 |
+
]
|
| 94 |
+
first_chunk = True
|
| 95 |
+
chat = await chat_manager.get_chat()
|
| 96 |
+
|
| 97 |
+
tokens = 0
|
| 98 |
+
|
| 99 |
+
async for chunk, token_count in chat.send_message(
|
| 100 |
+
messages=message,
|
| 101 |
+
config=config,
|
| 102 |
+
system_prompt="You are a helpful assistant.",
|
| 103 |
+
stream=True,
|
| 104 |
+
):
|
| 105 |
+
if first_chunk:
|
| 106 |
+
progress.stop()
|
| 107 |
+
first_chunk = False
|
| 108 |
+
console.print("=" * 20)
|
| 109 |
+
if chunk:
|
| 110 |
+
console.print(
|
| 111 |
+
chunk, end="", markup=True, highlight=True, emoji=True
|
| 112 |
+
)
|
| 113 |
+
tokens += token_count
|
| 114 |
+
|
| 115 |
+
console.print(f"\n[bold green]Tokens: {tokens}[/]")
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
if __name__ == "__main__":
|
| 119 |
+
asyncio.run(main())
|
managers/__pycache__/chat_manager.cpython-311.pyc
ADDED
|
Binary file (2.32 kB). View file
|
|
|
managers/__pycache__/chat_manager.cpython-312.pyc
ADDED
|
Binary file (2.09 kB). View file
|
|
|
managers/__pycache__/global_chat_manager.cpython-311.pyc
ADDED
|
Binary file (7 kB). View file
|
|
|
managers/__pycache__/global_chat_manager.cpython-312.pyc
ADDED
|
Binary file (6.31 kB). View file
|
|
|
managers/chat_manager.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
from managers.queue.base import ChatQueueBase
|
| 3 |
+
from managers.queue.deque_queue import DequeQueue
|
| 4 |
+
|
| 5 |
+
from api.chat.chat_api import ChatAPI
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class ChatManager:
|
| 9 |
+
def __init__(self, queue_type: str = "deque", **kwargs):
|
| 10 |
+
self.queue: ChatQueueBase = self._create_queue(queue_type, **kwargs)
|
| 11 |
+
|
| 12 |
+
def _create_queue(self, queue_type: str, **kwargs) -> ChatQueueBase:
|
| 13 |
+
queue_types = {
|
| 14 |
+
"deque": DequeQueue,
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
if queue_type not in queue_types:
|
| 18 |
+
raise ValueError(f"Unsupported queue type: {queue_type}")
|
| 19 |
+
|
| 20 |
+
return queue_types[queue_type](**kwargs)
|
| 21 |
+
|
| 22 |
+
async def add_chat(self, api_key: str):
|
| 23 |
+
"""添加一个新的chat实例到队列"""
|
| 24 |
+
await self.queue.add(api_key)
|
| 25 |
+
|
| 26 |
+
async def get_chat(self) -> Optional[ChatAPI]:
|
| 27 |
+
"""获取队列中的下一个chat实例"""
|
| 28 |
+
return await self.queue.get()
|
| 29 |
+
|
| 30 |
+
async def length(self) -> int:
|
| 31 |
+
"""获取队列长度"""
|
| 32 |
+
return await self.queue.length()
|
managers/global_chat_manager.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, List
|
| 2 |
+
from rich.console import Console
|
| 3 |
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
| 4 |
+
|
| 5 |
+
from managers.chat_manager import ChatManager
|
| 6 |
+
from api.telemetry import TelemetryAPI
|
| 7 |
+
from config.api_keys import APIKeyManager
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class GlobalChatManager:
|
| 11 |
+
"""
|
| 12 |
+
全局聊天管理器
|
| 13 |
+
专注于管理分布式队列(Redis/RabbitMQ)中的API密钥
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
_instance: Optional["GlobalChatManager"] = None
|
| 17 |
+
|
| 18 |
+
def __new__(cls, *args, **kwargs):
|
| 19 |
+
if cls._instance is None:
|
| 20 |
+
cls._instance = super().__new__(cls)
|
| 21 |
+
return cls._instance
|
| 22 |
+
|
| 23 |
+
def __init__(self, console: Console, queue_type: str = "deque", **kwargs):
|
| 24 |
+
# 防止重复初始化
|
| 25 |
+
if hasattr(self, "initialized"):
|
| 26 |
+
return
|
| 27 |
+
|
| 28 |
+
self.console = console
|
| 29 |
+
self.queue_type = queue_type
|
| 30 |
+
|
| 31 |
+
if queue_type != "deque":
|
| 32 |
+
raise ValueError("GlobalChatManager only supports 'deque' queue type")
|
| 33 |
+
|
| 34 |
+
self.chat_manager = ChatManager(queue_type=queue_type, **kwargs)
|
| 35 |
+
self.key_manager = APIKeyManager()
|
| 36 |
+
self.initialized = True
|
| 37 |
+
|
| 38 |
+
async def _register_single_key(self, api_key: str) -> None:
|
| 39 |
+
"""注册单个API key到队列"""
|
| 40 |
+
try:
|
| 41 |
+
await self.chat_manager.add_chat(api_key)
|
| 42 |
+
await self._send_registration_signals(api_key)
|
| 43 |
+
self.console.log(
|
| 44 |
+
f"[bold green]✓[/] API key registered successfully: {api_key[:8]}..."
|
| 45 |
+
)
|
| 46 |
+
except Exception as e:
|
| 47 |
+
self.console.log(
|
| 48 |
+
f"[bold red]✗[/] Failed to register API key {api_key[:8]}\n"
|
| 49 |
+
f" Error: {str(e)}"
|
| 50 |
+
)
|
| 51 |
+
raise
|
| 52 |
+
finally:
|
| 53 |
+
self.console.log("─" * 80)
|
| 54 |
+
|
| 55 |
+
async def _send_registration_signals(self, api_key: str) -> None:
|
| 56 |
+
"""发送注册相关的信号"""
|
| 57 |
+
self.console.log(
|
| 58 |
+
f"[bold yellow]⚡[/] Sending registration signals for {api_key[:8]}..."
|
| 59 |
+
)
|
| 60 |
+
await TelemetryAPI(api_key=api_key).do_telemetry()
|
| 61 |
+
|
| 62 |
+
async def initialize_queue(self, api_keys: List[str] = None) -> None:
|
| 63 |
+
"""初始化分布式队列"""
|
| 64 |
+
with Progress(
|
| 65 |
+
SpinnerColumn(),
|
| 66 |
+
TextColumn("[progress.description]{task.description}"),
|
| 67 |
+
console=self.console,
|
| 68 |
+
) as progress:
|
| 69 |
+
task = progress.add_task(
|
| 70 |
+
f"[bold blue]Initializing {self.queue_type.upper()} queue...[/]",
|
| 71 |
+
total=None
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
# 如果没有提供API keys,则从配置中读取
|
| 75 |
+
if not api_keys:
|
| 76 |
+
api_keys = self.key_manager.list_keys()
|
| 77 |
+
# 对API keys进行去重
|
| 78 |
+
api_keys = list(dict.fromkeys(api_keys))
|
| 79 |
+
|
| 80 |
+
if not api_keys:
|
| 81 |
+
raise ValueError("No API keys provided or found in configuration")
|
| 82 |
+
|
| 83 |
+
# 注册所有API keys
|
| 84 |
+
for api_key in api_keys:
|
| 85 |
+
await self._register_single_key(api_key)
|
| 86 |
+
progress.update(task, advance=1)
|
| 87 |
+
|
| 88 |
+
# 验证队列状态
|
| 89 |
+
queue_size = await self.chat_manager.length()
|
| 90 |
+
if queue_size == 0:
|
| 91 |
+
raise RuntimeError(f"Failed to initialize {self.queue_type} queue")
|
| 92 |
+
|
| 93 |
+
self.console.log(f"号池已初始化,当前容量: {queue_size} 个API密钥")
|
| 94 |
+
|
| 95 |
+
progress.update(
|
| 96 |
+
task,
|
| 97 |
+
description=f"{self.queue_type.upper()} queue initialized with {queue_size} keys",
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
async def add_api_key(self, api_key: str) -> None:
|
| 101 |
+
"""添加新的API key到队列"""
|
| 102 |
+
await self._register_single_key(api_key)
|
| 103 |
+
|
| 104 |
+
async def get_queue_size(self) -> int:
|
| 105 |
+
"""获取当前队列大小"""
|
| 106 |
+
return await self.chat_manager.length()
|
managers/queue/__pycache__/base.cpython-311.pyc
ADDED
|
Binary file (1.22 kB). View file
|
|
|
managers/queue/__pycache__/base.cpython-312.pyc
ADDED
|
Binary file (1.04 kB). View file
|
|
|
managers/queue/__pycache__/deque_queue.cpython-311.pyc
ADDED
|
Binary file (1.77 kB). View file
|
|
|
managers/queue/__pycache__/deque_queue.cpython-312.pyc
ADDED
|
Binary file (1.57 kB). View file
|
|
|
managers/queue/__pycache__/rabbitmq_queue.cpython-312.pyc
ADDED
|
Binary file (3.25 kB). View file
|
|
|
managers/queue/__pycache__/redis_queue.cpython-312.pyc
ADDED
|
Binary file (2.13 kB). View file
|
|
|
managers/queue/base.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
from typing import Optional
|
| 3 |
+
from api.chat.chat_api import ChatAPI
|
| 4 |
+
|
| 5 |
+
class ChatQueueBase(ABC):
|
| 6 |
+
@abstractmethod
|
| 7 |
+
async def add(self, api_key: str) -> None:
|
| 8 |
+
pass
|
| 9 |
+
|
| 10 |
+
@abstractmethod
|
| 11 |
+
async def get(self) -> Optional[ChatAPI]:
|
| 12 |
+
pass
|
| 13 |
+
|
| 14 |
+
@abstractmethod
|
| 15 |
+
async def length(self) -> int:
|
| 16 |
+
pass
|
managers/queue/deque_queue.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import deque
|
| 2 |
+
from typing import Optional
|
| 3 |
+
from api.chat.chat_api import ChatAPI
|
| 4 |
+
from .base import ChatQueueBase
|
| 5 |
+
|
| 6 |
+
class DequeQueue(ChatQueueBase):
|
| 7 |
+
def __init__(self):
|
| 8 |
+
self.queue = deque()
|
| 9 |
+
|
| 10 |
+
async def add(self, api_key: str) -> None:
|
| 11 |
+
self.queue.append(ChatAPI(api_key=api_key))
|
| 12 |
+
|
| 13 |
+
async def get(self) -> Optional[ChatAPI]:
|
| 14 |
+
if not self.queue:
|
| 15 |
+
return None
|
| 16 |
+
chat = self.queue.popleft()
|
| 17 |
+
self.queue.append(chat)
|
| 18 |
+
return chat
|
| 19 |
+
|
| 20 |
+
async def length(self) -> int:
|
| 21 |
+
return len(self.queue)
|