新增了完整的流式响应处理模块,包含代码围栏解析、分块策略、指令处理、流式草稿循环等核心能力,支持按段落/句子等规则切割响应块,支持进度更新与指令回调,完善流式交互的全流程支持
99 lines
2.6 KiB
Python
99 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from dataclasses import dataclass
|
|
from typing import TypedDict
|
|
|
|
_FENCE_LINE_RE = re.compile(r"^( {0,3})(`{3,}|~{3,})(.*)$")
|
|
|
|
|
|
class _OpenFence(TypedDict):
|
|
start: int
|
|
marker_char: str
|
|
marker_len: int
|
|
open_line: str
|
|
marker: str
|
|
indent: str
|
|
|
|
|
|
@dataclass
|
|
class FenceSpan:
|
|
start: int
|
|
end: int
|
|
open_line: str
|
|
marker: str
|
|
indent: str
|
|
|
|
|
|
def parse_fence_spans(buffer: str) -> list[FenceSpan]:
|
|
spans: list[FenceSpan] = []
|
|
open_fence: _OpenFence | None = None
|
|
offset = 0
|
|
|
|
while offset <= len(buffer):
|
|
next_newline = buffer.find("\n", offset)
|
|
line_end = next_newline if next_newline != -1 else len(buffer)
|
|
line = buffer[offset:line_end]
|
|
|
|
match = _FENCE_LINE_RE.match(line)
|
|
if match:
|
|
indent = match.group(1)
|
|
marker = match.group(2)
|
|
marker_char = marker[0]
|
|
marker_len = len(marker)
|
|
if open_fence is None:
|
|
open_fence = {
|
|
"start": offset,
|
|
"marker_char": marker_char,
|
|
"marker_len": marker_len,
|
|
"open_line": line,
|
|
"marker": marker,
|
|
"indent": indent,
|
|
}
|
|
elif open_fence["marker_char"] == marker_char and marker_len >= open_fence["marker_len"]:
|
|
spans.append(
|
|
FenceSpan(
|
|
start=open_fence["start"],
|
|
end=line_end,
|
|
open_line=open_fence["open_line"],
|
|
marker=open_fence["marker"],
|
|
indent=open_fence["indent"],
|
|
)
|
|
)
|
|
open_fence = None
|
|
|
|
if next_newline == -1:
|
|
break
|
|
offset = next_newline + 1
|
|
|
|
if open_fence is not None:
|
|
spans.append(
|
|
FenceSpan(
|
|
start=open_fence["start"],
|
|
end=len(buffer),
|
|
open_line=open_fence["open_line"],
|
|
marker=open_fence["marker"],
|
|
indent=open_fence["indent"],
|
|
)
|
|
)
|
|
|
|
return spans
|
|
|
|
|
|
def find_fence_span_at(spans: list[FenceSpan], index: int) -> FenceSpan | None:
|
|
low, high = 0, len(spans) - 1
|
|
while low <= high:
|
|
mid = (low + high) // 2
|
|
span = spans[mid]
|
|
if index < span.start:
|
|
high = mid - 1
|
|
elif index > span.end:
|
|
low = mid + 1
|
|
else:
|
|
return span
|
|
return None
|
|
|
|
|
|
def is_safe_fence_break(spans: list[FenceSpan], index: int) -> bool:
|
|
return find_fence_span_at(spans, index) is None
|