返回提交历史
Modified
g4f/Provider/Yupp.py
+115
-17
XFEstudio/gpt4free
Handle multi-line <think> and <yapp> blocks in Yupp
Added logic to capture and process multi-line <think> and <yapp class="image-gen"> blocks referenced by special IDs. Introduced block storage and extraction functions, enabling reasoning and image-gen content to be handled via references in the response stream.
c3f8d7e7
代码差异
1 个文件
+115
-17
@@ -475,10 +475,27 @@ class Yupp(AsyncGeneratorProvider, ProviderModelMixin):
475
475
}
476
476
# Holds leftStream / rightStream definitions to determine target/variant
477
477
select_stream = [None, None]
478
478
# State for capturing a multi-line <think> + <yapp> block (fa-style)
479
capturing_ref_id: Optional[str] = None
480
capturing_lines: List[bytes] = []
481
482
# Storage for special referenced blocks like $fa
483
think_blocks: Dict[str, str] = {}
484
image_blocks: Dict[str, str] = {}
485
479
486
def extract_ref_id(ref):
480
487
"""Extract ID from reference string, e.g., from '$@123' extract '123'"""
481
488
return ref[2:] if ref and isinstance(ref, str) and ref.startswith("$@") else None
489
490
def extract_ref_name(ref: str) -> Optional[str]:
491
"""Extract simple ref name from '$fa' → 'fa'"""
492
if not isinstance(ref, str):
493
return None
494
if ref.startswith("$@"):
495
return ref[2:]
496
if ref.startswith("$") and len(ref) > 1:
497
return ref[1:]
498
return None
482
499
def is_valid_content(content: str) -> bool:
483
500
"""Check if content is valid"""
484
501
if not content or content in [None, "", "$undefined"]:
@@ -524,7 +541,27 @@ class Yupp(AsyncGeneratorProvider, ProviderModelMixin):
524
541
if for_target:
525
542
normal_content += content
526
543
yield content
527
544
545
def finalize_capture_block(ref_id: str, lines: List[bytes]):
546
"""Parse captured <think> + <yapp> block for a given ref ID."""
547
text = b"".join(lines).decode("utf-8", errors="ignore")
548
549
# Extract <think>...</think>
550
think_start = text.find("<think>")
551
think_end = text.find("</think>")
552
if think_start != -1 and think_end != -1 and think_end > think_start:
553
inner = text[think_start + len("<think>"):think_end].strip()
554
if inner:
555
think_blocks[ref_id] = inner
556
557
# Extract <yapp class="image-gen">...</yapp>
558
yapp_start = text.find('<yapp class="image-gen">')
559
if yapp_start != -1:
560
yapp_end = text.find("</yapp>", yapp_start)
561
if yapp_end != -1:
562
yapp_block = text[yapp_start:yapp_end + len("</yapp>")]
563
image_blocks[ref_id] = yapp_block
564
528
565
try:
529
566
line_count = 0
530
567
quick_response_id = None
@@ -540,17 +577,55 @@ class Yupp(AsyncGeneratorProvider, ProviderModelMixin):
540
577
right_message_id = None
541
578
nudge_new_chat_id = None
542
579
nudge_new_chat = False
543
544
580
async for line in response_content:
545
581
line_count += 1
546
582
# If we are currently capturing a think/image block for some ref ID
583
if capturing_ref_id is not None:
584
capturing_lines.append(line)
585
586
# Check if this line closes the <yapp> block; after that, block is complete
587
if b"</yapp>" in line: # or b':{"curr"' in line:
588
# We may have trailing "2:{...}" after </yapp> on the same line
589
# Get id using re
590
idx = line.find(b"</yapp>")
591
suffix = line[idx + len(b"</yapp>"):]
592
593
# Finalize captured block for this ref ID
594
finalize_capture_block(capturing_ref_id, capturing_lines)
595
capturing_ref_id = None
596
capturing_lines = []
597
598
# If there is trailing content (e.g. '2:{"curr":"$fa"...}')
599
if suffix.strip():
600
# Process suffix as a new "line" in the same iteration
601
line = suffix
602
else:
603
# Nothing more on this line
604
continue
605
else:
606
# Still inside captured block; skip normal processing
607
continue
608
609
# Detect start of a <think> block assigned to a ref like 'fa:...<think>'
610
if b"<think>" in line:
611
m = line_pattern.match(line)
612
if m:
613
capturing_ref_id = m.group(1).decode()
614
capturing_lines = [line]
615
# Skip normal parsing; the rest of the block will be captured until </yapp>
616
continue
617
547
618
match = line_pattern.match(line)
548
619
if not match:
549
620
continue
550
621
551
622
chunk_id, chunk_data = match.groups()
552
623
chunk_id = chunk_id.decode()
553
624
625
if nudge_new_chat_id and chunk_id == nudge_new_chat_id:
626
nudge_new_chat = chunk_data.decode()
627
continue
628
554
629
try:
555
630
data = json.loads(chunk_data) if chunk_data != b"{}" else {}
556
631
except json.JSONDecodeError:
@@ -609,15 +684,41 @@ class Yupp(AsyncGeneratorProvider, ProviderModelMixin):
609
684
target_stream_id = extract_ref_id(data.get("next"))
610
685
content = data.get("curr", "")
611
686
if content:
612
async for chunk in process_content_chunk(
613
content,
614
chunk_id,
615
line_count,
616
for_target=True
617
):
618
stream["target"].append(chunk)
619
is_started = True
620
yield chunk
687
# Handle special "$fa" / "$<id>" reference
688
ref_name = extract_ref_name(content)
689
if ref_name and (ref_name in think_blocks or ref_name in image_blocks):
690
# Thinking block
691
if ref_name in think_blocks:
692
t_text = think_blocks[ref_name]
693
if t_text:
694
reasoning = Reasoning(t_text)
695
# thinking_content += t_text
696
stream["thinking"].append(reasoning)
697
# yield reasoning
698
699
# Image-gen block
700
if ref_name in image_blocks:
701
img_block_text = image_blocks[ref_name]
702
async for chunk in process_content_chunk(
703
img_block_text,
704
ref_name,
705
line_count,
706
for_target=True
707
):
708
stream["target"].append(chunk)
709
is_started = True
710
yield chunk
711
else:
712
# Normal textual chunk
713
async for chunk in process_content_chunk(
714
content,
715
chunk_id,
716
line_count,
717
for_target=True
718
):
719
stream["target"].append(chunk)
720
is_started = True
721
yield chunk
621
722
# Variant stream (comparison)
622
723
elif variant_stream_id and chunk_id == variant_stream_id:
623
724
yield PlainTextResponse("[Variant] " + line.decode(errors="ignore"))
@@ -660,8 +761,6 @@ class Yupp(AsyncGeneratorProvider, ProviderModelMixin):
660
761
...
661
762
elif chunk_id == left_message_id:
662
763
...
663
elif chunk_id == nudge_new_chat_id:
664
nudge_new_chat = data
665
764
# Miscellaneous extra content
666
765
elif isinstance(data, dict) and "curr" in data:
667
766
content = data.get("curr", "")
@@ -684,7 +783,6 @@ class Yupp(AsyncGeneratorProvider, ProviderModelMixin):
684
783
yield PreviewResponse(variant_text)
685
784
yield JsonResponse(**stream)
686
785
log_debug(f"Finished processing {line_count} lines")
687
688
786
except:
689
787
raise
690
788