File size: 8,151 Bytes
a4b70d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
from __future__ import annotations

import json
import re
import uuid
import random
from aiohttp import ClientSession, FormData

from ..typing import AsyncResult, Messages
from ..requests import raise_for_status
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import get_last_user_message
from ..providers.response import TitleGeneration, Reasoning, FinishReason
from ..errors import ModelNotFoundError
from .. import debug


class LambdaChat(AsyncGeneratorProvider, ProviderModelMixin):
    label = "Lambda Chat"
    url = "https://lambda.chat"
    conversation_url = f"{url}/conversation"

    working = True
    active_by_default = True

    default_model = "deepseek-llama3.3-70b"
    models = [
        default_model,
        "apriel-5b-instruct",
        "hermes-3-llama-3.1-405b-fp8",
        "llama3.3-70b-instruct-fp8",
        "llama3.3-70b-instruct-fp8",
        "qwen3-32b-fp8",
    ]
    model_aliases = {
        "llama-3.3-70b": "llama3.3-70b-instruct-fp8",
        "qwen-3-32b": "qwen3-32b-fp8"
    }

    @classmethod
    async def create_async_generator(
        cls, model: str, messages: Messages,
        api_key: str = None, 
        proxy: str = None,
        cookies: dict = None,
        **kwargs
    ) -> AsyncResult:
        model = cls.get_model(model)
        headers = {
            "Origin": cls.url,
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
            "Accept": "*/*",
            "Accept-Language": "en-US,en;q=0.9",
            "Referer": cls.url,
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-origin",
            "Priority": "u=1, i",
            "Pragma": "no-cache",
            "Cache-Control": "no-cache"
        }
        
        # Initialize cookies if not provided
        if cookies is None:
            cookies = {
                "hf-chat": str(uuid.uuid4())  # Generate a session ID
            }
        
        async with ClientSession(headers=headers, cookies=cookies) as session:
            # Step 1: Create a new conversation
            data = {"model": model}
            async with session.post(cls.conversation_url, json=data, proxy=proxy) as response:
                await raise_for_status(response)
                conversation_response = await response.json()
                conversation_id = conversation_response["conversationId"]
                
                # Update cookies with any new ones from the response
                for cookie_name, cookie in response.cookies.items():
                    cookies[cookie_name] = cookie.value
            
            # Step 2: Get data for this conversation to extract message ID
            async with session.get(
                f"{cls.conversation_url}/{conversation_id}/__data.json?x-sveltekit-invalidated=11", 
                proxy=proxy
            ) as response:
                await raise_for_status(response)
                response_text = await response.text()
                
                # Update cookies again
                for cookie_name, cookie in response.cookies.items():
                    cookies[cookie_name] = cookie.value
                
                # Parse the JSON response to find the message ID
                message_id = None
                try:
                    # Try to parse each line as JSON
                    for line in response_text.splitlines():
                        if not line.strip():
                            continue
                        
                        try:
                            data_json = json.loads(line)
                            if "type" in data_json and data_json["type"] == "data" and "nodes" in data_json:
                                for node in data_json["nodes"]:
                                    if "type" in node and node["type"] == "data" and "data" in node:
                                        # Look for system message ID
                                        for item in node["data"]:
                                            if isinstance(item, dict) and "id" in item and "from" in item and item.get("from") == "system":
                                                message_id = item["id"]
                                                break
                                        
                                        # If we found the ID, break out of the loop
                                        if message_id:
                                            break
                        except json.JSONDecodeError:
                            continue
                    
                    # If we still don't have a message ID, try to find any UUID in the response
                    if not message_id:
                        uuid_pattern = r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
                        uuids = re.findall(uuid_pattern, response_text)
                        if uuids:
                            message_id = uuids[0]
                    
                    if not message_id:
                        raise ValueError("Could not find message ID in response")
                        
                except (IndexError, KeyError, ValueError) as e:
                    raise RuntimeError(f"Failed to parse conversation data: {str(e)}")
            
            # Step 3: Send the user message
            user_message = get_last_user_message(messages)
            
            # Prepare form data exactly as in the curl example
            form_data = FormData()
            form_data.add_field(
                "data",
                json.dumps({
                    "inputs": user_message, 
                    "id": message_id, 
                    "is_retry": False, 
                    "is_continue": False, 
                    "web_search": False, 
                    "tools": []
                }),
                content_type="application/json"
            )
            
            async with session.post(
                f"{cls.conversation_url}/{conversation_id}", 
                data=form_data,
                proxy=proxy
            ) as response:
                if not response.ok:
                    debug.log(f"LambdaChat: Request Body: {form_data}")
                await raise_for_status(response)
                
                async for chunk in response.content:
                    if not chunk:
                        continue
                        
                    chunk_str = chunk.decode('utf-8', errors='ignore')
                    
                    try:
                        data = json.loads(chunk_str)
                    except json.JSONDecodeError:
                        continue
                        
                    # Handling different types of responses
                    if data.get("type") == "stream" and "token" in data:
                        # Remove null characters from the token
                        token = data["token"].replace("\u0000", "")
                        if token:
                            yield token
                    elif data.get("type") == "title":
                        yield TitleGeneration(data.get("title", ""))
                    elif data.get("type") == "reasoning":
                        subtype = data.get("subtype")
                        token = data.get("token", "").replace("\u0000", "")
                        status = data.get("status", "")
                        
                        if subtype == "stream" and token:
                            yield Reasoning(token=token)
                        elif subtype == "status" and status:
                            yield Reasoning(status=status)
                    elif data.get("type") == "finalAnswer":
                        yield FinishReason("stop")
                        break
                    elif data.get("type") == "status" and data.get("status") == "keepAlive":
                        # Just a keepalive, ignore
                        continue