feat: add remote workspaces and verified deliverables
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Annotated
|
||||
from urllib.parse import quote
|
||||
|
||||
import uvicorn
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException, Query
|
||||
from fastapi.responses import Response, StreamingResponse
|
||||
|
||||
from agent_platform.auth import UserIdentity, decode_openwebui_identity, verify_service_bearer
|
||||
from agent_platform.config import Settings, get_settings
|
||||
@@ -19,6 +21,7 @@ from agent_platform.gateway.schemas import (
|
||||
SearchFilesRequest,
|
||||
StartProcessRequest,
|
||||
ToolResult,
|
||||
WorkspaceListing,
|
||||
WorkspaceStatus,
|
||||
WriteFileRequest,
|
||||
)
|
||||
@@ -70,6 +73,11 @@ def create_app(
|
||||
async with mutation_locks_guard:
|
||||
return mutation_locks.setdefault(user_id, asyncio.Lock())
|
||||
|
||||
def attachment_header(filename: str) -> str:
|
||||
fallback = "".join(character for character in filename if character.isascii() and character.isalnum())
|
||||
fallback = fallback[:80] or "download"
|
||||
return f"attachment; filename={fallback}; filename*=UTF-8''{quote(filename)}"
|
||||
|
||||
@app.get("/health", include_in_schema=False)
|
||||
async def health() -> dict:
|
||||
return {"status": "ok", "provider": settings.execution_provider}
|
||||
@@ -85,6 +93,52 @@ def create_app(
|
||||
except ValueError as exc:
|
||||
raise translate_value_error(exc) from exc
|
||||
|
||||
@app.get("/v1/files", response_model=WorkspaceListing, operation_id="browse_workspace_files")
|
||||
async def browse_workspace_files(
|
||||
identity: Identity,
|
||||
path: Annotated[str, Query(max_length=4096)] = ".",
|
||||
) -> WorkspaceListing:
|
||||
try:
|
||||
return await executor().browse_files(identity.user_id, path)
|
||||
except ValueError as exc:
|
||||
raise translate_value_error(exc) from exc
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
|
||||
@app.get("/v1/files/download", operation_id="download_workspace_file")
|
||||
async def download_workspace_file(
|
||||
identity: Identity,
|
||||
path: Annotated[str, Query(max_length=4096)],
|
||||
) -> Response:
|
||||
try:
|
||||
download = await executor().download_file(identity.user_id, path)
|
||||
except ValueError as exc:
|
||||
raise translate_value_error(exc) from exc
|
||||
except (FileNotFoundError, IsADirectoryError) as exc:
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
return Response(
|
||||
content=download.content or b"",
|
||||
media_type=download.media_type,
|
||||
headers={"Content-Disposition": attachment_header(download.filename)},
|
||||
)
|
||||
|
||||
@app.get("/v1/files/archive", operation_id="archive_workspace_files")
|
||||
async def archive_workspace_files(
|
||||
identity: Identity,
|
||||
path: Annotated[str, Query(max_length=4096)] = ".",
|
||||
) -> StreamingResponse:
|
||||
try:
|
||||
download = await executor().archive_files(identity.user_id, path)
|
||||
except ValueError as exc:
|
||||
raise translate_value_error(exc) from exc
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
return StreamingResponse(
|
||||
download.chunks or iter(()),
|
||||
media_type=download.media_type,
|
||||
headers={"Content-Disposition": attachment_header(download.filename)},
|
||||
)
|
||||
|
||||
@app.post("/v1/tools/read_file", response_model=ToolResult, operation_id="read_file")
|
||||
async def read_file(body: ReadFileRequest, identity: Identity) -> ToolResult:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user