diff --git a/APIKey.py b/APIKey.py index bbc6edc..3fc0ff1 100644 --- a/APIKey.py +++ b/APIKey.py @@ -8,7 +8,6 @@ class APIKey: if provider == Provider.OPENAI: self.model = "" - self.trial = False self.has_quota = False self.default_org = "" self.organizations = [] @@ -74,6 +73,10 @@ class APIKey: self.unlimited = False self.pro_voice_limit = 0 + elif provider == Provider.DEEPSEEK: + self.balance = "" + self.available = False + def clone(self): cloned_key = APIKey(self.provider, self.api_key) cloned_key.__dict__ = self.__dict__.copy() @@ -91,3 +94,4 @@ class Provider(Enum): MISTRAL = 8 OPENROUTER = 9 ELEVENLABS = 10 + DEEPSEEK = 11 diff --git a/Deepseek.py b/Deepseek.py new file mode 100644 index 0000000..15b004c --- /dev/null +++ b/Deepseek.py @@ -0,0 +1,33 @@ +import APIKey + + +async def check_whale(key: APIKey, session): + async with session.get(f'https://api.deepseek.com/models', headers={'Authorization': f'Bearer {key.api_key}'}) as response: + if response.status != 200: + return + if not await check_balance(key, session): + return + return True + + +async def check_balance(key: APIKey, session): + async with session.get(f'https://api.deepseek.com/user/balance', headers={'Authorization': f'Bearer {key.api_key}'}) as response: + if response.status != 200: + return + data = await response.json() + key.available = data.get('is_available', False) + balance_infos = data.get('balance_infos', []) + if balance_infos: + key.balance = float(balance_infos[0].get('total_balance', '0.0')) + return True + + +def pretty_print_deepseek_keys(keys): + keys = sorted(keys, key=lambda x: x.balance, reverse=True) + print('-' * 90) + available = sum(1 for key in keys if key.available) + print(f'Validated {len(keys)} Deepseek keys:') + for key in keys: + balance_str = f'| ${key.balance}' if key.available else '' + print(f'{key.api_key} {balance_str}') + print(f'\n--- Total Valid Deepseek Keys: {len(keys)} ({available} with sufficient usage balance) ---\n') diff --git a/OpenAI.py b/OpenAI.py index 182fc86..cbfc753 100644 --- a/OpenAI.py +++ b/OpenAI.py @@ -2,8 +2,7 @@ import APIKey import asyncio oai_api_url = "https://api.openai.com/v1" -oai_t1_rpm_limits = {"gpt-3.5-turbo": 3500, "gpt-4": 500, "gpt-4-32k-0314": 20} -oai_tiers = {40000: 'Free', 200000: 'Tier1', 2000000: 'Tier2', 4000000: 'Tier3', 10000000: 'Tier4', 50000000: 'Tier5'} +oai_tiers = {40000: 'Free', 200000: 'Tier1', 2000000: 'Tier2', 4000000: 'Tier3', 10000000: 'Tier4', 150000000: 'Tier5'} async def get_oai_model(key: APIKey, session, retries, org=None): @@ -15,7 +14,7 @@ async def get_oai_model(key: APIKey, session, retries, org=None): if response.status == 200: data = await response.json() models = sorted(data["data"], key=lambda m: len(m["id"])) - top_model = "gpt-3.5-turbo" + top_model = "gpt-4o-mini" for model in models: if "ft:" in model["id"]: key.has_special_models = True @@ -25,12 +24,12 @@ async def get_oai_model(key: APIKey, session, retries, org=None): key.real_32k = True if model["id"] == "gpt-4-32k-0314": top_model = model["id"] - elif model["id"] == "gpt-4": + elif model["id"] == "gpt-4o": top_model = model["id"] key.model = top_model return True elif response.status == 403: - key.model = "gpt-4" + key.model = "gpt-4o" return True elif response.status != 502: return @@ -59,21 +58,17 @@ async def get_oai_key_attribs(key: APIKey, session, retries, org=None): case "invalid_request_error": key.has_quota = True key.rpm = int(response.headers.get("x-ratelimit-limit-requests")) - if key.rpm < oai_t1_rpm_limits[key.model]: # oddly seen some gpt4 trial keys - key.trial = True key.tier = await get_oai_key_tier(key, session, retries) return True - elif response.status != 502: + elif response.status not in [502, 500]: return await asyncio.sleep(0.5) # this will weed out fake t4/t5 keys reporting a 10k rpm limit, those keys would have requested to have their rpm increased async def get_oai_key_tier(key: APIKey, session, retries, org=None): - if key.trial: - return 'Free' - chat_object = {"model": f'gpt-3.5-turbo', "messages": [{"role": "user", "content": ""}], "max_tokens": 0} - for _ in range(retries): + chat_object = {"model": f'gpt-4o-mini', "messages": [{"role": "user", "content": ""}], "max_tokens": 0} + for attempt in range(retries): headers = {'Authorization': f'Bearer {key.api_key}', 'accept': 'application/json'} if org is not None: headers['OpenAI-Organization'] = org @@ -82,6 +77,10 @@ async def get_oai_key_tier(key: APIKey, session, retries, org=None): try: return oai_tiers[int(response.headers.get("x-ratelimit-limit-tokens"))] except (KeyError, TypeError, ValueError): + if attempt == retries - 1: + # saw a few keys return no limit headers at all for 4o-mini, but then a 4mil token limit for normal 4o which is more than t4, while also having a t2 rpm (5k)? + # there also seems to be another key tier in between free and t1, with a 100k token limit and 200 rpm + return "Tier Unknown (strange or absent token limit in header)" continue elif response.status != 502: return @@ -91,10 +90,10 @@ async def get_oai_key_tier(key: APIKey, session, retries, org=None): async def get_oai_org(key: APIKey, session, retries): for _ in range(retries): - async with session.get(f'{oai_api_url}/organizations', headers={'Authorization': f'Bearer {key.api_key}'}) as response: + async with session.get(f'{oai_api_url}/me', headers={'Authorization': f'Bearer {key.api_key}'}) as response: if response.status == 200: data = await response.json() - orgs = data["data"] + orgs = data["orgs"]["data"] for org in orgs: if not org["personal"]: @@ -112,7 +111,7 @@ async def get_oai_org(key: APIKey, session, retries): async def clone_key(key: APIKey, session, retries): cloned_keys = set() - if len(key.organizations) <= 1: + if len(key.organizations) <= 0: return False for org in key.organizations: if org == key.default_org: @@ -127,9 +126,9 @@ async def clone_key(key: APIKey, session, retries): def check_manual_increase(key: APIKey): - if key.model == 'gpt-3.5-turbo' and key.rpm > 3500: + if key.model == 'gpt-4o-mini' and key.rpm > 500: # could false flag high tier alternative orgs that restrict model access to only 4o-mini return True - elif key.tier == 'Tier1' and key.model != 'gpt-3.5-turbo' and key.rpm > 500: + elif key.tier == 'Tier1' and key.model != 'gpt-4o-mini' and key.rpm > 500: return True elif key.tier in ['Tier2', 'Tier3'] and key.rpm > 5000: return True @@ -146,11 +145,11 @@ def pretty_print_oai_keys(keys, cloned_keys): t5_count = 0 key_groups = { - "gpt-3.5-turbo": { + "gpt-4o-mini": { "has_quota": [], "no_quota": [] }, - "gpt-4": { + "gpt-4o": { "has_quota": [], "no_quota": [] }, @@ -172,33 +171,31 @@ def pretty_print_oai_keys(keys, cloned_keys): key_groups[key.model]['no_quota'].append(key) no_quota_count += 1 - print(f'Validated {len(key_groups["gpt-3.5-turbo"]["has_quota"])} Turbo keys with quota:') - for key in key_groups["gpt-3.5-turbo"]["has_quota"]: + print(f'Validated {len(key_groups["gpt-4o-mini"]["has_quota"])} gpt-4o-mini keys with quota:') + for key in key_groups["gpt-4o-mini"]["has_quota"]: print(f"{key.api_key}" + (f" | default org - {key.default_org}" if key.default_org else "") + (f" | other orgs - {key.organizations}" if len(key.organizations) > 1 else "") + f" | {key.rpm} RPM" + (f" - {key.tier}" if key.tier else "") - + (" (RPM increased via request)" if check_manual_increase(key) else "") - + (f" | TRIAL KEY" if key.trial else "")) + + (" (RPM increased via request)" if check_manual_increase(key) else "")) - print(f'\nValidated {len(key_groups["gpt-3.5-turbo"]["no_quota"])} Turbo keys with no quota:') - for key in key_groups["gpt-3.5-turbo"]["no_quota"]: + print(f'\nValidated {len(key_groups["gpt-4o-mini"]["no_quota"])} gpt-4o-mini keys with no quota:') + for key in key_groups["gpt-4o-mini"]["no_quota"]: print(f"{key.api_key}" + (f" | default org - {key.default_org}" if key.default_org else "") + (f" | other orgs - {key.organizations}" if len(key.organizations) > 1 else "")) - print(f'\nValidated {len(key_groups["gpt-4"]["has_quota"])} gpt-4 keys with quota:') - for key in key_groups["gpt-4"]["has_quota"]: + print(f'\nValidated {len(key_groups["gpt-4o"]["has_quota"])} gpt-4o keys with quota:') + for key in key_groups["gpt-4o"]["has_quota"]: print(f"{key.api_key}" + (f" | default org - {key.default_org}" if key.default_org else "") + (f" | other orgs - {key.organizations}" if len(key.organizations) > 1 else "") + f" | {key.rpm} RPM" + (f" - {key.tier}" if key.tier else "") + (" (RPM increased via request)" if check_manual_increase(key) else "") - + (f" | TRIAL KEY" if key.trial else "") + (f" | key has finetuned models" if key.has_special_models else "")) - print(f'\nValidated {len(key_groups["gpt-4"]["no_quota"])} gpt-4 keys with no quota:') - for key in key_groups["gpt-4"]["no_quota"]: + print(f'\nValidated {len(key_groups["gpt-4o"]["no_quota"])} gpt-4o keys with no quota:') + for key in key_groups["gpt-4o"]["no_quota"]: print(f"{key.api_key}" + (f" | default org - {key.default_org}" if key.default_org else "") + (f" | other orgs - {key.organizations}" if len(key.organizations) > 1 else "") @@ -211,7 +208,6 @@ def pretty_print_oai_keys(keys, cloned_keys): + (f" | other orgs - {key.organizations}" if len(key.organizations) > 1 else "") + f" | {key.rpm} RPM" + (f" - {key.tier}" if key.tier else "") + (" (RPM increased via request)" if check_manual_increase(key) else "") - + (f" | TRIAL KEY" if key.trial else "") + (f" | key has finetuned models" if key.has_special_models else "") + (f" | real 32k key (pre deprecation)" if key.real_32k else "") + (f" | !!!god key!!!" if key.the_one else "")) diff --git a/README.md b/README.md index 6cec51c..bc4a10d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Currently supports and validates keys for the services below, and checks for the - MistralAI - (Subscription status) - OpenRouter - (Estimated balance, usage in $, credit limit, RPM, has purchased any credits) - ElevenLabs - (Key tier, remaining characters in plan, detect uncapped char quota, pro voice cloning limit, invoice details on pay as you go plans) +- DeepSeek - (Account balance) # Usage: `pip install -r requirements.txt` @@ -25,7 +26,6 @@ Currently supports and validates keys for the services below, and checks for the Outputs keys in a format that can be easily copied and pasted into khanon's proxy instead of pretty print - `-nooutput` Stops outputting and saving keys to the snapshot file (proxyoutput will also do this) diff --git a/main.py b/main.py index 7264603..3a6fa7d 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ from Anthropic import check_anthropic, pretty_print_anthropic_keys +from Deepseek import check_whale, pretty_print_deepseek_keys from IO import IO from OpenAI import get_oai_model, get_oai_key_attribs, get_oai_org, pretty_print_oai_keys, clone_key from AI21 import check_ai21, pretty_print_ai21_keys @@ -181,6 +182,16 @@ async def validate_vertexai(key: APIKey, sem): api_keys.add(key) +async def validate_whale(key: APIKey, sem): + async with sem, aiohttp.ClientSession() as session: + IO.conditional_print(f"Checking Deepseek key: {key.api_key}", args.verbose) + if await check_whale(key, session) is None: + IO.conditional_print(f"Invalid Deepseek key: {key.api_key}", args.verbose) + return + IO.conditional_print(f"Deepseek key '{key.api_key}' is valid", args.verbose) + api_keys.add(key) + + async def execute_with_retries(func, key, sem, retries): attempt = 0 while attempt < retries: @@ -210,6 +221,7 @@ makersuite_regex = re.compile(r'AIzaSy[A-Za-z0-9\-_]{33}') aws_regex = re.compile(r'^(AKIA[0-9A-Z]{16}):([A-Za-z0-9+/]{40})$') azure_regex = re.compile(r'^(.+):([a-z0-9]{32})$') openrouter_regex = re.compile(r'sk-or-v1-[a-z0-9]{64}') +deepseek_regex = re.compile(r'sk-[a-f0-9]{32}') # vertex_regex = re.compile(r'^(.+):(ya29.[A-Za-z0-9\-_]{469})$') regex for the oauth tokens, useless since they expire hourly executor = ThreadPoolExecutor(max_workers=100) concurrent_connections = asyncio.Semaphore(1500) @@ -245,15 +257,18 @@ async def validate_keys(): key_obj = APIKey(Provider.OPENROUTER, key) tasks.append(execute_with_retries(validate_openrouter, key_obj, concurrent_connections, 5)) elif "sk-" in key: - anthropic_flag = "T3BlbkFJ" not in key - if anthropic_flag: + anthropic_flag = "T3BlbkFJ" not in key and len(key) > 36 + deepseek_flag = len(key) < 36 + if deepseek_flag: + match = deepseek_regex.match(key) + elif anthropic_flag: match = anthropic_third_regex.match(key) else: match = oai_regex.match(key) if not match: continue - key_obj = APIKey(Provider.ANTHROPIC if anthropic_flag else Provider.OPENAI, key) - tasks.append(execute_with_retries(validate_anthropic if anthropic_flag else validate_openai, key_obj, concurrent_connections, 5)) + key_obj = APIKey(Provider.DEEPSEEK if deepseek_flag else Provider.ANTHROPIC if anthropic_flag else Provider.OPENAI, key) + tasks.append(execute_with_retries(validate_whale if deepseek_flag else validate_anthropic if anthropic_flag else validate_openai, key_obj, concurrent_connections, 5)) elif ":" and "AKIA" in key: match = aws_regex.match(key) if not match: @@ -293,7 +308,7 @@ async def validate_keys(): futures.clear() -def get_invalid_keys(valid_oai_keys, valid_anthropic_keys, valid_ai21_keys, valid_makersuite_keys, valid_aws_keys, valid_azure_keys, valid_vertexai_keys, valid_mistral_keys, valid_openrouter_keys, valid_elevenlabs_keys): +def get_invalid_keys(valid_oai_keys, valid_anthropic_keys, valid_ai21_keys, valid_makersuite_keys, valid_aws_keys, valid_azure_keys, valid_vertexai_keys, valid_mistral_keys, valid_openrouter_keys, valid_elevenlabs_keys, valid_deepseek_keys): valid_oai_keys_set = set([key.api_key for key in valid_oai_keys]) valid_anthropic_keys_set = set([key.api_key for key in valid_anthropic_keys]) valid_ai21_keys_set = set([key.api_key for key in valid_ai21_keys]) @@ -304,8 +319,9 @@ def get_invalid_keys(valid_oai_keys, valid_anthropic_keys, valid_ai21_keys, vali valid_mistral_keys_set = set([key.api_key for key in valid_mistral_keys]) valid_openrouter_keys_set = set([key.api_key for key in valid_openrouter_keys]) valid_elevenlabs_set = set([key.api_key for key in valid_elevenlabs_keys]) + valid_deepseek_set = set([key.api_key for key in valid_deepseek_keys]) - invalid_keys = inputted_keys - valid_oai_keys_set - valid_anthropic_keys_set - valid_ai21_keys_set - valid_makersuite_keys_set - valid_aws_keys_set - valid_azure_keys_set - valid_vertexai_keys_set - valid_mistral_keys_set - valid_openrouter_keys_set - valid_elevenlabs_set + invalid_keys = inputted_keys - valid_oai_keys_set - valid_anthropic_keys_set - valid_ai21_keys_set - valid_makersuite_keys_set - valid_aws_keys_set - valid_azure_keys_set - valid_vertexai_keys_set - valid_mistral_keys_set - valid_openrouter_keys_set - valid_elevenlabs_set - valid_deepseek_set invalid_keys_len = len(invalid_keys) + len(cloned_keys) if cloned_keys else len(invalid_keys) if invalid_keys_len < 1: return @@ -327,6 +343,7 @@ def output_keys(): valid_mistral_keys = [] valid_openrouter_keys = [] valid_elevenlabs_keys = [] + valid_deepseek_keys = [] for key in api_keys: if key.provider == Provider.OPENAI: @@ -349,6 +366,8 @@ def output_keys(): valid_openrouter_keys.append(key) elif key.provider == Provider.ELEVENLABS: valid_elevenlabs_keys.append(key) + elif key.provider == Provider.DEEPSEEK: + valid_deepseek_keys.append(key) if should_write: output_filename = "key_snapshots.txt" @@ -360,7 +379,7 @@ def output_keys(): print(f"Key snapshot from {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print("#" * 90) print(f'\n--- Checked {len(inputted_keys)} keys | {invalid_keys} were invalid ---') - get_invalid_keys(valid_oai_keys, valid_anthropic_keys, valid_ai21_keys, valid_makersuite_keys, valid_aws_keys, valid_azure_keys, valid_vertexai_keys, valid_mistral_keys, valid_openrouter_keys, valid_elevenlabs_keys) + get_invalid_keys(valid_oai_keys, valid_anthropic_keys, valid_ai21_keys, valid_makersuite_keys, valid_aws_keys, valid_azure_keys, valid_vertexai_keys, valid_mistral_keys, valid_openrouter_keys, valid_elevenlabs_keys, valid_deepseek_keys) print() if valid_oai_keys: pretty_print_oai_keys(valid_oai_keys, cloned_keys) @@ -382,6 +401,8 @@ def output_keys(): pretty_print_openrouter_keys(valid_openrouter_keys) if valid_elevenlabs_keys: pretty_print_elevenlabs_keys(valid_elevenlabs_keys) + if valid_deepseek_keys: + pretty_print_deepseek_keys(valid_deepseek_keys) else: print("OPENAI_KEY=" + ','.join(key.api_key for key in valid_oai_keys if key.has_quota)) print("ANTHROPIC_KEY=" + ','.join(key.api_key for key in valid_anthropic_keys if key.has_quota))