Skip to content

Commit bd00513

Browse files
authored
Merge pull request #6 from UMass-Rescue/rescuebox-desktop-compliant
make audio transcribe work with rescuebox desktop
2 parents 6690fe4 + ef8ef69 commit bd00513

File tree

18 files changed

+1035
-457
lines changed

18 files changed

+1035
-457
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ __pycache__/
33
*.py[cod]
44
*$py.class
55

6+
.vscode/
7+
68
# C extensions
79
*.so
810

@@ -24,6 +26,7 @@ share/python-wheels/
2426
.installed.cfg
2527
*.egg
2628
MANIFEST
29+
*.bat
2730

2831
# PyInstaller
2932
# Usually these files are written by a python script from a template

docs

Submodule docs updated from 1ba336e to 37198c4

poetry.lock

+251-177
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ python = ">=3.11,<3.13"
1414
pyyaml = "^6.0.2"
1515
typer = "^0.12.5"
1616
llvmlite = "^0.44.0"
17+
pytest = "^8.3.4"
18+
httpx = "^0.28.1"
19+
# add dependencies common to all plugins here
1720
numba = "^0.61.0"
21+
torch = "2.2.2"
22+
numpy = "1.26.4"
1823

1924
rb-lib = { path = "src/rb-lib", develop = true }
2025

@@ -23,8 +28,8 @@ rb-doc-parser = { path = "src/rb-doc-parser", develop = true }
2328
rb-audio-transcription = { path = "src/rb-audio-transcription", develop = true }
2429

2530
# Don't add new packages here, add them appropriately in the list above
26-
torch = "2.2.2"
27-
numpy = "1.26.4"
31+
32+
2833

2934

3035
[tool.poetry.group.dev.dependencies]

src/rb-api/rb/api/main.py

+46-3
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,72 @@
11
import multiprocessing
22
import os
3-
3+
from starlette.middleware.base import BaseHTTPMiddleware
4+
import logging
5+
from logging.config import dictConfig
46
from fastapi import FastAPI
57
from fastapi.staticfiles import StaticFiles
68
from rb.api import routes
79

10+
log_config = {
11+
"version": 1,
12+
"disable_existing_loggers": False,
13+
"formatters": {
14+
"default": {
15+
"()": "uvicorn.logging.DefaultFormatter",
16+
"fmt": "%(levelprefix)s %(asctime)s %(message)s",
17+
"datefmt": "%Y-%m-%d %H:%M:%S",
18+
19+
},
20+
},
21+
"handlers": {
22+
"default": {
23+
"formatter": "default",
24+
"class": "logging.StreamHandler",
25+
"stream": "ext://sys.stderr",
26+
},
27+
},
28+
"loggers": {
29+
"foo-logger": {"handlers": ["default"], "level": "DEBUG"},
30+
},
31+
}
32+
dictConfig(log_config)
33+
34+
35+
36+
class LogMiddleware(BaseHTTPMiddleware):
37+
async def dispatch(self, request, call_next):
38+
logging.debug(f"Request: {request.method} {request.url}")
39+
response = await call_next(request)
40+
logging.debug(f"Response: {response.status_code}")
41+
return response
42+
43+
44+
845
app = FastAPI(
946
title="RescueBoxAPI",
1047
summary="RescueBox is a set of tools for file system investigations.",
1148
version="0.1.0",
49+
debug=True,
1250
contact={
13-
"name": "Jagath Jai Kumar",
51+
"name": "Umass Amherst RescuBox Team",
1452
},
1553
)
1654

55+
app.add_middleware(LogMiddleware)
56+
1757
app.mount(
1858
"/static",
1959
StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")),
2060
name="static",
2161
)
2262

2363
app.include_router(routes.probes_router, prefix="/probes")
24-
app.include_router(routes.cli_router)
64+
app.include_router(routes.cli_to_api_router)
2565
app.include_router(routes.ui_router)
2666

67+
68+
69+
2770
if __name__ == "__main__":
2871
import uvicorn
2972

src/rb-api/rb/api/models.py

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
from pydantic import BaseModel, ConfigDict, Field, RootModel
1111

12+
API_APPMETDATA="app_metadata"
13+
API_ROUTES="routes"
14+
PLUGIN_SCHEMA_SUFFIX="_schema"
1215

1316
class AppMetadata(BaseModel):
1417
model_config = ConfigDict(

src/rb-api/rb/api/routes/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from .cli import cli_router
1+
from .cli import cli_to_api_router
22
from .probes import probes_router
33
from .ui import ui_router
44

5-
__all__ = ["cli_router", "probes_router", "ui_router"]
5+
__all__ = ["cli_to_api_router", "probes_router", "ui_router"]

src/rb-api/rb/api/routes/cli.py

+46-31
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
import time
33
import json
44
import logging
5-
from typing import Callable, Generator, Optional, Any
5+
from typing import Callable, Generator, Optional
66
from pydantic import BaseModel
7-
87
import typer
9-
from fastapi import APIRouter, HTTPException, Query
8+
from fastapi import APIRouter, HTTPException
109
from fastapi.responses import StreamingResponse
1110
from makefun import with_signature
1211
from rb.lib.stdout import Capturing # type: ignore
@@ -21,13 +20,12 @@
2120
BatchTextResponse,
2221
BatchDirectoryResponse,
2322
)
24-
23+
from rb.api.models import API_APPMETDATA, API_ROUTES, PLUGIN_SCHEMA_SUFFIX
2524
from rescuebox.main import app as rescuebox_app
26-
2725
logging.basicConfig(level=logging.DEBUG)
2826
logger = logging.getLogger(__name__)
2927

