123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- from __future__ import annotations
- import asyncio
- import aiohttp
- import random
- import string
- import json
- from urllib.parse import urlencode
- from aiohttp import ClientSession
- from ..typing import AsyncResult, Messages
- from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
- from .helper import format_prompt
- class RubiksAI(AsyncGeneratorProvider, ProviderModelMixin):
- label = "Rubiks AI"
- url = "https://rubiks.ai"
- api_endpoint = "https://rubiks.ai/search/api.php"
- working = True
- supports_stream = True
- supports_system_message = True
- supports_message_history = True
- default_model = 'llama-3.1-70b-versatile'
- models = [default_model, 'gpt-4o-mini']
- model_aliases = {
- "llama-3.1-70b": "llama-3.1-70b-versatile",
- }
- @classmethod
- def get_model(cls, model: str) -> str:
- if model in cls.models:
- return model
- elif model in cls.model_aliases:
- return cls.model_aliases[model]
- else:
- return cls.default_model
- @staticmethod
- def generate_mid() -> str:
- """
- Generates a 'mid' string following the pattern:
- 6 characters - 4 characters - 4 characters - 4 characters - 12 characters
- Example: 0r7v7b-quw4-kdy3-rvdu-ekief6xbuuq4
- """
- parts = [
- ''.join(random.choices(string.ascii_lowercase + string.digits, k=6)),
- ''.join(random.choices(string.ascii_lowercase + string.digits, k=4)),
- ''.join(random.choices(string.ascii_lowercase + string.digits, k=4)),
- ''.join(random.choices(string.ascii_lowercase + string.digits, k=4)),
- ''.join(random.choices(string.ascii_lowercase + string.digits, k=12))
- ]
- return '-'.join(parts)
- @staticmethod
- def create_referer(q: str, mid: str, model: str = '') -> str:
- """
- Creates a Referer URL with dynamic q and mid values, using urlencode for safe parameter encoding.
- """
- params = {'q': q, 'model': model, 'mid': mid}
- encoded_params = urlencode(params)
- return f'https://rubiks.ai/search/?{encoded_params}'
- @classmethod
- async def create_async_generator(
- cls,
- model: str,
- messages: Messages,
- proxy: str = None,
- websearch: bool = False,
- **kwargs
- ) -> AsyncResult:
- """
- Creates an asynchronous generator that sends requests to the Rubiks AI API and yields the response.
- Parameters:
- - model (str): The model to use in the request.
- - messages (Messages): The messages to send as a prompt.
- - proxy (str, optional): Proxy URL, if needed.
- - websearch (bool, optional): Indicates whether to include search sources in the response. Defaults to False.
- """
- model = cls.get_model(model)
- prompt = format_prompt(messages)
- q_value = prompt
- mid_value = cls.generate_mid()
- referer = cls.create_referer(q=q_value, mid=mid_value, model=model)
- url = cls.api_endpoint
- params = {
- 'q': q_value,
- 'model': model,
- 'id': '',
- 'mid': mid_value
- }
- headers = {
- 'Accept': 'text/event-stream',
- 'Accept-Language': 'en-US,en;q=0.9',
- 'Cache-Control': 'no-cache',
- 'Connection': 'keep-alive',
- 'Pragma': 'no-cache',
- 'Referer': referer,
- 'Sec-Fetch-Dest': 'empty',
- 'Sec-Fetch-Mode': 'cors',
- 'Sec-Fetch-Site': 'same-origin',
- 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
- 'sec-ch-ua': '"Chromium";v="129", "Not=A?Brand";v="8"',
- 'sec-ch-ua-mobile': '?0',
- 'sec-ch-ua-platform': '"Linux"'
- }
- try:
- timeout = aiohttp.ClientTimeout(total=None)
- async with ClientSession(timeout=timeout) as session:
- async with session.get(url, headers=headers, params=params, proxy=proxy) as response:
- if response.status != 200:
- yield f"Request ended with status code {response.status}"
- return
- assistant_text = ''
- sources = []
- async for line in response.content:
- decoded_line = line.decode('utf-8').strip()
- if not decoded_line.startswith('data: '):
- continue
- data = decoded_line[6:]
- if data in ('[DONE]', '{"done": ""}'):
- break
- try:
- json_data = json.loads(data)
- except json.JSONDecodeError:
- continue
- if 'url' in json_data and 'title' in json_data:
- if websearch:
- sources.append({'title': json_data['title'], 'url': json_data['url']})
- elif 'choices' in json_data:
- for choice in json_data['choices']:
- delta = choice.get('delta', {})
- content = delta.get('content', '')
- role = delta.get('role', '')
- if role == 'assistant':
- continue
- assistant_text += content
- if websearch and sources:
- sources_text = '\n'.join([f"{i+1}. [{s['title']}]: {s['url']}" for i, s in enumerate(sources)])
- assistant_text += f"\n\n**Source:**\n{sources_text}"
- yield assistant_text
- except asyncio.CancelledError:
- yield "The request was cancelled."
- except aiohttp.ClientError as e:
- yield f"An error occurred during the request: {e}"
- except Exception as e:
- yield f"An unexpected error occurred: {e}"
|