feat: 任务详情 LLM 请求数(cost_records 存 completions)
This commit is contained in:
1 parent
2ff8975f43
commit
ccb5a8638d
9 files changed
+215
-77
No files matched your search
+84
-34
@@ -372,8 +372,56 @@ def _drain_remote(browser):
|
||||
file=sys.stderr)
|
||||
|
||||
|
||||
class _LineReader:
|
||||
"""Non-blocking stdin line pump (starvation-free).
|
||||
|
||||
The previous loop used select() + sys.stdin.readline(). That
|
||||
starves on pipelined input: one readline() can buffer several
|
||||
lines in userspace, after which select() sees an empty fd
|
||||
forever and the buffered lines are never read. A client that
|
||||
writes notifications/initialized and its next request
|
||||
back-to-back (every MCP client does this after initialize)
|
||||
hangs the server exactly this way. Reading the raw fd into
|
||||
our own buffer delivers every line in arrival order.
|
||||
"""
|
||||
|
||||
def __init__(self, fd):
|
||||
self._fd = fd
|
||||
os.set_blocking(fd, False)
|
||||
self._buf = b""
|
||||
self.eof = False
|
||||
|
||||
def poll(self):
|
||||
"""Return (lines, drained).
|
||||
|
||||
lines are decoded strings without the newline. A trailing
|
||||
partial line is returned once at EOF. drained is True only
|
||||
when EOF was reached and nothing is left buffered.
|
||||
"""
|
||||
if not self.eof:
|
||||
try:
|
||||
chunk = os.read(self._fd, 65536)
|
||||
except BlockingIOError:
|
||||
chunk = None
|
||||
except OSError:
|
||||
self.eof = True
|
||||
chunk = None
|
||||
if chunk == b"":
|
||||
self.eof = True
|
||||
elif chunk:
|
||||
self._buf += chunk
|
||||
lines = []
|
||||
while b"\n" in self._buf:
|
||||
raw, self._buf = self._buf.split(b"\n", 1)
|
||||
lines.append(raw.decode("utf-8", "replace"))
|
||||
if self.eof and self._buf:
|
||||
lines.append(self._buf.decode("utf-8", "replace"))
|
||||
self._buf = b""
|
||||
return lines, (self.eof and not self._buf)
|
||||
|
||||
|
||||
def main():
|
||||
import select
|
||||
import time
|
||||
browser = _get_browser()
|
||||
# Frame sink must be set before any navigate so pump_screencast
|
||||
# has frames for the bridge to publish. No bridge (missing
|
||||
@@ -381,42 +429,44 @@ def main():
|
||||
# of accumulating unboundedly in a queue nobody drains.
|
||||
if _get_bridge(browser) is not None:
|
||||
browser.set_frame_sink(_frame_queue())
|
||||
buffer = ""
|
||||
reader = _LineReader(sys.stdin.fileno())
|
||||
while True:
|
||||
_drain_remote(browser)
|
||||
try:
|
||||
readable, _, _ = select.select([sys.stdin], [], [], 0.05)
|
||||
except Exception: # noqa: BLE001 - stdin gone
|
||||
lines, drained = reader.poll()
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
_handle_line(line, browser)
|
||||
if drained:
|
||||
break
|
||||
if not readable:
|
||||
continue
|
||||
chunk = sys.stdin.readline()
|
||||
if not chunk:
|
||||
break
|
||||
line = chunk.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
request = json.loads(line)
|
||||
except json.JSONDecodeError as exc:
|
||||
sys.stdout.write(json.dumps(
|
||||
{"jsonrpc": "2.0", "id": None,
|
||||
"error": _err(-32700, "parse error: %s" % exc)}) + "\n")
|
||||
sys.stdout.flush()
|
||||
continue
|
||||
try:
|
||||
response = handle_request(request, browser)
|
||||
except Exception as exc: # noqa: BLE001 - server must not die
|
||||
print("playwright_mcp: handler error: %s" % exc,
|
||||
file=sys.stderr)
|
||||
if "id" in request:
|
||||
response = {"jsonrpc": "2.0", "id": request["id"],
|
||||
"error": _err(-32603, "internal error")}
|
||||
else:
|
||||
response = None
|
||||
if response is not None:
|
||||
sys.stdout.write(json.dumps(response) + "\n")
|
||||
sys.stdout.flush()
|
||||
if not lines:
|
||||
time.sleep(0.05)
|
||||
|
||||
|
||||
def _handle_line(line, browser):
|
||||
"""Handle one JSON-RPC line; responses go to stdout."""
|
||||
try:
|
||||
request = json.loads(line)
|
||||
except json.JSONDecodeError as exc:
|
||||
sys.stdout.write(json.dumps(
|
||||
{"jsonrpc": "2.0", "id": None,
|
||||
"error": _err(-32700, "parse error: %s" % exc)}) + "\n")
|
||||
sys.stdout.flush()
|
||||
return
|
||||
try:
|
||||
response = handle_request(request, browser)
|
||||
except Exception as exc: # noqa: BLE001 - server must not die
|
||||
print("playwright_mcp: handler error: %s" % exc,
|
||||
file=sys.stderr)
|
||||
if "id" in request:
|
||||
response = {"jsonrpc": "2.0", "id": request["id"],
|
||||
"error": _err(-32603, "internal error")}
|
||||
else:
|
||||
response = None
|
||||
if response is not None:
|
||||
sys.stdout.write(json.dumps(response) + "\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def _frame_queue():
|
||||
|
||||
Reference in new issue
Block a user