|
5 | 5 | import logging
|
6 | 6 | from typing import Any, Callable, Optional
|
7 | 7 |
|
8 |
| -from fastapi import FastAPI, HTTPException, status |
| 8 | +from fastapi import FastAPI, status |
9 | 9 | from pydantic import BaseModel
|
10 | 10 | from starlette.responses import RedirectResponse
|
11 | 11 | from uvicorn.importer import import_from_string
|
|
21 | 21 | from unstructured_platform_plugins.schema.json_schema import (
|
22 | 22 | schema_to_base_model,
|
23 | 23 | )
|
| 24 | +from unstructured_platform_plugins.schema.usage import UsageData |
24 | 25 |
|
25 | 26 | logger = logging.getLogger("uvicorn.error")
|
26 | 27 |
|
@@ -54,44 +55,56 @@ def generate_fast_api(
|
54 | 55 |
|
55 | 56 | response_type = get_output_sig(func)
|
56 | 57 |
|
57 |
| - input_schema_model = schema_to_base_model(get_input_schema(func)) |
| 58 | + class InvokeResponse(BaseModel): |
| 59 | + usage: list[UsageData] |
| 60 | + status_code: int |
| 61 | + status_code_text: Optional[str] = None |
| 62 | + output: Optional[response_type] = None |
| 63 | + |
| 64 | + input_schema = get_input_schema(func, omit=["usage"]) |
| 65 | + input_schema_model = schema_to_base_model(input_schema) |
58 | 66 |
|
59 | 67 | logging.getLogger("etl_uvicorn.fastapi")
|
60 | 68 |
|
| 69 | + usage: list[UsageData] = [] |
| 70 | + |
| 71 | + async def wrap_fn(func: Callable, kwargs: Optional[dict[str, Any]] = None) -> InvokeResponse: |
| 72 | + request_dict = kwargs if kwargs else {} |
| 73 | + if "usage" in inspect.signature(func).parameters: |
| 74 | + request_dict["usage"] = usage |
| 75 | + else: |
| 76 | + logger.warning("usage data not an expected parameter, omitting") |
| 77 | + try: |
| 78 | + output = await invoke_func(func=func, kwargs=request_dict) |
| 79 | + return InvokeResponse(usage=usage, status_code=status.HTTP_200_OK, output=output) |
| 80 | + except Exception as invoke_error: |
| 81 | + logger.error(f"failed to invoke plugin: {invoke_error}", exc_info=True) |
| 82 | + return InvokeResponse( |
| 83 | + usage=usage, |
| 84 | + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| 85 | + status_code_text=f"failed to invoke plugin: " |
| 86 | + f"[{invoke_error.__class__.__name__}] {invoke_error}", |
| 87 | + ) |
| 88 | + |
61 | 89 | if input_schema_model.model_fields:
|
62 |
| - logger.debug(f"input model set to: {input_schema_model.model_fields}") |
63 | 90 |
|
64 |
| - @fastapi_app.post("/invoke", response_model=response_type) |
65 |
| - async def run_job(request: input_schema_model) -> response_type: |
| 91 | + @fastapi_app.post("/invoke", response_model=InvokeResponse) |
| 92 | + async def run_job(request: input_schema_model) -> InvokeResponse: |
66 | 93 | logger.debug(f"invoking function {func} with input: {request.model_dump()}")
|
67 | 94 | # Create dictionary from pydantic model while preserving underlying types
|
68 | 95 | request_dict = {f: getattr(request, f) for f in request.model_fields}
|
69 | 96 | map_inputs(func=func, raw_inputs=request_dict)
|
70 | 97 | logger.debug(f"passing inputs to function: {request_dict}")
|
71 |
| - try: |
72 |
| - return await invoke_func(func=func, kwargs=request_dict) |
73 |
| - except Exception as e: |
74 |
| - logger.error( |
75 |
| - f"failed to invoke plugin with inputs {request_dict}: {e}", exc_info=True |
76 |
| - ) |
77 |
| - raise HTTPException( |
78 |
| - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
79 |
| - detail=f"failed to invoke plugin: {e}", |
80 |
| - ) |
| 98 | + return await wrap_fn(func=func, kwargs=request_dict) |
81 | 99 |
|
82 | 100 | else:
|
83 | 101 |
|
84 | 102 | @fastapi_app.post("/invoke", response_model=response_type)
|
85 | 103 | async def run_job() -> response_type:
|
86 | 104 | logger.debug(f"invoking function without inputs: {func}")
|
87 |
| - try: |
88 |
| - return await invoke_func(func=func) |
89 |
| - except Exception as e: |
90 |
| - logger.error(f"failed to invoke plugin: {e}", exc_info=True) |
91 |
| - raise HTTPException( |
92 |
| - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
93 |
| - detail=f"failed to invoke plugin: {e}", |
94 |
| - ) |
| 105 | + return await wrap_fn( |
| 106 | + func=func, |
| 107 | + ) |
95 | 108 |
|
96 | 109 | class SchemaOutputResponse(BaseModel):
|
97 | 110 | inputs: dict[str, Any]
|
|
0 commit comments