Skip to content

Commit 4aa01b5

Browse files
committed
feat: enhance Copilot and Yupp classes with session ID and credit fetching functionality
1 parent 282b350 commit 4aa01b5

File tree

3 files changed

+75
-34
lines changed

3 files changed

+75
-34
lines changed

g4f/Provider/Copilot.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
import json
5+
import uuid
56
import asyncio
67
import base64
78
import random
@@ -135,7 +136,7 @@ async def create_authed(
135136
if not has_curl_cffi:
136137
raise MissingRequirementsError('Install or update "curl_cffi" package | pip install -U curl_cffi')
137138
model = cls.get_model(model)
138-
websocket_url = cls.websocket_url
139+
websocket_url = cls.websocket_url + f"&clientSessionId={uuid.uuid4()}"
139140
headers = DEFAULT_HEADERS.copy()
140141
headers["origin"] = cls.url
141142
headers["referer"] = cls.url + "/"

g4f/Provider/CopilotSession.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ async def create_authed(
6969
cls,
7070
model: str,
7171
messages: Messages,
72+
auth_result: AuthResult,
7273
proxy: str = None,
7374
timeout: int = 30,
7475
prompt: str = None,
@@ -87,9 +88,12 @@ async def create_authed(
8788
page = await session.get(url)
8889
await page.send(cdp.network.enable())
8990
queue = asyncio.Queue()
91+
def handle_ws_message(event):
92+
if hasattr(event, "response") and event.response.payload_data:
93+
queue.put_nowait((event.request_id, event.response.payload_data))
9094
page.add_handler(
9195
cdp.network.WebSocketFrameReceived,
92-
lambda event: queue.put_nowait((event.request_id, event.response.payload_data)),
96+
handle_ws_message
9397
)
9498
textarea = await page.select("textarea")
9599
if textarea is not None:

g4f/Provider/Yupp.py

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import hashlib
33
import json
44
import os
5+
import random
56
import re
67
import time
78
import uuid
@@ -159,32 +160,28 @@ async def claim_yupp_reward(
159160
def sync_record_model_feedback(
160161
scraper: CloudScraper,
161162
account: Dict[str, Any],
162-
turn_id: str,
163-
left_message_id: str,
164-
right_message_id: str,
163+
reward_kw: Dict[str, str]
165164
) -> Optional[str]:
166165
try:
167-
log_debug(f"Recording model feedback for turn {turn_id}...")
168-
url = "https://yupp.ai/api/trpc/evals.recordModelFeedback?batch=1"
169-
payload = {
170-
"0": {
171-
"json": {
172-
"turnId": turn_id,
173-
"evalType": "SELECTION",
174-
"messageEvals": [
175-
{
176-
"messageId": right_message_id,
177-
"rating": "GOOD",
178-
"reasons": ["Fast"],
179-
},
180-
{"messageId": left_message_id, "rating": "BAD", "reasons": []},
181-
],
182-
"comment": "",
183-
"requireReveal": False,
184-
}
185-
}
186-
}
166+
url = "https://yupp.ai/api/trpc/evals.getTurnAnnotations"
167+
payload = {"0": {"json": {"turnId": reward_kw["turn_id"]}}}
187168
scraper.cookies.set("__Secure-yupp.session-token", account["token"])
169+
response = scraper.get(url, params={"batch": "1", "input": json.dumps(payload)})
170+
data = response.json()
171+
positive_notes = []
172+
for result in data:
173+
json_data = result.get("result", {}).get("data", {}).get("json", {})
174+
positive_notes = [row[0] for row in json_data.get("positive_notes", [])]
175+
positive_notes = [random.choice(positive_notes)] if positive_notes else []
176+
log_debug(f"Recording feedback for turn {reward_kw['turn_id']}: {positive_notes}")
177+
url = "https://yupp.ai/api/trpc/evals.recordModelFeedback?batch=1"
178+
selected_message_id = reward_kw.get("left_message_id") if reward_kw.get("selection") == "left" else reward_kw.get("right_message_id")
179+
variant_message_id = reward_kw.get("right_message_id") if reward_kw.get("selection") == "left" else reward_kw.get("left_message_id")
180+
payload = {"0":{"json":{"turnId":reward_kw["turn_id"],"isOnboarding":False,"evalType":"SELECTION","messageEvals":[
181+
{"messageId":selected_message_id,"rating":"GOOD","reasons":positive_notes},
182+
{"messageId":variant_message_id,"rating":"BAD","reasons":[]}
183+
],"comment":"","requireReveal":False}}}
184+
188185
response = scraper.post(url, json=payload)
189186
response.raise_for_status()
190187
data = response.json()
@@ -206,19 +203,15 @@ def sync_record_model_feedback(
206203
async def record_model_feedback(
207204
scraper: CloudScraper,
208205
account: Dict[str, Any],
209-
turn_id: str,
210-
left_message_id: str,
211-
right_message_id: str,
206+
reward_kw: Dict[str, str]
212207
) -> Optional[str]:
213208
loop = asyncio.get_event_loop()
214209
return await loop.run_in_executor(
215210
_executor,
216211
sync_record_model_feedback,
217212
scraper,
218213
account,
219-
turn_id,
220-
left_message_id,
221-
right_message_id,
214+
reward_kw
222215
)
223216

224217

@@ -289,6 +282,22 @@ async def make_chat_private(
289282
_executor, sync_make_chat_private, scraper, account, chat_id
290283
)
291284

285+
async def get_credits(scraper: CloudScraper, account: Dict[str, Any]) -> Optional[float]:
286+
try:
287+
log_debug("Fetching credit balance...")
288+
url = "https://yupp.ai/api/trpc/credits.getCredits?batch=1&input=%7B%220%22%3A%7B%22json%22%3Anull%2C%22meta%22%3A%7B%22values%22%3A%5B%22undefined%22%5D%2C%22v%22%3A1%7D%7D%7D"
289+
scraper.cookies.set("__Secure-yupp.session-token", account["token"])
290+
def sync_fetch_credits():
291+
response = scraper.get(url)
292+
response.raise_for_status()
293+
data = response.json()
294+
balance = data[0]["result"]["data"]["json"]
295+
return balance
296+
loop = asyncio.get_event_loop()
297+
return await loop.run_in_executor(_executor, sync_fetch_credits)
298+
except Exception as e:
299+
log_debug(f"Failed to fetch credit balance: {e}")
300+
return None
292301

293302
def log_debug(message: str):
294303
if os.getenv("DEBUG_MODE", "false").lower() == "true":
@@ -495,6 +504,26 @@ def sync_stream_request(
495504
)
496505
response.raise_for_status()
497506
return response
507+
508+
@classmethod
509+
async def get_quota(cls, api_key: str = None) -> Optional[float]:
510+
if not api_key:
511+
api_key = AuthManager.load_api_key(cls)
512+
if not api_key:
513+
api_key = get_cookies("yupp.ai", False).get("__Secure-yupp.session-token")
514+
if api_key:
515+
load_yupp_accounts(api_key)
516+
else:
517+
raise MissingAuthError(
518+
"No Yupp accounts configured. Set YUPP_API_KEY environment variable."
519+
)
520+
credits = await get_credits(create_scraper(), await get_best_yupp_account())
521+
return {
522+
"credits": {
523+
"remaining": credits,
524+
"total": 5000
525+
}
526+
}
498527

499528
@classmethod
500529
async def create_async_generator(
@@ -546,6 +575,14 @@ async def create_async_generator(
546575
scraper = create_scraper()
547576
if proxy:
548577
scraper.proxies = {"http": proxy, "https": proxy}
578+
579+
credits = await get_credits(scraper, account)
580+
log_debug(f"Account ...{account['token'][-4:]} has {credits} credits")
581+
if credits is not None and credits <= 100:
582+
log_debug(f"Account ...{account['token'][-4:]} has low credits, rotating")
583+
async with account_rotation_lock:
584+
account["error_count"] += 1
585+
continue
549586

550587
# Initialize token extractor for automatic token swapping
551588
token_extractor = get_token_extractor(
@@ -943,6 +980,7 @@ def iter_lines():
943980
target_stream_id = extract_ref_id(
944981
select_stream[i].get("next")
945982
)
983+
reward_kw["selection"] = "left" if i == 0 else "right"
946984
provider_info["modelLabel"] = selection.get(
947985
"shortLabel"
948986
)
@@ -1073,9 +1111,7 @@ def iter_lines():
10731111
eval_id = await record_model_feedback(
10741112
scraper,
10751113
account,
1076-
reward_kw["turn_id"],
1077-
reward_kw["left_message_id"],
1078-
reward_kw["right_message_id"],
1114+
reward_kw
10791115
)
10801116
if eval_id:
10811117
await claim_yupp_reward(scraper, account, eval_id)

0 commit comments

Comments
 (0)