gpt4free/g4f/Provider/not_working/PuterJS.py
kqlio67 4f2bf3048b refactor: remove deprecated providers, rename LMArenaProvider, update LMArena and models
- Deleted multiple deprecated providers including Acytoo, AiAsk, AiService, Aibn, Aivvm, Berlin, ChatAnywhere, ChatgptDuo, CodeLinkAva, Cromicle, DfeHub, EasyChat, FakeGpt, FastGpt, Forefront, GPTalk, GeekGpt, GetGpt, H2o, Hashnode, Myshell, NoowAi, Opchatgpts, OpenAssistant, V50, Vitalentum, VoiGpt, Wewordle, Wuguokai, Ylokh, Yqcloud, and corresponding deprecated/__init__.py
- Renamed LMArenaProvider.py to LMArena.py and incorporated its functionality with enhancements, including updated model lists, aliases, comprehensive model discovery, payload building, and asynchronous generator for completions
- Removed LMArenaProvider import and added LMArena import in Provider/__init__.py
- Modified Blackbox provider:
  - Removed generate_session_data method and updated generate_session to use fixed email
    - Updated session payload usage in send request to use generate_session without email argument
      - Added asyncMode flag set to False in session payload
      - In DeepInfraChat, removed model aliases for "llama-4-maverick-17b" and "llama-4-scout-17b"
      - In PollinationsAI, updated model aliases: replaced "command-r-plus-08-2024" with "command-r-plus"; added "gpt-image" and "grok-3-mini" aliases
      - In LambdaChat, added "llama-3.3-70b" mapping to "llama3.3-70b-instruct"
      - In hf_space:
        - Deleted Qwen_QVQ_72B and Voodoohop_Flux1Schnell providers
          - Updated model_aliases in Qwen_Qwen_2_5_Max to fix model alias key from "qwen-2-5-max" to "qwen-2.5-max"
            - Changed model_aliases in StabilityAI_SD35Large from "stable-diffusion-3.5-large" to "sd-3.5-large"
              - Removed imports of deleted providers in hf_space/__init__.py and updated defaults accordingly
              - In BingCreateImages moved import to relative from .bing.create_images
              - Moved bing directory into needs_auth directory and updated imports accordingly
              - Changed PuterJS provider:
                - Changed working flag from True to False
                  - Changed return_conversation default from False to True in create_async_generator
                    - Changed yield error messages to raising exceptions for authentication and rate limits
                    - Modified models.py:
                      - Added ModelRegistry class for dynamic registration and lookup of Model instances
                        - Modified Model dataclass to auto-register instances on initialization via ModelRegistry
                          - Adjusted imports and removed PuterJS from lists of providers and best_provider assignments
                            - Replaced many references of PuterJS as best_provider with LMArena or IterListProvider including core models like gpt-3.5-turbo, gpt-4, gpt-4o, llama series, mistral, hermes, Microsoft phi, gemini, anthopic claude, cohere, qwen, deepseek, and others
                              - Fixed aliases and model names (e.g., "qwen-2-5-max" to "qwen-2.5-max")
                                - Removed outdated or deprecated model definitions referencing PuterJS
                                - Updated HarProvider label to "LM Arena (Har)" from "LM Arena"
                                - Removed deprecated providers imports from Provider/__init__.py and not_working directory imports updated accordingly
                                - Various exact functions impacted: create_async_generator in providers Blackbox, LMArena, PollinationsAI, LambdaChat, PuterJS; model aliases and model definitions in models.py; Provider package __init__.py files; BingCreateImages import; and deletions of numerous deprecated providers and not_working providers.
2025-05-25 21:38:12 +03:00

1232 lines
78 KiB
Python

