Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/audio-transcription/audio_transcription/main.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""audio transcribe plugin"""

import logging
from typing import TypedDict
from typing import List, TypedDict

from pydantic import DirectoryPath
import typer
from rb.api.models import (
BatchTextResponse,
DirectoryInput,
FileFilterDirectory,
InputSchema,
InputType,
ResponseBody,
Expand All @@ -19,7 +21,7 @@
logger = logging.getLogger(__name__)

logging.basicConfig(
level=logging.INFO,
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)

Expand All @@ -35,9 +37,17 @@

model = AudioTranscriptionModel()

AUDIO_EXTENSIONS = {".mp3", ".wav", ".flac", ".aac"}


class AudioDirectory(FileFilterDirectory):

path: DirectoryPath
file_extensions: List[str] = AUDIO_EXTENSIONS


class AudioInput(TypedDict):
input_dir: DirectoryInput
input_dir: AudioDirectory


def task_schema() -> TaskSchema:
Expand Down
Empty file.
18 changes: 18 additions & 0 deletions src/audio-transcription/tests/test_main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from pathlib import Path

from rb.api.models import ResponseBody
Expand Down Expand Up @@ -55,3 +56,20 @@ def test_api_transcribe_command(self):
assert response.status_code == 200
body = ResponseBody(**response.json())
assert body.root.texts and "Twinkle" in body.root.texts[0].value

def test_negative_api_transcribe_command(self):
"""pass in valid directory but no audio files , expect 422 validation error"""
transcribe_api = f"/{APP_NAME}/transcribe"
full_path = Path.cwd() / "src" / "audio-transcription" / "tests" / "negative"
input_json = {
"inputs": {
"input_dir": {
"path": str(full_path),
}
}
}
response = self.client.post(transcribe_api, json=input_json)
assert response.status_code == 422
resp = json.loads(json.dumps(response.json()))
print(f"Response: {resp["detail"]["error"]}")
assert resp and "No file extensions matching" in resp["detail"]["error"]
17 changes: 16 additions & 1 deletion src/rb-api/rb/api/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import multiprocessing
import os
import sys
from fastapi import FastAPI
from fastapi import FastAPI, HTTPException, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.staticfiles import StaticFiles
from rb.api import routes

Expand All @@ -21,6 +22,20 @@
name="static",
)


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError): # fmt: skip
"""response handler for all plugin input validation errors"""
error_msg = str(exc)
for e in exc.errors():
error_msg = e.get("msg")

raise HTTPException( # pylint: disable=raise-missing-from
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail={"error": f"{error_msg}"},
)


app.include_router(routes.probes_router, prefix="/probes")
app.include_router(routes.cli_to_api_router)
app.include_router(routes.ui_router)
Expand Down
48 changes: 47 additions & 1 deletion src/rb-api/rb/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@
from __future__ import annotations

from enum import Enum
from pathlib import Path
from typing import Annotated, Any, Dict, List, Literal, Optional, Union

from pydantic import BaseModel, ConfigDict, DirectoryPath, Field, FilePath, RootModel
from pydantic import (
BaseModel,
ConfigDict,
DirectoryPath,
Field,
FilePath,
RootModel,
field_validator,
model_validator,
)

API_APPMETDATA = "app_metadata"
API_ROUTES = "routes"
Expand Down Expand Up @@ -51,6 +61,42 @@ class DirectoryInput(BaseModel):
path: DirectoryPath


class FileFilterDirectory(DirectoryInput):
"""Find files with file_extensions in path directory."""

model_config = ConfigDict(
populate_by_name=True,
)
path: str
file_extensions: List[str]

@field_validator("path")
@classmethod
def validate_directory(cls, v):
path = Path(v)

if not path.exists():
raise ValueError(f"validate directory: '{v}' does not exist.")
if not path.is_dir():
raise ValueError(f"validate directory: Path '{v}' is not a directory.")
return v

@model_validator(mode="after")
def file_filter(self) -> "FileFilterDirectory":
path_obj = Path(self.path)
files = list(path_obj.glob("*"))
if not files:
raise ValueError(f"validate directory: Directory {path_obj} is empty.")
number_of_matched_files = [
f.name for f in files if f.suffix.lower() in self.file_extensions
]
if len(number_of_matched_files) < 1:
raise ValueError(
f"validate directory: No file extensions matching {self.file_extensions} found in directory: {path_obj}"
)
return self


class TextInput(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
Expand Down
9 changes: 5 additions & 4 deletions src/rb-api/rb/api/routes/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Callable, Generator, Optional

import typer
from fastapi import APIRouter, HTTPException, Response
from fastapi import APIRouter, HTTPException, Response, status
from fastapi.responses import StreamingResponse
from makefun import with_signature
from pydantic import BaseModel
Expand Down Expand Up @@ -68,10 +68,11 @@ def static_endpoint(callback: Callable, *args, **kwargs) -> ResponseBody:
# this has an issue of nor sending back details to desktop ui the api caller ?
raise ValueError(f"Invalid return type from Typer command: {type(result)}")
except Exception as e:
logger.error("Error executing CLI command: %s", e)
# response handler for all plugin runtime errors
logger.error("Error: %s %s", e, stdout)
raise HTTPException( # pylint: disable=raise-missing-from
status_code=400,
detail={"error": f"Typer CLI aborted {e}", "stdout": stdout[-10:]},
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={"error": f"{e}"},
)


Expand Down
60 changes: 0 additions & 60 deletions src/rb-lib/rb/lib/abstract_parser.py

This file was deleted.

4 changes: 2 additions & 2 deletions src/rb-lib/rb/lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ def ensure_ml_func_hinting_and_task_schemas_are_valid(
input_type_hint is FileInput
), f"For key {key}, the input type is NewFileInputType, but the TypeDict hint is {input_type_hint}. Change to FileInput."
case InputType.DIRECTORY:
assert (
input_type_hint is DirectoryInput
assert issubclass(
input_type_hint, DirectoryInput
), f"For key {key}, the input type is InputType.DIRECTORY, but the TypeDict hint is {input_type_hint}. Change to DirectoryInput."
case InputType.TEXT:
assert (
Expand Down