Stream Jupyter workspace file downloads
This commit is contained in:
+65
-6
@@ -83,6 +83,9 @@ FEISHU_SPREADSHEET_MAX_CELL_CHARS = 5000
|
||||
FEISHU_SPREADSHEET_WRITE_BATCH_ROWS = 500
|
||||
FEISHU_ONLINE_DOCS_FILENAME = 'online-docs.json'
|
||||
JUPYTER_WORKSPACE_FILENAME = 'jupyter_workspace.json'
|
||||
JUPYTER_STREAM_CHUNK_BYTES = 1024 * 1024
|
||||
FEISHU_REMOTE_DOC_MAX_BYTES = 50 * 1024 * 1024
|
||||
FEISHU_REMOTE_SHEET_MAX_BYTES = 100 * 1024 * 1024
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -1096,11 +1099,20 @@ def create_app(state: AgentState) -> FastAPI:
|
||||
if runtime is None:
|
||||
raise HTTPException(status_code=404, detail='Jupyter runtime is not connected')
|
||||
try:
|
||||
data, filename = runtime.read_file_bytes(path)
|
||||
response, filename = runtime.open_file_stream(path)
|
||||
except JupyterRuntimeError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
return Response(
|
||||
data,
|
||||
|
||||
def stream_chunks() -> Any:
|
||||
try:
|
||||
for chunk in response.iter_content(chunk_size=JUPYTER_STREAM_CHUNK_BYTES):
|
||||
if chunk:
|
||||
yield chunk
|
||||
finally:
|
||||
response.close()
|
||||
|
||||
return StreamingResponse(
|
||||
stream_chunks(),
|
||||
media_type=_content_type_for_filename(filename),
|
||||
headers={
|
||||
'content-disposition': (
|
||||
@@ -3050,9 +3062,27 @@ def _materialize_jupyter_file_for_feishu(
|
||||
if runtime is None:
|
||||
raise HTTPException(status_code=404, detail='Jupyter runtime is not connected')
|
||||
try:
|
||||
data, filename = runtime.read_file_bytes(remote_path)
|
||||
info = runtime.file_info(remote_path)
|
||||
filename = _safe_uploaded_filename(str(info.get('name') or 'remote-file'))
|
||||
suffix = Path(filename).suffix.lower()
|
||||
max_bytes = (
|
||||
FEISHU_REMOTE_SHEET_MAX_BYTES
|
||||
if suffix in FEISHU_SPREADSHEET_SUFFIXES
|
||||
else FEISHU_REMOTE_DOC_MAX_BYTES
|
||||
)
|
||||
size = info.get('size')
|
||||
if isinstance(size, int) and size > max_bytes:
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=(
|
||||
f'远端文件过大,当前在线文档转换上限为 '
|
||||
f'{_format_bytes(max_bytes)},请先在 Jupyter 侧裁剪或抽样后再转换。'
|
||||
),
|
||||
)
|
||||
response, stream_filename = runtime.open_file_stream(remote_path)
|
||||
except JupyterRuntimeError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
filename = filename or _safe_uploaded_filename(stream_filename)
|
||||
target_dir = (
|
||||
state.account_paths(account_id)['base']
|
||||
/ 'integrations'
|
||||
@@ -3061,8 +3091,27 @@ def _materialize_jupyter_file_for_feishu(
|
||||
/ session_id
|
||||
)
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
target = target_dir / _safe_uploaded_filename(filename)
|
||||
target.write_bytes(data)
|
||||
target = target_dir / filename
|
||||
written = 0
|
||||
try:
|
||||
with target.open('wb') as handle:
|
||||
for chunk in response.iter_content(chunk_size=JUPYTER_STREAM_CHUNK_BYTES):
|
||||
if not chunk:
|
||||
continue
|
||||
written += len(chunk)
|
||||
if written > max_bytes:
|
||||
handle.close()
|
||||
target.unlink(missing_ok=True)
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=(
|
||||
f'远端文件过大,当前在线文档转换上限为 '
|
||||
f'{_format_bytes(max_bytes)},请先在 Jupyter 侧裁剪或抽样后再转换。'
|
||||
),
|
||||
)
|
||||
handle.write(chunk)
|
||||
finally:
|
||||
response.close()
|
||||
return target
|
||||
|
||||
|
||||
@@ -3080,6 +3129,16 @@ def _safe_uploaded_filename(filename: str) -> str:
|
||||
return cleaned[:180] or 'remote-file'
|
||||
|
||||
|
||||
def _format_bytes(value: int) -> str:
|
||||
if value >= 1024 * 1024 * 1024:
|
||||
return f'{value / (1024 * 1024 * 1024):.1f}GB'
|
||||
if value >= 1024 * 1024:
|
||||
return f'{value / (1024 * 1024):.0f}MB'
|
||||
if value >= 1024:
|
||||
return f'{value / 1024:.0f}KB'
|
||||
return f'{value}B'
|
||||
|
||||
|
||||
def _resolve_account_file(state: AgentState, account_id: str | None, raw_path: str) -> Path:
|
||||
path = Path(raw_path).expanduser().resolve()
|
||||
account_base = state.account_paths(account_id)['base'].resolve()
|
||||
|
||||
Reference in New Issue
Block a user