from __future__ import annotations
import json
import time
import re
import random
import json
import uuid
import asyncio
from typing import Optional, Dict, Any
from aiohttp import ClientSession
from ...typing import AsyncResult, Messages, MediaListType
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ...providers.response import FinishReason, JsonConversation
from ...image import to_data_uri
from ...errors import RateLimitError, ModelNotFoundError
from ... import debug
class AuthData:
"""
Stores authentication data for a specific model.
"""
def __init__(self):
self.auth_token: Optional[str] = None
self.app_token: Optional[str] = None
self.created_at: float = time.time()
self.tokens_valid: bool = False
self.rate_limited_until: float = 0
def is_valid(self, expiration_time: int) -> bool:
"""Check if the tokens are still valid based on expiration time."""
return (self.auth_token and self.app_token and
self.tokens_valid and
time.time() - self.created_at < expiration_time)
def invalidate(self):
"""Mark tokens as invalid."""
self.tokens_valid = False
def set_rate_limit(self, seconds: int = 60):
"""Set rate limit expiry time."""
self.rate_limited_until = time.time() + seconds
def is_rate_limited(self) -> bool:
"""Check if currently rate limited."""
return time.time() < self.rate_limited_until
class Conversation(JsonConversation):
"""
Stores conversation state and authentication tokens for PuterJS provider.
Maintains separate authentication data for different models.
"""
message_history: Messages = []
def __init__(self, model: str):
self.model = model
self.message_history = []
# Authentication tokens by specific model
self._auth_data: Dict[str, AuthData] = {}
def get_auth_for_model(self, model: str, provider: PuterJS) -> AuthData:
"""Get the authentication data for a specific model."""
# Create auth data for this model if it doesn't exist
if model not in self._auth_data:
self._auth_data[model] = AuthData()
return self._auth_data[model]
# Override get_dict to exclude auth_data
def get_dict(self) -> Dict[str, Any]:
"""Return a dictionary representation for JSON serialization."""
return {
"model": self.model,
"message_history": self.message_history
}
# Override __getstate__ for pickle serialization
def __getstate__(self) -> Dict[str, Any]:
"""Support for pickle serialization."""
# Include auth_data for pickle serialization
state = self.__dict__.copy()
return state
# Override __setstate__ for pickle deserialization
def __setstate__(self, state: Dict[str, Any]) -> None:
"""Support for pickle deserialization."""
self.__dict__.update(state)
class PuterJS(AsyncGeneratorProvider, ProviderModelMixin):
label = "Puter.js"
url = "https://docs.puter.com/playground"
api_endpoint = "https://api.puter.com/drivers/call"
working = False
needs_auth = False
supports_stream = True
supports_system_message = True
supports_message_history = True
default_model = 'gpt-4o'
default_vision_model = default_model
# https://api.puter.com/puterai/chat/models/
openai_models = [default_vision_model,"gpt-4o-mini", "o1", "o1-mini", "o1-pro", "o3", "o3-mini", "o4-mini", "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", "gpt-4.5-preview"]
claude_models = ["claude-3-7-sonnet-20250219", "claude-3-7-sonnet-latest", "claude-3-5-sonnet-20241022", "claude-3-5-sonnet-latest", "claude-3-5-sonnet-20240620", "claude-3-haiku-20240307"]
mistral_models = ["ministral-3b-2410","ministral-3b-latest","ministral-8b-2410","ministral-8b-latest","open-mistral-7b","mistral-tiny","mistral-tiny-2312","open-mixtral-8x7b","mistral-small","mistral-small-2312","open-mixtral-8x22b","open-mixtral-8x22b-2404","mistral-large-2411","mistral-large-latest","pixtral-large-2411","pixtral-large-latest","mistral-large-pixtral-2411","codestral-2501","codestral-latest","codestral-2412","codestral-2411-rc5","pixtral-12b-2409","pixtral-12b","pixtral-12b-latest","mistral-small-2503","mistral-small-latest"]
xai_models = ["grok-beta", "grok-vision-beta"]
deepseek_models = ["deepseek-chat","deepseek-reasoner"]
gemini_models = ["gemini-1.5-flash","gemini-2.0-flash"]
openrouter_models = ["openrouter:meta-llama/llama-3.3-8b-instruct:free","openrouter:nousresearch/deephermes-3-mistral-24b-preview:free","openrouter:mistralai/mistral-medium-3","openrouter:google/gemini-2.5-pro-preview","openrouter:arcee-ai/caller-large","openrouter:arcee-ai/spotlight","openrouter:arcee-ai/maestro-reasoning","openrouter:arcee-ai/virtuoso-large","openrouter:arcee-ai/coder-large","openrouter:arcee-ai/virtuoso-medium-v2","openrouter:arcee-ai/arcee-blitz","openrouter:microsoft/phi-4-reasoning-plus:free","openrouter:microsoft/phi-4-reasoning-plus","openrouter:microsoft/phi-4-reasoning:free","openrouter:qwen/qwen3-0.6b-04-28:free","openrouter:inception/mercury-coder-small-beta","openrouter:qwen/qwen3-1.7b:free","openrouter:qwen/qwen3-4b:free","openrouter:opengvlab/internvl3-14b:free","openrouter:opengvlab/internvl3-2b:free","openrouter:deepseek/deepseek-prover-v2:free","openrouter:deepseek/deepseek-prover-v2","openrouter:meta-llama/llama-guard-4-12b","openrouter:qwen/qwen3-30b-a3b:free","openrouter:qwen/qwen3-30b-a3b","openrouter:qwen/qwen3-8b:free","openrouter:qwen/qwen3-8b","openrouter:qwen/qwen3-14b:free","openrouter:qwen/qwen3-14b","openrouter:qwen/qwen3-32b:free","openrouter:qwen/qwen3-32b","openrouter:qwen/qwen3-235b-a22b:free","openrouter:qwen/qwen3-235b-a22b","openrouter:tngtech/deepseek-r1t-chimera:free","openrouter:thudm/glm-z1-rumination-32b","openrouter:thudm/glm-z1-9b:free","openrouter:thudm/glm-4-9b:free","openrouter:microsoft/mai-ds-r1:free","openrouter:thudm/glm-z1-32b:free","openrouter:thudm/glm-z1-32b","openrouter:thudm/glm-4-32b:free","openrouter:thudm/glm-4-32b","openrouter:google/gemini-2.5-flash-preview","openrouter:google/gemini-2.5-flash-preview:thinking","openrouter:openai/o4-mini-high","openrouter:openai/o3","openrouter:openai/o4-mini","openrouter:shisa-ai/shisa-v2-llama3.3-70b:free","openrouter:qwen/qwen2.5-coder-7b-instruct","openrouter:openai/gpt-4.1","openrouter:openai/gpt-4.1-mini","openrouter:openai/gpt-4.1-nano","openrouter:eleutherai/llemma_7b","openrouter:alfredpros/codellama-7b-instruct-solidity","openrouter:arliai/qwq-32b-arliai-rpr-v1:free","openrouter:agentica-org/deepcoder-14b-preview:free","openrouter:moonshotai/kimi-vl-a3b-thinking:free","openrouter:x-ai/grok-3-mini-beta","openrouter:x-ai/grok-3-beta","openrouter:nvidia/llama-3.3-nemotron-super-49b-v1:free","openrouter:nvidia/llama-3.3-nemotron-super-49b-v1","openrouter:nvidia/llama-3.1-nemotron-ultra-253b-v1:free","openrouter:meta-llama/llama-4-maverick:free","openrouter:meta-llama/llama-4-maverick","openrouter:meta-llama/llama-4-scout:free","openrouter:meta-llama/llama-4-scout","openrouter:all-hands/openhands-lm-32b-v0.1","openrouter:mistral/ministral-8b","openrouter:deepseek/deepseek-v3-base:free","openrouter:scb10x/llama3.1-typhoon2-8b-instruct","openrouter:scb10x/llama3.1-typhoon2-70b-instruct","openrouter:bytedance-research/ui-tars-72b:free","openrouter:qwen/qwen2.5-vl-3b-instruct:free","openrouter:google/gemini-2.5-pro-exp-03-25","openrouter:qwen/qwen2.5-vl-32b-instruct:free","openrouter:qwen/qwen2.5-vl-32b-instruct","openrouter:deepseek/deepseek-chat-v3-0324:free","openrouter:deepseek/deepseek-chat-v3-0324","openrouter:featherless/qwerky-72b:free","openrouter:openai/o1-pro","openrouter:mistralai/mistral-small-3.1-24b-instruct:free","openrouter:mistralai/mistral-small-3.1-24b-instruct","openrouter:open-r1/olympiccoder-32b:free","openrouter:google/gemma-3-1b-it:free","openrouter:google/gemma-3-4b-it:free","openrouter:google/gemma-3-4b-it","openrouter:ai21/jamba-1.6-large","openrouter:ai21/jamba-1.6-mini","openrouter:google/gemma-3-12b-it:free","openrouter:google/gemma-3-12b-it","openrouter:cohere/command-a","openrouter:openai/gpt-4o-mini-search-preview","openrouter:openai/gpt-4o-search-preview","openrouter:rekaai/reka-flash-3:free","openrouter:google/gemma-3-27b-it:free","openrouter:google/gemma-3-27b-it","openrouter:thedrummer/anubis-pro-105b-v1","openrouter:thedrummer/skyfall-36b-v2","openrouter:microsoft/phi-4-multimodal-instruct","openrouter:perplexity/sonar-reasoning-pro","openrouter:perplexity/sonar-pro","openrouter:perplexity/sonar-deep-research","openrouter:deepseek/deepseek-r1-zero:free","openrouter:qwen/qwq-32b:free","openrouter:qwen/qwq-32b","openrouter:moonshotai/moonlight-16b-a3b-instruct:free","openrouter:nousresearch/deephermes-3-llama-3-8b-preview:free","openrouter:openai/gpt-4.5-preview","openrouter:google/gemini-2.0-flash-lite-001","openrouter:anthropic/claude-3.7-sonnet","openrouter:anthropic/claude-3.7-sonnet:thinking","openrouter:anthropic/claude-3.7-sonnet:beta","openrouter:perplexity/r1-1776","openrouter:mistralai/mistral-saba","openrouter:cognitivecomputations/dolphin3.0-r1-mistral-24b:free","openrouter:cognitivecomputations/dolphin3.0-mistral-24b:free","openrouter:meta-llama/llama-guard-3-8b","openrouter:openai/o3-mini-high","openrouter:deepseek/deepseek-r1-distill-llama-8b","openrouter:google/gemini-2.0-flash-001","openrouter:qwen/qwen-vl-plus","openrouter:aion-labs/aion-1.0","openrouter:aion-labs/aion-1.0-mini","openrouter:aion-labs/aion-rp-llama-3.1-8b","openrouter:qwen/qwen-vl-max","openrouter:qwen/qwen-turbo","openrouter:qwen/qwen2.5-vl-72b-instruct:free","openrouter:qwen/qwen2.5-vl-72b-instruct","openrouter:qwen/qwen-plus","openrouter:qwen/qwen-max","openrouter:openai/o3-mini","openrouter:deepseek/deepseek-r1-distill-qwen-1.5b","openrouter:mistralai/mistral-small-24b-instruct-2501:free","openrouter:mistralai/mistral-small-24b-instruct-2501","openrouter:deepseek/deepseek-r1-distill-qwen-32b:free","openrouter:deepseek/deepseek-r1-distill-qwen-32b","openrouter:deepseek/deepseek-r1-distill-qwen-14b:free","openrouter:deepseek/deepseek-r1-distill-qwen-14b","openrouter:perplexity/sonar-reasoning","openrouter:perplexity/sonar","openrouter:liquid/lfm-7b","openrouter:liquid/lfm-3b","openrouter:deepseek/deepseek-r1-distill-llama-70b:free","openrouter:deepseek/deepseek-r1-distill-llama-70b","openrouter:deepseek/deepseek-r1:free","openrouter:deepseek/deepseek-r1","openrouter:minimax/minimax-01","openrouter:mistralai/codestral-2501","openrouter:microsoft/phi-4","openrouter:deepseek/deepseek-chat:free","openrouter:deepseek/deepseek-chat","openrouter:sao10k/l3.3-euryale-70b","openrouter:openai/o1","openrouter:eva-unit-01/eva-llama-3.33-70b","openrouter:x-ai/grok-2-vision-1212","openrouter:x-ai/grok-2-1212","openrouter:cohere/command-r7b-12-2024","openrouter:google/gemini-2.0-flash-exp:free","openrouter:meta-llama/llama-3.3-70b-instruct:free","openrouter:meta-llama/llama-3.3-70b-instruct","openrouter:amazon/nova-lite-v1","openrouter:amazon/nova-micro-v1","openrouter:amazon/nova-pro-v1","openrouter:qwen/qwq-32b-preview","openrouter:eva-unit-01/eva-qwen-2.5-72b","openrouter:openai/gpt-4o-2024-11-20","openrouter:mistralai/mistral-large-2411","openrouter:mistralai/mistral-large-2407","openrouter:mistralai/pixtral-large-2411","openrouter:x-ai/grok-vision-beta","openrouter:infermatic/mn-inferor-12b","openrouter:qwen/qwen-2.5-coder-32b-instruct:free","openrouter:qwen/qwen-2.5-coder-32b-instruct","openrouter:raifle/sorcererlm-8x22b","openrouter:eva-unit-01/eva-qwen-2.5-32b","openrouter:thedrummer/unslopnemo-12b","openrouter:anthropic/claude-3.5-haiku:beta","openrouter:anthropic/claude-3.5-haiku","openrouter:anthropic/claude-3.5-haiku-20241022:beta","openrouter:anthropic/claude-3.5-haiku-20241022","openrouter:neversleep/llama-3.1-lumimaid-70b","openrouter:anthracite-org/magnum-v4-72b","openrouter:anthropic/claude-3.5-sonnet:beta","openrouter:anthropic/claude-3.5-sonnet","openrouter:x-ai/grok-beta","openrouter:mistralai/ministral-8b","openrouter:mistralai/ministral-3b","openrouter:qwen/qwen-2.5-7b-instruct:free","openrouter:qwen/qwen-2.5-7b-instruct","openrouter:nvidia/llama-3.1-nemotron-70b-instruct","openrouter:inflection/inflection-3-productivity","openrouter:inflection/inflection-3-pi","openrouter:google/gemini-flash-1.5-8b","openrouter:thedrummer/rocinante-12b","openrouter:anthracite-org/magnum-v2-72b","openrouter:liquid/lfm-40b","openrouter:meta-llama/llama-3.2-3b-instruct:free","openrouter:meta-llama/llama-3.2-3b-instruct","openrouter:meta-llama/llama-3.2-1b-instruct:free","openrouter:meta-llama/llama-3.2-1b-instruct","openrouter:meta-llama/llama-3.2-90b-vision-instruct","openrouter:meta-llama/llama-3.2-11b-vision-instruct:free","openrouter:meta-llama/llama-3.2-11b-vision-instruct","openrouter:qwen/qwen-2.5-72b-instruct:free","openrouter:qwen/qwen-2.5-72b-instruct","openrouter:neversleep/llama-3.1-lumimaid-8b","openrouter:openai/o1-preview","openrouter:openai/o1-preview-2024-09-12","openrouter:openai/o1-mini","openrouter:openai/o1-mini-2024-09-12","openrouter:mistralai/pixtral-12b","openrouter:cohere/command-r-plus-08-2024","openrouter:cohere/command-r-08-2024","openrouter:qwen/qwen-2.5-vl-7b-instruct:free","openrouter:qwen/qwen-2.5-vl-7b-instruct","openrouter:sao10k/l3.1-euryale-70b","openrouter:microsoft/phi-3.5-mini-128k-instruct","openrouter:nousresearch/hermes-3-llama-3.1-70b","openrouter:nousresearch/hermes-3-llama-3.1-405b","openrouter:openai/chatgpt-4o-latest","openrouter:sao10k/l3-lunaris-8b","openrouter:aetherwiing/mn-starcannon-12b","openrouter:openai/gpt-4o-2024-08-06","openrouter:meta-llama/llama-3.1-405b:free","openrouter:meta-llama/llama-3.1-405b","openrouter:nothingiisreal/mn-celeste-12b","openrouter:perplexity/llama-3.1-sonar-small-128k-online","openrouter:perplexity/llama-3.1-sonar-large-128k-online","openrouter:meta-llama/llama-3.1-8b-instruct:free","openrouter:meta-llama/llama-3.1-8b-instruct","openrouter:meta-llama/llama-3.1-405b-instruct","openrouter:meta-llama/llama-3.1-70b-instruct","openrouter:mistralai/codestral-mamba","openrouter:mistralai/mistral-nemo:free","openrouter:mistralai/mistral-nemo","openrouter:openai/gpt-4o-mini","openrouter:openai/gpt-4o-mini-2024-07-18","openrouter:google/gemma-2-27b-it","openrouter:alpindale/magnum-72b","openrouter:google/gemma-2-9b-it:free","openrouter:google/gemma-2-9b-it","openrouter:01-ai/yi-large","openrouter:ai21/jamba-instruct","openrouter:anthropic/claude-3.5-sonnet-20240620:beta","openrouter:anthropic/claude-3.5-sonnet-20240620","openrouter:sao10k/l3-euryale-70b","openrouter:cognitivecomputations/dolphin-mixtral-8x22b","openrouter:qwen/qwen-2-72b-instruct","openrouter:mistralai/mistral-7b-instruct:free","openrouter:mistralai/mistral-7b-instruct","openrouter:nousresearch/hermes-2-pro-llama-3-8b","openrouter:mistralai/mistral-7b-instruct-v0.3","openrouter:microsoft/phi-3-mini-128k-instruct","openrouter:microsoft/phi-3-medium-128k-instruct","openrouter:neversleep/llama-3-lumimaid-70b","openrouter:deepseek/deepseek-coder","openrouter:google/gemini-flash-1.5","openrouter:openai/gpt-4o","openrouter:openai/gpt-4o:extended","openrouter:meta-llama/llama-guard-2-8b","openrouter:openai/gpt-4o-2024-05-13","openrouter:allenai/olmo-7b-instruct","openrouter:neversleep/llama-3-lumimaid-8b:extended","openrouter:neversleep/llama-3-lumimaid-8b","openrouter:sao10k/fimbulvetr-11b-v2","openrouter:meta-llama/llama-3-8b-instruct","openrouter:meta-llama/llama-3-70b-instruct","openrouter:mistralai/mixtral-8x22b-instruct","openrouter:microsoft/wizardlm-2-8x22b","openrouter:google/gemini-pro-1.5","openrouter:openai/gpt-4-turbo","openrouter:cohere/command-r-plus","openrouter:cohere/command-r-plus-04-2024","openrouter:sophosympatheia/midnight-rose-70b","openrouter:cohere/command","openrouter:cohere/command-r","openrouter:anthropic/claude-3-haiku:beta","openrouter:anthropic/claude-3-haiku","openrouter:anthropic/claude-3-opus:beta","openrouter:anthropic/claude-3-opus","openrouter:anthropic/claude-3-sonnet:beta","openrouter:anthropic/claude-3-sonnet","openrouter:cohere/command-r-03-2024","openrouter:mistralai/mistral-large","openrouter:openai/gpt-3.5-turbo-0613","openrouter:openai/gpt-4-turbo-preview","openrouter:nousresearch/nous-hermes-2-mixtral-8x7b-dpo","openrouter:mistralai/mistral-medium","openrouter:mistralai/mistral-small","openrouter:mistralai/mistral-tiny","openrouter:mistralai/mistral-7b-instruct-v0.2","openrouter:mistralai/mixtral-8x7b-instruct","openrouter:neversleep/noromaid-20b","openrouter:anthropic/claude-2.1:beta","openrouter:anthropic/claude-2.1","openrouter:anthropic/claude-2:beta","openrouter:anthropic/claude-2","openrouter:undi95/toppy-m-7b","openrouter:alpindale/goliath-120b","openrouter:openrouter/auto","openrouter:openai/gpt-3.5-turbo-1106","openrouter:openai/gpt-4-1106-preview","openrouter:jondurbin/airoboros-l2-70b","openrouter:openai/gpt-3.5-turbo-instruct","openrouter:mistralai/mistral-7b-instruct-v0.1","openrouter:pygmalionai/mythalion-13b","openrouter:openai/gpt-3.5-turbo-16k","openrouter:openai/gpt-4-32k","openrouter:openai/gpt-4-32k-0314","openrouter:mancer/weaver","openrouter:anthropic/claude-2.0:beta","openrouter:anthropic/claude-2.0","openrouter:undi95/remm-slerp-l2-13b","openrouter:gryphe/mythomax-l2-13b","openrouter:meta-llama/llama-2-70b-chat","openrouter:openai/gpt-3.5-turbo","openrouter:openai/gpt-3.5-turbo-0125","openrouter:openai/gpt-4","openrouter:openai/gpt-4-0314"]
vision_models = [*openai_models, *claude_models, *mistral_models, *xai_models, *deepseek_models, *gemini_models, *openrouter_models]
models = vision_models
model_aliases = {
### mistral_models ###
"mixtral-8x22b": ["open-mixtral-8x22b", "open-mixtral-8x22b-2404"],
"pixtral-large": ["pixtral-large-2411","pixtral-large-latest", "mistral-large-pixtral-2411"],
### openrouter_models ###
# llama
"llama-2-70b": "openrouter:meta-llama/llama-2-70b-chat",
"llama-3-8b": "openrouter:meta-llama/llama-3-8b-instruct",
"llama-3-70b": "openrouter:meta-llama/llama-3-70b-instruct",
"llama-3.1-8b": ["openrouter:meta-llama/llama-3.1-8b-instruct:free", "openrouter:meta-llama/llama-3.1-8b-instruct"],
"llama-3.1-70b": "openrouter:meta-llama/llama-3.1-70b-instruct",
"llama-3.1-405b": ["openrouter:meta-llama/llama-3.1-405b:free", "openrouter:meta-llama/llama-3.1-405b", "openrouter:meta-llama/llama-3.1-405b-instruct"],
"llama-3.2-1b": ["openrouter:meta-llama/llama-3.2-1b-instruct:free", "openrouter:meta-llama/llama-3.2-1b-instruct"],
"llama-3.2-3b": ["openrouter:meta-llama/llama-3.2-3b-instruct:free","openrouter:meta-llama/llama-3.2-3b-instruct"],
"llama-3.2-11b": ["openrouter:meta-llama/llama-3.2-11b-vision-instruct:free", "openrouter:meta-llama/llama-3.2-11b-vision-instruct"],
"llama-3.2-90b": "openrouter:meta-llama/llama-3.2-90b-vision-instruct",
"llama-3.3-8b": "openrouter:meta-llama/llama-3.3-8b-instruct:free",
"llama-3.3-70b": ["openrouter:meta-llama/llama-3.3-70b-instruct:free", "openrouter:meta-llama/llama-3.3-70b-instruct"],
"llama-4-maverick": ["openrouter:meta-llama/llama-4-maverick:free", "openrouter:meta-llama/llama-4-maverick"],
"llama-4-scout": ["openrouter:meta-llama/llama-4-scout:free", "openrouter:meta-llama/llama-4-scout"],
#"": "openrouter:meta-llama/llama-guard-3-8b",
#"": "openrouter:meta-llama/llama-guard-2-8b",
#"": "openrouter:meta-llama/llama-guard-4-12b",
# google (gemini)
"gemini-1.5-flash": ["gemini-1.5-flash", "openrouter:google/gemini-flash-1.5", "gemini-flash-1.5-8b"],
"gemini-1.5-8b-flash": "openrouter:google/gemini-flash-1.5-8b",
"gemini-1.5-pro": "openrouter:google/gemini-pro-1.5",
"gemini-2.0-flash": ["gemini-2.0-flash", "openrouter:google/gemini-2.0-flash-lite-001", "openrouter:google/gemini-2.0-flash-001", "openrouter:google/gemini-2.0-flash-exp:free"],
"gemini-2.5-pro": ["openrouter:google/gemini-2.5-pro-preview", "openrouter:google/gemini-2.5-pro-exp-03-25"],
"gemini-2.5-flash": "openrouter:google/gemini-2.5-flash-preview",
"gemini-2.5-flash-thinking": "openrouter:google/gemini-2.5-flash-preview:thinking",
# google (gemma)
"gemma-2-9b": ["openrouter:google/gemma-2-9b-it:free","openrouter:google/gemma-2-9b-it"],
"gemma-2-27b": "openrouter:google/gemma-2-27b-it",
"gemma-3-1b": "openrouter:google/gemma-3-1b-it:free",
"gemma-3-4b": ["openrouter:google/gemma-3-4b-it:free", "openrouter:google/gemma-3-4b-it"],
"gemma-3-12b": ["openrouter:google/gemma-3-12b-it:free", "openrouter:google/gemma-3-12b-it"],
"gemma-3-27b": ["openrouter:google/gemma-3-27b-it:free", "openrouter:google/gemma-3-27b-it"],
# openai (gpt-3.5)
"gpt-3.5-turbo": ["openrouter:openai/gpt-3.5-turbo-0613", "openrouter:openai/gpt-3.5-turbo-1106", "openrouter:openai/gpt-3.5-turbo-0125", "openrouter:openai/gpt-3.5-turbo", "openrouter:openai/gpt-3.5-turbo-instruct", "openrouter:openai/gpt-3.5-turbo-16k"],
# openai (gpt-4)
"gpt-4": ["openrouter:openai/gpt-4-1106-preview", "openrouter:openai/gpt-4-32k", "openrouter:openai/gpt-4-32k-0314", "openrouter:openai/gpt-4", "openrouter:openai/gpt-4-0314",],
"gpt-4-turbo": ["openrouter:openai/gpt-4-turbo", "openrouter:openai/gpt-4-turbo-preview"],
# openai (gpt-4o)
"gpt-4o": ["gpt-4o", "openrouter:openai/gpt-4o-2024-08-06", "openrouter:openai/gpt-4o-2024-11-20", "openrouter:openai/chatgpt-4o-latest", "openrouter:openai/gpt-4o", "openrouter:openai/gpt-4o:extended", "openrouter:openai/gpt-4o-2024-05-13",],
"gpt-4o-search": "openrouter:openai/gpt-4o-search-preview",
"gpt-4o-mini": ["gpt-4o-mini", "openrouter:openai/gpt-4o-mini", "openrouter:openai/gpt-4o-mini-2024-07-18"],
"gpt-4o-mini-search": "openrouter:openai/gpt-4o-mini-search-preview",
# openai (o1)
"o1": ["o1", "openrouter:openai/o1", "openrouter:openai/o1-preview", "openrouter:openai/o1-preview-2024-09-12"],
"o1-mini": ["o1-mini", "openrouter:openai/o1-mini", "openrouter:openai/o1-mini-2024-09-12"],
"o1-pro": ["o1-pro", "openrouter:openai/o1-pro"],
# openai (o3)
"o3": ["o3", "openrouter:openai/o3"],
"o3-mini": ["o3-mini", "openrouter:openai/o3-mini", "openrouter:openai/o3-mini-high"],
"o3-mini-high": "openrouter:openai/o3-mini-high",
# openai (o4)
"o4-mini": ["o4-mini", "openrouter:openai/o4-mini"],
"o4-mini-high": "openrouter:openai/o4-mini-high",
# openai (gpt-4.1)
"gpt-4.1": ["gpt-4.1", "openrouter:openai/gpt-4.1"],
"gpt-4.1-mini": ["gpt-4.1-mini", "openrouter:openai/gpt-4.1-mini"],
"gpt-4.1-nano": ["gpt-4.1-nano", "openrouter:openai/gpt-4.1-nano"],
# openai (gpt-4.5)
"gpt-4.5": ["gpt-4.5-preview", "openrouter:openai/gpt-4.5-preview"],
# mistralai
"mistral-large": ["openrouter:mistralai/mistral-large", "openrouter:mistralai/mistral-large-2411", "openrouter:mistralai/mistral-large-2407", "openrouter:mistralai/pixtral-large-2411"],
"mistral-medium": ["openrouter:mistralai/mistral-medium", "openrouter:mistralai/mistral-medium-3"],
"mistral-small": ["mistral-small", "mistral-small-2312", "mistral-small-2503","mistral-small-latest", "openrouter:mistralai/mistral-small", "openrouter:mistralai/mistral-small-3.1-24b-instruct:free", "openrouter:mistralai/mistral-small-3.1-24b-instruct", "openrouter:mistralai/mistral-small-24b-instruct-2501:free", "openrouter:mistralai/mistral-small-24b-instruct-2501"],
"mistral-tiny": ["mistral-tiny", "mistral-tiny-2312", "openrouter:mistralai/mistral-tiny"],
"mistral-7b": ["open-mistral-7b", "openrouter:mistralai/mistral-7b-instruct", "openrouter:mistralai/mistral-7b-instruct:free", "openrouter:mistralai/mistral-7b-instruct-v0.1", "openrouter:mistralai/mistral-7b-instruct-v0.2", "openrouter:mistralai/mistral-7b-instruct-v0.3",],
"mixtral-8x7b": ["open-mixtral-8x7b", "openrouter:mistralai/mixtral-8x7b-instruct"],
"mixtral-8x22b": ["open-mixtral-8x22b", "open-mixtral-8x22b-2404", "openrouter:mistralai/mixtral-8x7b-instruct", "openrouter:mistralai/mixtral-8x22b-instruct"],
"ministral-8b": ["ministral-8b-2410", "ministral-8b-latest", "openrouter:mistral/ministral-8b", "openrouter:mistralai/ministral-8b"],
"mistral-nemo": ["openrouter:mistralai/mistral-nemo:free", "openrouter:mistralai/mistral-nemo"],
"ministral-3b": ["ministral-3b-2410", "ministral-3b-latest", "openrouter:mistralai/ministral-3b"],
"mistral-saba": "openrouter:mistralai/mistral-saba",
"codestral": ["codestral-2501","codestral-latest","codestral-2412","codestral-2411-rc5", "openrouter:mistralai/codestral-2501", "openrouter:mistralai/codestral-mamba"],
"pixtral-12b": ["pixtral-12b-2409","pixtral-12b","pixtral-12b-latest", "openrouter:mistralai/pixtral-12b"],
# nousresearch
"hermes-2-dpo": "openrouter:nousresearch/nous-hermes-2-mixtral-8x7b-dpo",
"hermes-2-pro": "openrouter:nousresearch/hermes-2-pro-llama-3-8b",
"hermes-3-70b": "openrouter:nousresearch/hermes-3-llama-3.1-70b",
"hermes-3-405b": "openrouter:nousresearch/hermes-3-llama-3.1-405b",
"deephermes-3-8b": "openrouter:nousresearch/deephermes-3-llama-3-8b-preview:free",
"deephermes-3-24b": "openrouter:nousresearch/deephermes-3-mistral-24b-preview:free",
# microsoft
"phi-3-mini": "openrouter:microsoft/phi-3-mini-128k-instruct",
"phi-3-medium": "openrouter:microsoft/phi-3-medium-128k-instruct",
"phi-3.5-mini": "openrouter:microsoft/phi-3.5-mini-128k-instruct",
"phi-4": "openrouter:microsoft/phi-4",
"phi-4-multimodal": "openrouter:microsoft/phi-4-multimodal-instruct",
"phi-4-reasoning": "openrouter:microsoft/phi-4-reasoning:free",
"phi-4-reasoning-plus": ["openrouter:microsoft/phi-4-reasoning-plus:free", "openrouter:microsoft/phi-4-reasoning-plus"],
"wizardlm-2-8x22b": "openrouter:microsoft/wizardlm-2-8x22b",
"mai-ds-r1": "openrouter:microsoft/mai-ds-r1:free",
# anthropic
"claude-3.7-sonnet": ["claude-3-7-sonnet-20250219", "claude-3-7-sonnet-latest", "openrouter:anthropic/claude-3.7-sonnet", "openrouter:anthropic/claude-3.7-sonnet:beta",],
"claude-3.7-sonnet-thinking": "openrouter:anthropic/claude-3.7-sonnet:thinking",
"claude-3.5-haiku": ["openrouter:anthropic/claude-3.5-haiku:beta", "openrouter:anthropic/claude-3.5-haiku", "openrouter:anthropic/claude-3.5-haiku-20241022:beta", "openrouter:anthropic/claude-3.5-haiku-20241022"],
"claude-3.5-sonnet": ["claude-3-5-sonnet-20241022", "claude-3-5-sonnet-latest", "claude-3-5-sonnet-20240620", "openrouter:anthropic/claude-3.5-sonnet-20240620:beta", "openrouter:anthropic/claude-3.5-sonnet-20240620", "openrouter:anthropic/claude-3.5-sonnet:beta", "openrouter:anthropic/claude-3.5-sonnet",],
"claude-3-haiku": ["claude-3-haiku-20240307", "openrouter:anthropic/claude-3-haiku:beta", "openrouter:anthropic/claude-3-haiku"],
"claude-3-opus": ["openrouter:anthropic/claude-3-opus:beta", "openrouter:anthropic/claude-3-opus"],
"claude-3-sonnet": ["openrouter:anthropic/claude-3-sonnet:beta", "openrouter:anthropic/claude-3-sonnet"],
"claude-2.1": ["openrouter:anthropic/claude-2.1:beta", "openrouter:anthropic/claude-2.1"],
"claude-2": ["openrouter:anthropic/claude-2:beta", "openrouter:anthropic/claude-2",],
"claude-2.0": ["openrouter:anthropic/claude-2.0:beta", "openrouter:anthropic/claude-2.0"],
# rekaai
"reka-flash": "openrouter:rekaai/reka-flash-3:free",
# cohere
"command-r7b": "openrouter:cohere/command-r7b-12-2024",
"command-r-plus": ["openrouter:cohere/command-r-plus-08-2024", "openrouter:cohere/command-r-plus", "openrouter:cohere/command-r-plus-04-2024"],
"command": "openrouter:cohere/command",
"command-r": ["openrouter:cohere/command-r-08-2024", "openrouter:cohere/command-r", "openrouter:cohere/command-r-03-2024"],
"command-a": "openrouter:cohere/command-a",
# qwen
"qwq-32b": ["openrouter:qwen/qwq-32b-preview", "openrouter:qwen/qwq-32b:free", "openrouter:qwen/qwq-32b"],
"qwen-vl-plus": "openrouter:qwen/qwen-vl-plus",
"qwen-vl-max": "openrouter:qwen/qwen-vl-max",
"qwen-turbo": "openrouter:qwen/qwen-turbo",
"qwen-2.5-vl-72b": ["openrouter:qwen/qwen2.5-vl-72b-instruct:free", "openrouter:qwen/qwen2.5-vl-72b-instruct"],
"qwen-plus": "openrouter:qwen/qwen-plus",
"qwen-max": "openrouter:qwen/qwen-max",
"qwen-2.5-coder-32b": ["openrouter:qwen/qwen-2.5-coder-32b-instruct:free", "openrouter:qwen/qwen-2.5-coder-32b-instruct"],
"qwen-2.5-7b": ["openrouter:qwen/qwen-2.5-7b-instruct:free", "openrouter:qwen/qwen-2.5-7b-instruct"],
"qwen-2.5-72b": ["openrouter:qwen/qwen-2.5-72b-instruct:free", "openrouter:qwen/qwen-2.5-72b-instruct"],
"qwen-2.5-vl-7b": ["openrouter:qwen/qwen-2.5-vl-7b-instruct:free", "openrouter:qwen/qwen-2.5-vl-7b-instruct"],
"qwen-2-72b": "openrouter:qwen/qwen-2-72b-instruct",
"qwen-3-0.6b": "openrouter:qwen/qwen3-0.6b-04-28:free",
"qwen-3-1.7b": "openrouter:qwen/qwen3-1.7b:free",
"qwen-3-4b": "openrouter:qwen/qwen3-4b:free",
"qwen-3-30b": ["openrouter:qwen/qwen3-30b-a3b:free", "openrouter:qwen/qwen3-30b-a3b"],
"qwen-3-8b": ["openrouter:qwen/qwen3-8b:free", "openrouter:qwen/qwen3-8b"],
"qwen-3-14b": ["openrouter:qwen/qwen3-14b:free", "openrouter:qwen/qwen3-14b"],
"qwen-3-32b": ["openrouter:qwen/qwen3-32b:free", "openrouter:qwen/qwen3-32b"],
"qwen-3-235b": ["openrouter:qwen/qwen3-235b-a22b:free", "openrouter:qwen/qwen3-235b-a22b"],
"qwen-2.5-coder-7b": "openrouter:qwen/qwen2.5-coder-7b-instruct",
"qwen-2.5-vl-3b": "openrouter:qwen/qwen2.5-vl-3b-instruct:free",
"qwen-2.5-vl-32b": ["openrouter:qwen/qwen2.5-vl-32b-instruct:free", "openrouter:qwen/qwen2.5-vl-32b-instruct"],
# deepseek
"deepseek-prover-v2": ["openrouter:deepseek/deepseek-prover-v2:free", "openrouter:deepseek/deepseek-prover-v2"],
"deepseek-v3": "openrouter:deepseek/deepseek-v3-base:free",
"deepseek-v3-0324": ["deepseek-chat", "openrouter:deepseek/deepseek-chat-v3-0324:free", "openrouter:deepseek/deepseek-chat-v3-0324"],
"deepseek-r1-zero": "openrouter:deepseek/deepseek-r1-zero:free",
"deepseek-r1-distill-llama-8b": "openrouter:deepseek/deepseek-r1-distill-llama-8b",
"deepseek-r1-distill-qwen-1.5b": "openrouter:deepseek/deepseek-r1-distill-qwen-1.5b",
"deepseek-r1-distill-qwen-32b": ["openrouter:deepseek/deepseek-r1-distill-qwen-32b:free", "openrouter:deepseek/deepseek-r1-distill-qwen-32b"],
"deepseek-r1-distill-qwen-14b": ["openrouter:deepseek/deepseek-r1-distill-qwen-14b:free","openrouter:deepseek/deepseek-r1-distill-qwen-14b"],
"deepseek-r1-distill-llama-70b": ["openrouter:deepseek/deepseek-r1-distill-llama-70b:free", "openrouter:deepseek/deepseek-r1-distill-llama-70b"],
"deepseek-r1": ["deepseek-reasoner", "openrouter:deepseek/deepseek-r1:free", "openrouter:deepseek/deepseek-r1"],
"deepseek-chat": ["deepseek-chat", "openrouter:deepseek/deepseek-chat:free", "openrouter:deepseek/deepseek-chat"],
"deepseek-coder": ["openrouter:deepseek/deepseek-coder"],
# inflection
"inflection-3-productivity": "openrouter:inflection/inflection-3-productivity",
"inflection-3-pi": "openrouter:inflection/inflection-3-pi",
# x-ai
"grok-3-mini": "openrouter:x-ai/grok-3-mini-beta",
"grok-3-beta": "openrouter:x-ai/grok-3-beta",
"grok-2": ["openrouter:x-ai/grok-2-vision-1212", "openrouter:x-ai/grok-2-1212"],
"grok": ["openrouter:x-ai/grok-vision-beta", "openrouter:x-ai/grok-2-vision-1212", "openrouter:x-ai/grok-2-1212", "grok-beta","grok-vision-beta", "openrouter:x-ai/grok-beta", "openrouter:x-ai/grok-3-beta", "openrouter:x-ai/grok-3-mini-beta"],
"grok-beta": ["grok-beta","grok-vision-beta", "openrouter:x-ai/grok-beta", "openrouter:x-ai/grok-3-beta"],
# perplexity
"sonar-reasoning-pro": "openrouter:perplexity/sonar-reasoning-pro",
"sonar-pro": "openrouter:perplexity/sonar-pro",
"sonar-deep-research": "openrouter:perplexity/sonar-deep-research",
"r1-1776": "openrouter:perplexity/r1-1776",
"sonar-reasoning": "openrouter:perplexity/sonar-reasoning",
"sonar": "openrouter:perplexity/sonar",
"llama-3.1-sonar-small-online": "openrouter:perplexity/llama-3.1-sonar-small-128k-online",
"llama-3.1-sonar-large-online": "openrouter:perplexity/llama-3.1-sonar-large-128k-online",
# nvidia
"nemotron-49b": ["openrouter:nvidia/llama-3.3-nemotron-super-49b-v1:free", "openrouter:nvidia/llama-3.3-nemotron-super-49b-v1"],
"nemotron-70b": "openrouter:nvidia/llama-3.1-nemotron-70b-instruct",
"nemotron-253b": "openrouter:nvidia/llama-3.1-nemotron-ultra-253b-v1:free",
# thudm
"glm-4": ["openrouter:thudm/glm-4-32b:free", "openrouter:thudm/glm-4-32b", "openrouter:thudm/glm-4-9b:free",],
"glm-4-32b": ["openrouter:thudm/glm-4-32b:free", "openrouter:thudm/glm-4-32b"],
"glm-z1-32b": ["openrouter:thudm/glm-z1-32b:free", "openrouter:thudm/glm-z1-32b"],
"glm-4-9b": "openrouter:thudm/glm-4-9b:free",
"glm-z1-9b": "openrouter:thudm/glm-z1-9b:free",
"glm-z1-rumination-32b": "openrouter:thudm/glm-z1-rumination-32b",
# minimax
"minimax": "openrouter:minimax/minimax-01",
# cognitivecomputations
"dolphin-3.0-r1-24b": "openrouter:cognitivecomputations/dolphin3.0-r1-mistral-24b:free",
"dolphin-3.0-24b": "openrouter:cognitivecomputations/dolphin3.0-mistral-24b:free",
"dolphin-8x22b": "openrouter:cognitivecomputations/dolphin-mixtral-8x22b",
# agentica-org
"deepcoder-14b": "openrouter:agentica-org/deepcoder-14b-preview:free",
# moonshotai
"kimi-vl-thinking": "openrouter:moonshotai/kimi-vl-a3b-thinking:free",
"moonlight-16b": "openrouter:moonshotai/moonlight-16b-a3b-instruct:free",
# featherless
"qwerky-72b": "openrouter:featherless/qwerky-72b:free",
# liquid
"lfm-7b": "openrouter:liquid/lfm-7b",
"lfm-3b": "openrouter:liquid/lfm-3b",
"lfm-40b": "openrouter:liquid/lfm-40b",
# arcee-ai
#"": "openrouter:arcee-ai/caller-large",
#"": "openrouter:arcee-ai/spotlight",
#"": "openrouter:arcee-ai/maestro-reasoning",
#"": "openrouter:arcee-ai/virtuoso-large",
#"": "openrouter:arcee-ai/coder-large",
#"": "openrouter:arcee-ai/virtuoso-medium-v2",
#"": "openrouter:arcee-ai/arcee-blitz",
# inception
#"": "openrouter:inception/mercury-coder-small-beta",
# opengvlab
#"": "openrouter:opengvlab/internvl3-14b:free",
#"": "openrouter:opengvlab/internvl3-2b:free",
# tngtech
#"": "openrouter:tngtech/deepseek-r1t-chimera:free",
# shisa-ai
#"": "openrouter:shisa-ai/shisa-v2-llama3.3-70b:free",
# eleutherai
#"": "openrouter:eleutherai/llemma_7b",
# shisa-ai
#"": "openrouter:alfredpros/codellama-7b-instruct-solidity",
# arliai
#"": "openrouter:arliai/qwq-32b-arliai-rpr-v1:free",
# all-hands
#"": "openrouter:all-hands/openhands-lm-32b-v0.1",
# scb10x
#"": "openrouter:scb10x/llama3.1-typhoon2-8b-instruct",
#"": "openrouter:scb10x/llama3.1-typhoon2-70b-instruct",
# bytedance-research
#"": "openrouter:bytedance-research/ui-tars-72b:free",
# open-r1
#"": "openrouter:open-r1/olympiccoder-32b:free",
# ai21
#"": "openrouter:ai21/jamba-1.6-large",
#"": "openrouter:ai21/jamba-1.6-mini",
#"": "openrouter:ai21/jamba-instruct",
# thedrummer
#"": "openrouter:thedrummer/anubis-pro-105b-v1",
#"": "openrouter:thedrummer/skyfall-36b-v2",
#"": "openrouter:thedrummer/unslopnemo-12b",
#"": "openrouter:thedrummer/rocinante-12b",
# aion-labs
#"": "openrouter:aion-labs/aion-1.0",
#"": "openrouter:aion-labs/aion-1.0-mini",
#"": "openrouter:aion-labs/aion-rp-llama-3.1-8b",
# sao10k
#"": "openrouter:sao10k/l3.3-euryale-70b",
#"": "openrouter:sao10k/l3.1-euryale-70b",
#"": "openrouter:sao10k/l3-lunaris-8b",
#"": "openrouter:sao10k/l3-euryale-70b",
#"": "openrouter:sao10k/fimbulvetr-11b-v2",
# eva-unit-01
#"": "openrouter:eva-unit-01/eva-llama-3.33-70b",
#"": "openrouter:eva-unit-01/eva-qwen-2.5-72b",
#"": "openrouter:eva-unit-01/eva-qwen-2.5-32b",
# amazon
#"": "openrouter:amazon/nova-lite-v1",
#"": "openrouter:amazon/nova-micro-v1",
#"": "openrouter:amazon/nova-pro-v1",
# infermatic
#"": "openrouter:infermatic/mn-inferor-12b",
# raifle
#"": "openrouter:raifle/sorcererlm-8x22b",
# neversleep
#"": "openrouter:neversleep/llama-3.1-lumimaid-70b",
#"": "openrouter:neversleep/llama-3.1-lumimaid-8b",
#"": "openrouter:neversleep/llama-3-lumimaid-70b",
#"": "openrouter:neversleep/llama-3-lumimaid-8b:extended",
#"": "openrouter:neversleep/llama-3-lumimaid-8b",
#"": "openrouter:neversleep/noromaid-20b",
# anthracite-org
#"": "openrouter:anthracite-org/magnum-v2-72b",
#"": "openrouter:anthracite-org/magnum-v4-72b",
# aetherwiing
#"": "openrouter:aetherwiing/mn-starcannon-12b",
# nothingiisreal
#"": "openrouter:nothingiisreal/mn-celeste-12b",
# alpindale
#"": "openrouter:alpindale/magnum-72b",
#"": "openrouter:alpindale/goliath-120b",
# 01-ai
#"": "openrouter:01-ai/yi-large",
# allenai
#"": "openrouter:allenai/olmo-7b-instruct",
# sophosympatheia
#"": "openrouter:sophosympatheia/midnight-rose-70b",
# undi95
#"": "openrouter:undi95/toppy-m-7b",
# jondurbin
#"": "openrouter:jondurbin/airoboros-l2-70b",
# pygmalionai
#"": "openrouter:pygmalionai/mythalion-13b",
# mancer
#"": "openrouter:mancer/weaver",
# undi95
#"": "openrouter:undi95/remm-slerp-l2-13b",
# gryphe
#"": "openrouter:gryphe/mythomax-l2-13b",
}
# Token expiration time in seconds (30 minutes)
TOKEN_EXPIRATION = 30 * 60
# Rate limit handling
MAX_RETRIES = 3
RETRY_DELAY = 5 # seconds
RATE_LIMIT_DELAY = 60 # seconds
# Make classes available at the class level for easier access
Conversation = Conversation
AuthData = AuthData
# Class-level auth data cache to reduce signup requests
_shared_auth_data = {}
@staticmethod
def get_driver_for_model(model: str) -> str:
"""Determine the appropriate driver based on the model name."""
if model in PuterJS.openai_models:
return "openai-completion"
elif model in PuterJS.claude_models:
return "claude"
elif model in PuterJS.mistral_models:
return "mistral"
elif model in PuterJS.xai_models:
return "xai"
elif model in PuterJS.deepseek_models:
return "deepseek"
elif model in PuterJS.gemini_models:
return "gemini"
elif model in PuterJS.openrouter_models:
return "openrouter"
else:
# Default to OpenAI for unknown models
return "openai-completion"
@staticmethod
def format_messages_with_images(messages: Messages, media: MediaListType = None) -> Messages:
"""
Format messages to include image data in the proper format for vision models.
Args:
messages: List of message dictionaries
media: List of tuples containing (image_data, image_name)
Returns:
Formatted messages with image content
"""
if not media:
return messages
# Create a copy of messages to avoid modifying the original
formatted_messages = messages.copy()
# Find the last user message to add images to
for i in range(len(formatted_messages) - 1, -1, -1):
if formatted_messages[i]["role"] == "user":
user_msg = formatted_messages[i]
# Convert to content list format if it's a string
if isinstance(user_msg["content"], str):
text_content = user_msg["content"]
user_msg["content"] = [{"type": "text", "text": text_content}]
elif not isinstance(user_msg["content"], list):
# Initialize as empty list if not already a list or string
user_msg["content"] = []
# Add image content
for image_data, image_name in media:
if isinstance(image_data, str) and (image_data.startswith("http://") or image_data.startswith("https://")):
# Direct URL
user_msg["content"].append({
"type": "image_url",
"image_url": {"url": image_data}
})
else:
# Convert to data URI
image_uri = to_data_uri(image_data, image_name)
user_msg["content"].append({
"type": "image_url",
"image_url": {"url": image_uri}
})
formatted_messages[i] = user_msg
break
return formatted_messages
@classmethod
async def _create_temporary_account(cls, session: ClientSession, proxy: str = None) -> Dict[str, str]:
"""
Create a temporary account with retry logic and rate limit handling.
Args:
session: The aiohttp ClientSession
proxy: Optional proxy URL
Returns:
Dict containing auth_token
Raises:
RateLimitError: If rate limited after retries
"""
signup_headers = {
"Content-Type": "application/json",
"host": "puter.com", # Kept the previous fix for Host header
"connection": "keep-alive",
"sec-ch-ua-platform": "macOS",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"sec-ch-ua": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"sec-ch-ua-mobile": "?0",
"accept": "*/*",
"origin": "https://puter.com",
"sec-fetch-site": "same-site",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
"referer": "https://puter.com/",
"accept-encoding": "gzip",
"accept-language": "en-US,en;q=0.9"
}
signup_data = {
"is_temp": True,
"client_id": str(uuid.uuid4()) # Changed to generate a standard UUID
}
for attempt in range(cls.MAX_RETRIES):
try:
async with session.post(
"https://puter.com/signup",
headers=signup_headers,
json=signup_data,
proxy=proxy,
timeout=30
) as signup_response:
if signup_response.status == 429:
# Rate limited, wait and retry
retry_after = int(signup_response.headers.get('Retry-After', cls.RATE_LIMIT_DELAY))
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(retry_after)
continue
else:
# from ..errors import RateLimitError (ensure this import path is correct for your project)
raise Exception(f"Rate limited by Puter.js API. Try again after {retry_after} seconds.") # Placeholder if RateLimitError not accessible
if signup_response.status != 200:
error_text = await signup_response.text()
if attempt < cls.MAX_RETRIES - 1:
# Wait before retrying
await asyncio.sleep(cls.RETRY_DELAY * (attempt + 1))
continue
else:
raise Exception(f"Failed to create temporary account. Status: {signup_response.status}, Details: {error_text}")
try:
return await signup_response.json()
except Exception as e:
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(cls.RETRY_DELAY)
continue
else:
raise Exception(f"Failed to parse signup response as JSON: {e}")
except Exception as e:
if attempt < cls.MAX_RETRIES - 1:
# Exponential backoff
await asyncio.sleep(cls.RETRY_DELAY * (2 ** attempt))
continue
else:
raise e
# Should not reach here, but just in case
raise Exception("Failed to create temporary account after multiple retries")
@classmethod
async def _get_app_token(cls, session: ClientSession, auth_token: str, proxy: str = None) -> Dict[str, str]:
"""
Get app token with retry logic and rate limit handling.
Args:
session: The aiohttp ClientSession
auth_token: The auth token from signup
proxy: Optional proxy URL
Returns:
Dict containing app_token
Raises:
RateLimitError: If rate limited after retries
"""
app_token_headers = {
"host": "api.puter.com",
"connection": "keep-alive",
"authorization": f"Bearer {auth_token}",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"accept": "*/*",
"origin": "https://puter.com",
"sec-fetch-site": "same-site",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
"referer": "https://puter.com/",
"accept-encoding": "gzip",
"accept-language": "en-US,en;q=0.9",
"content-type": "application/json"
}
# Randomize origin slightly to avoid detection
origins = ["http://docs.puter.com", "https://docs.puter.com", "https://puter.com"]
app_token_data = {"origin": random.choice(origins)}
for attempt in range(cls.MAX_RETRIES):
try:
async with session.post(
"https://api.puter.com/auth/get-user-app-token",
headers=app_token_headers,
json=app_token_data,
proxy=proxy,
timeout=30
) as app_token_response:
if app_token_response.status == 429:
# Rate limited, wait and retry
retry_after = int(app_token_response.headers.get('Retry-After', cls.RATE_LIMIT_DELAY))
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(retry_after)
continue
else:
raise RateLimitError(f"Rate limited by Puter.js API. Try again after {retry_after} seconds.")
if app_token_response.status != 200:
error_text = await app_token_response.text()
if attempt < cls.MAX_RETRIES - 1:
# Wait before retrying
await asyncio.sleep(cls.RETRY_DELAY * (attempt + 1))
continue
else:
raise Exception(f"Failed to get app token. Status: {app_token_response.status}, Details: {error_text}")
try:
return await app_token_response.json()
except Exception as e:
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(cls.RETRY_DELAY)
continue
else:
raise Exception(f"Failed to parse app token response as JSON: {e}")
except Exception as e:
if attempt < cls.MAX_RETRIES - 1:
# Exponential backoff
await asyncio.sleep(cls.RETRY_DELAY * (2 ** attempt))
continue
else:
raise e
# Should not reach here, but just in case
raise Exception("Failed to get app token after multiple retries")
@classmethod
def get_model(cls, model: str) -> str:
"""Get the internal model name from the user-provided model name."""
if not model:
return cls.default_model
# Check if the model exists directly in our models list
if model in cls.models:
return model
# Check if there's an alias for this model
if model in cls.model_aliases:
alias = cls.model_aliases[model]
# If the alias is a list, randomly select one of the options
if isinstance(alias, list):
selected_model = random.choice(alias)
debug.log(f"PuterJS: Selected model '{selected_model}' from alias '{model}'")
return selected_model
debug.log(f"PuterJS: Using model '{alias}' for alias '{model}'")
return alias
raise ModelNotFoundError(f"Model {model} not found")
@classmethod
async def create_async_generator(
cls,
model: str,
messages: Messages,
proxy: str = None,
stream: bool = True,
conversation: Optional[JsonConversation] = None,
return_conversation: bool = True,
media: MediaListType = None, # Add media parameter for images
**kwargs
) -> AsyncResult:
model = cls.get_model(model)
# Check if we need to use a vision model
has_images = False
if media is not None and len(media) > 0:
has_images = True
# If images are present and model doesn't support vision, switch to default vision model
if model not in cls.vision_models:
model = cls.default_vision_model
# Check for image URLs in messages
if not has_images:
for msg in messages:
if msg["role"] == "user":
content = msg.get("content", "")
if isinstance(content, list):
for item in content:
if item.get("type") == "image_url":
has_images = True
if model not in cls.vision_models:
model = cls.default_vision_model
break
elif isinstance(content, str):
# Check for URLs in the text
urls = re.findall(r'https?://\S+\.(jpg|jpeg|png|gif|webp)', content, re.IGNORECASE)
if urls:
has_images = True
if model not in cls.vision_models:
model = cls.default_vision_model
break
# Check if the conversation is of the correct type
if conversation is not None and not isinstance(conversation, cls.Conversation):
# Convert generic JsonConversation to our specific Conversation class
new_conversation = cls.Conversation(model)
new_conversation.message_history = conversation.message_history.copy() if hasattr(conversation, 'message_history') else messages.copy()
conversation = new_conversation
# Initialize or update conversation
if conversation is None:
conversation = cls.Conversation(model)
# Format messages with images if needed
if has_images and media:
conversation.message_history = cls.format_messages_with_images(messages, media)
else:
conversation.message_history = messages.copy()
else:
# Update message history with new messages
if has_images and media:
formatted_messages = cls.format_messages_with_images(messages, media)
for msg in formatted_messages:
if msg not in conversation.message_history:
conversation.message_history.append(msg)
else:
for msg in messages:
if msg not in conversation.message_history:
conversation.message_history.append(msg)
# Get the authentication data for this specific model
auth_data = conversation.get_auth_for_model(model, cls)
# Check if we can use shared auth data
if model in cls._shared_auth_data and cls._shared_auth_data[model].is_valid(cls.TOKEN_EXPIRATION):
# Copy shared auth data to conversation
shared_auth = cls._shared_auth_data[model]
auth_data.auth_token = shared_auth.auth_token
auth_data.app_token = shared_auth.app_token
auth_data.created_at = shared_auth.created_at
auth_data.tokens_valid = shared_auth.tokens_valid
# Check if rate limited
if auth_data.is_rate_limited():
wait_time = auth_data.rate_limited_until - time.time()
if wait_time > 0:
raise RateLimitError(f"Rate limited. Please try again in {int(wait_time)} seconds.")
async with ClientSession() as session:
# Step 1: Create a temporary account (if needed)
if not auth_data.is_valid(cls.TOKEN_EXPIRATION):
try:
# Try to authenticate
signup_data = await cls._create_temporary_account(session, proxy)
auth_data.auth_token = signup_data.get("token")
if not auth_data.auth_token:
raise RuntimeError(f"Error: No auth token in response for model {model}")
# Get app token
app_token_data = await cls._get_app_token(session, auth_data.auth_token, proxy)
auth_data.app_token = app_token_data.get("token")
if not auth_data.app_token:
raise RuntimeError(f"Error: No app token in response for model {model}")
# Mark tokens as valid
auth_data.created_at = time.time()
auth_data.tokens_valid = True
# Update shared auth data
cls._shared_auth_data[model] = auth_data
except RateLimitError as e:
# Set rate limit and inform user
auth_data.set_rate_limit(cls.RATE_LIMIT_DELAY)
raise e
except Exception as e:
raise RuntimeError(f"Error during authentication for model {model}: {str(e)}")
# Step 3: Make the chat request with proper image handling
try:
chat_headers = {
"host": "api.puter.com",
"connection": "keep-alive",
"authorization": f"Bearer {auth_data.app_token}",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"content-type": "application/json;charset=UTF-8",
# Set appropriate accept header based on stream mode
"accept": "text/event-stream" if stream else "*/*",
"origin": "http://docs.puter.com",
"sec-fetch-site": "cross-site",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
"referer": "http://docs.puter.com/",
"accept-encoding": "gzip",
"accept-language": "en-US,en;q=0.9"
}
# Determine the appropriate driver based on the model
driver = cls.get_driver_for_model(model)
# Prepare messages for the API request
processed_messages = conversation.message_history
# Special handling for direct image URLs in the HTML example
if has_images and not any(isinstance(msg.get("content"), list) for msg in processed_messages):
# This handles the case where an image URL is passed directly to puter.ai.chat()
# as in the HTML example: puter.ai.chat("What do you see?", "https://assets.puter.site/doge.jpeg")
for i, msg in enumerate(processed_messages):
if msg["role"] == "user":
# Check if there are image URLs in the media parameter
if media and len(media) > 0:
# Format with the media parameter
processed_messages = cls.format_messages_with_images([msg], media)
else:
# Check for URLs in the text content
content = msg.get("content", "")
if isinstance(content, str):
urls = re.findall(r'https?://\S+\.(jpg|jpeg|png|gif|webp)', content, re.IGNORECASE)
if urls:
# Extract URLs from the text
text_parts = []
image_urls = []
# Simple URL extraction
words = content.split()
for word in words:
if re.match(r'https?://\S+\.(jpg|jpeg|png|gif|webp)', word, re.IGNORECASE):
image_urls.append(word)
else:
text_parts.append(word)
# Create formatted message with text and images
formatted_content = []
if text_parts:
formatted_content.append({
"type": "text",
"text": " ".join(text_parts)
})
for url in image_urls:
formatted_content.append({
"type": "image_url",
"image_url": {"url": url}
})
processed_messages[i]["content"] = formatted_content
break
chat_data = {
"interface": "puter-chat-completion",
"driver": driver,
"test_mode": False,
"method": "complete",
"args": {
"messages": processed_messages,
"model": model,
"stream": stream,
"max_tokens": kwargs.get("max_tokens", 4096)
}
}
# Add any additional parameters from kwargs
for key, value in kwargs.items():
if key not in ["messages", "model", "stream", "max_tokens"]:
chat_data["args"][key] = value
# Try the chat request with retries
for attempt in range(cls.MAX_RETRIES):
try:
async with session.post(
cls.api_endpoint,
headers=chat_headers,
json=chat_data,
proxy=proxy,
timeout=120 # Longer timeout for vision requests
) as response:
if response.status == 429:
# Rate limited, set rate limit and inform user
retry_after = int(response.headers.get('Retry-After', cls.RATE_LIMIT_DELAY))
auth_data.set_rate_limit(retry_after)
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(min(retry_after, 10)) # Wait but cap at 10 seconds for retries
continue
else:
raise RateLimitError(f"Rate limited by Puter.js API. Try again after {retry_after} seconds.")
# Handle authentication errors
if response.status in [401, 403]:
error_text = await response.text()
auth_data.invalidate() # Mark tokens as invalid
if attempt < cls.MAX_RETRIES - 1:
# Try to get new tokens
signup_data = await cls._create_temporary_account(session, proxy)
auth_data.auth_token = signup_data.get("token")
app_token_data = await cls._get_app_token(session, auth_data.auth_token, proxy)
auth_data.app_token = app_token_data.get("token")
auth_data.created_at = time.time()
auth_data.tokens_valid = True
# Update shared auth data
cls._shared_auth_data[model] = auth_data
# Retry with new token
continue
else:
raise Exception(f"Authentication failed after {cls.MAX_RETRIES} attempts: {error_text}")
if response.status != 200:
error_text = await response.text()
if attempt < cls.MAX_RETRIES - 1:
# Wait before retrying
await asyncio.sleep(cls.RETRY_DELAY * (attempt + 1))
continue
else:
raise Exception(f"Chat request failed. Status: {response.status}, Details: {error_text}")
# Process successful response
if stream:
# Process the streaming response
full_response = ""
buffer = ""
# Use iter_any() to process chunks as they arrive
async for chunk in response.content.iter_any():
if chunk:
try:
chunk_text = chunk.decode('utf-8')
buffer += chunk_text
# Process complete lines in the buffer
lines = buffer.split('\n')
# Keep the last potentially incomplete line in the buffer
buffer = lines.pop() if lines else ""
for line in lines:
line = line.strip()
if not line:
continue
# Handle different streaming formats
if line.startswith("data: "):
line = line[6:] # Remove "data: " prefix
# Skip "[DONE]" marker
if line == "[DONE]":
continue
try:
data = json.loads(line)
# OpenAI format
if "choices" in data:
choice = data["choices"][0]
delta = choice.get("delta", {})
content = delta.get("content", "")
if content:
full_response += content
yield content
# Puter.js format
elif "type" in data and data["type"] == "text":
content = data.get("text", "")
if content:
full_response += content
yield content
except json.JSONDecodeError:
# Not valid JSON, might be a plain text response
if line and line != "[DONE]":
full_response += line
yield line
else:
# Try to parse as JSON directly
try:
data = json.loads(line)
if "type" in data and data["type"] == "text":
content = data.get("text", "")
if content:
full_response += content
yield content
except json.JSONDecodeError:
# Not valid JSON, might be a plain text response
if line and line != "[DONE]":
full_response += line
yield line
except UnicodeDecodeError:
continue
# Process any remaining data in the buffer
if buffer:
if buffer.startswith("data: "):
buffer = buffer[6:] # Remove "data: " prefix
if buffer != "[DONE]":
try:
data = json.loads(buffer)
if "choices" in data:
choice = data["choices"][0]
delta = choice.get("delta", {})
content = delta.get("content", "")
if content:
full_response += content
yield content
elif "type" in data and data["type"] == "text":
content = data.get("text", "")
if content:
full_response += content
yield content
except json.JSONDecodeError:
# Not valid JSON, might be a plain text response
if buffer and buffer != "[DONE]":
full_response += buffer
yield buffer
# Add the assistant's response to the conversation history
if full_response:
conversation.message_history.append({
"role": "assistant",
"content": full_response
})
# Return the conversation object if requested
if return_conversation:
yield conversation
# Signal completion
yield FinishReason("stop")
else:
# Process non-streaming response
try:
response_json = await response.json()
except Exception as e:
error_text = await response.text()
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(cls.RETRY_DELAY)
continue
else:
raise Exception(f"Failed to parse chat response as JSON: {error_text}")
if response_json.get("success") is True:
# Extract the content from the response
content = response_json.get("result", {}).get("message", {}).get("content", "")
# Add the assistant's response to the conversation history
if content:
conversation.message_history.append({
"role": "assistant",
"content": content
})
yield content
# Return the conversation object if requested
if return_conversation:
yield conversation
# Signal completion
yield FinishReason("stop")
else:
# Handle error in response
error_msg = response_json.get("error", {}).get("message", "Unknown error")
# Check for rate limiting or auth errors in the error message
if "rate" in error_msg.lower() or "limit" in error_msg.lower():
auth_data.set_rate_limit()
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(cls.RETRY_DELAY)
continue
else:
raise RateLimitError(f"Rate limited: {error_msg}")
if "auth" in error_msg.lower() or "token" in error_msg.lower():
auth_data.invalidate()
if attempt < cls.MAX_RETRIES - 1:
# Try to get new tokens
signup_data = await cls._create_temporary_account(session, proxy)
auth_data.auth_token = signup_data.get("token")
app_token_data = await cls._get_app_token(session, auth_data.auth_token, proxy)
auth_data.app_token = app_token_data.get("token")
auth_data.created_at = time.time()
auth_data.tokens_valid = True
# Update headers with new token
chat_headers["authorization"] = f"Bearer {auth_data.app_token}"
# Update shared auth data
cls._shared_auth_data[model] = auth_data
# Retry with new token
continue
# For other errors, retry or raise
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(cls.RETRY_DELAY)
continue
else:
yield f"Error: {error_msg}"
# If we get here, we've successfully processed the response
break
except RateLimitError as e:
# Set rate limit and inform user
auth_data.set_rate_limit(cls.RATE_LIMIT_DELAY)
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(min(cls.RATE_LIMIT_DELAY, 10)) # Wait but cap at 10 seconds for retries
continue
else:
raise RuntimeError(str(e))
except Exception as e:
# For network errors or other exceptions
if "token" in str(e).lower() or "auth" in str(e).lower():
auth_data.invalidate()
if attempt < cls.MAX_RETRIES - 1:
await asyncio.sleep(cls.RETRY_DELAY * (attempt + 1))
continue
else:
raise RuntimeError(str(e))
except Exception as e:
# If any error occurs outside the retry loop
if "token" in str(e).lower() or "auth" in str(e).lower():
auth_data.invalidate()
raise RuntimeError(str(e))