30-
cli_router = APIRouter()
28+
cli_to_api_router = APIRouter()
3129

3230

3331
def static_endpoint(callback: Callable, *args, **kwargs) -> ResponseBody:
@@ -36,14 +34,24 @@ def static_endpoint(callback: Callable, *args, **kwargs) -> ResponseBody:
3634
try:
3735
logger.debug(f"Executing CLI command: {callback.__name__} with args={args}, kwargs={kwargs}")
3836
result = callback(*args, **kwargs) # Ensure this returns a valid Pydantic model
39-
logger.debug(f"CLI command output: {result}")
37+
38+
logger.debug(f"CLI command output type: {type(result)}")
4039

41-
if isinstance(result, BaseModel): # Ensure it's a valid Pydantic model
40+
if isinstance(result, ResponseBody): # Ensure it's a valid Pydantic model
41+
return result
42+
if isinstance(result, BaseModel): # Ensure it's a valid Pydantic model , not sure if this is needed for desktop calls
4243
return ResponseBody(root=result)
44+
if isinstance(result, dict): # or Ensure it's a valid dict model for desktop app metadata call to work
45+
return result
46+
if isinstance(result, list): # or Ensure it's a valid str model for routes call
47+
return result
48+
if isinstance(result, str): # or Ensure it's a valid str model for routes call
49+
return ResponseBody(root=TextResponse(value=result))
50+
# this has an issue of nor sending back details to desktop ui the api caller ?
4351
raise ValueError(f"Invalid return type from Typer command: {type(result)}")
4452
except Exception as e:
45-
logger.error(f"Error executing CLI command: {e}")
46-
raise HTTPException(
53+
logger.error("Error executing CLI command: %s", e)
54+
raise HTTPException( # pylint: disable=raise-missing-from
4755
status_code=400,
4856
detail={"error": f"Typer CLI aborted {e}", "stdout": stdout[-10:]},
4957
)
@@ -52,7 +60,7 @@ def static_endpoint(callback: Callable, *args, **kwargs) -> ResponseBody:
5260
def streaming_endpoint(callback: Callable, *args, **kwargs) -> Generator:
5361
"""Execute a CLI command and stream the results with proper response handling"""
5462

55-
logger.debug(f"🚀 Streaming started for command: {callback.__name__} with args={args}, kwargs={kwargs}")
63+
logger.debug(f"🚀Streaming started for command: {callback.__name__} with args={args}, kwargs={kwargs}")
5664

5765
for line in capture_stdout_as_generator(callback, *args, **kwargs):
5866
try:
@@ -96,20 +104,11 @@ def streaming_endpoint(callback: Callable, *args, **kwargs) -> Generator:
96104
time.sleep(0.01)
97105

98106

99-
100107
def command_callback(command: typer.models.CommandInfo):
101108
"""Create a FastAPI endpoint handler for a Typer CLI command with `ResponseBody`"""
102109

103110
original_signature = inspect.signature(command.callback)
104-
new_params = []
105-
106-
# Convert Typer CLI arguments to FastAPI-compatible query/body parameters
107-
for param in original_signature.parameters.values():
108-
if param.default is inspect.Parameter.empty: # Required argument
109-
param = param.replace(default=Query(..., description=f"Required parameter {param.name}"))
110-
elif param.default is Ellipsis: # Typer required argument
111-
param = param.replace(default=Query(..., description=f"Required parameter {param.name}"))
112-
new_params.append(param)
111+
new_params = list(original_signature.parameters.values())
113112

114113
streaming_param = inspect.Parameter(
115114
"streaming",
@@ -144,14 +143,30 @@ def wrapper(*args, **kwargs) -> ResponseBody:
144143
router = APIRouter()
145144

146145
for command in plugin.typer_instance.registered_commands:
147-
logger.debug(f"Registering FastAPI route for CLI command: {command.callback.__name__}")
148146

149-
router.add_api_route(
150-
f"/{command.callback.__name__}",
151-
endpoint=command_callback(command),
152-
methods=["POST"],
153-
name=command.callback.__name__,
154-
response_model=ResponseBody,
155-
)
156-
157-
cli_router.include_router(router, prefix=f"/{plugin.name}", tags=[plugin.name])
147+
148+
if command.name and (command.name == API_APPMETDATA or
149+
command.name == API_ROUTES or
150+
command.name.endswith(PLUGIN_SCHEMA_SUFFIX)):
151+
logger.debug(f'plugin command name is {command.name}')
152+
router.add_api_route(
153+
f"/{command.callback.__name__}",
154+
endpoint=command_callback(command),
155+
methods=["GET"],
156+
name=command.callback.__name__,
157+
)
158+
# FIXME: prefix /api to make desktop call happy for now , eventually this will go away
159+
# GOAL : /audio/routes is valid /api/routes should no longer work
160+
cli_to_api_router.include_router(router,prefix=f'/api', tags=[plugin.name])
161+
162+
logger.debug(f"Registering FastAPI route for {plugin.name} desktop call: {command.callback.__name__}")
163+
else:
164+
router.add_api_route(
165+
f"/{command.callback.__name__}",
166+
endpoint=command_callback(command),
167+
methods=["POST"],
168+
name=command.callback.__name__,
169+
response_model=ResponseBody,
170+
)
171+
logger.debug(f"Registering FastAPI route for {plugin.name} command: {command.callback.__name__}")
172+
cli_to_api_router.include_router(router, prefix=f"/{plugin.name}", tags=[plugin.name])

src/rb-api/rb/api/templates.py

-83
This file was deleted.

0 commit comments

Comments
 (0)