|
1 | | -# RESTful Wrapper Application for MONAI Deploy |
| 1 | +# Creating REST Service with MONAI Deploy Application |
2 | 2 |
|
3 | | -This application provides a RESTful web interface to run MONAI Deploy applications. |
| 3 | +This application provides an example of how to make a MONAI Deploy app run as a REST service on [Aidoc](https://www.aidoc.com/) platform. It is compliant with its [third party integration API](https://ai-partner-sdk.aidoc-cloud.com/prod/api/third-parties/doc/#), and the results [callback message schema](https://ai-partner-sdk.aidoc-cloud.com/prod/api/aidoc-callback/doc/#). |
4 | 4 |
|
5 | | -It allows you to start a processing job, check the status, and receive a callback when the job is complete. |
| 5 | +This example uses a subset of the callback message attributes, covering only the required ones as well as some common attributes. For the full message definition, please contact Aidoc directly. |
6 | 6 |
|
7 | | -As it stands now, the callback message content is stubbed/generated in the wrapper app, and this will change to the design |
8 | | -where the wrapper app will pass a static callback function to the MONAI Deploy app which will have a reporter operator |
9 | | -that gathers the operations and domain specific info in the app's pipeline and then reports back the content via |
10 | | -this callback. The wrapper app will then have a mapping function to transform the reported data to that expected by |
11 | | -the external callback endpoint. |
| 7 | +## High Level Design |
12 | 8 |
|
13 | | -Also, the whole Restful application can be packaged into a container image using MONAI Deploy app packager, but not doner here. |
| 9 | +The high-level design of this REST service involves a few key components: |
| 10 | + |
| 11 | +1. **MONAI Deploy Application**: The core AI logic is encapsulated in a standard MONAI Deploy application (e.g., `AISpleenSegApp`), which is built and tested as a regular containerized workload. |
| 12 | +2. **RESTful Service**: A lightweight RESTful application, built using Flask, acts as the front-end. It exposes endpoints to start and check the status of a processing job. |
| 13 | +3. **Request Handling**: |
| 14 | + - When the RESTful service receives a request to process data, it handles only one request at a time, as per the API specification. |
| 15 | + - It creates an instance of the MONAI Deploy application. |
| 16 | + - It sets the necessary environment variables for the input and output folders. |
| 17 | + - Crucially, it delegates the execution of the MONAI Deploy application to a separate background thread to avoid blocking the web server. |
| 18 | +4. **Callback Mechanism**: |
| 19 | + - The callback message, which includes the AI results and a list of output files, is generated within the MONAI Deploy application at the end of its run. |
| 20 | + - This message is then passed to a callback function that was provided by the REST service during the creation of the MONAI Deploy app instance. |
| 21 | + - The REST service, upon receiving the callback, is then responsible for making the final `POST` request to the external callback endpoint specified by the original caller. |
| 22 | + |
| 23 | +This design separates the core AI application from the web-serving logic, allowing each to be developed and tested independently. |
| 24 | + |
| 25 | +## Diagrams |
| 26 | + |
| 27 | +### Component Diagram |
| 28 | + |
| 29 | +This diagram shows the static components of the system and their relationships using the C4 model. |
| 30 | + |
| 31 | +```mermaid |
| 32 | +C4Component |
| 33 | + title Component Diagram for MONAI Deploy REST Service |
| 34 | +
|
| 35 | + Person(client, "External Client", "e.g., Aidoc Platform") |
| 36 | +
|
| 37 | + Container_Boundary(rest_service_container, "RESTful Service") { |
| 38 | + Component(flask, "Flask App", "Python, Flask", "Handles HTTP requests, manages processing threads, and sends callbacks.") |
| 39 | + } |
| 40 | +
|
| 41 | + Container_Boundary(monai_app_container, "MONAI Deploy Application") { |
| 42 | + Component(monai_app, "AISpleenSegApp", "Python, MONAI Deploy SDK", "Orchestrates the AI inference pipeline and prepares the result message.") |
| 43 | + Component(operators, "MONAI Deploy Operators", "Python, MONAI Deploy SDK", "Perform tasks like data loading, inference, and writing results.") |
| 44 | + } |
| 45 | +
|
| 46 | + System_Ext(fs, "Filesystem", "Stores input/output data.") |
| 47 | +
|
| 48 | + Rel(client, flask, "1. Sends processing request", "JSON/HTTPS") |
| 49 | + Rel(flask, client, "2. Responds 202 Accepted") |
| 50 | + Rel(flask, monai_app, "3. Instantiates & runs in background thread") |
| 51 | + Rel(monai_app, operators, "4. Uses operators to process data") |
| 52 | + Rel(monai_app, fs, "5. Reads from & Writes to") |
| 53 | + Rel(monai_app, flask, "6. Invokes callback on completion") |
| 54 | + Rel(flask, client, "7. Sends final results", "JSON/HTTPS") |
| 55 | +``` |
| 56 | + |
| 57 | +### Sequence Diagram |
| 58 | + |
| 59 | +This diagram illustrates the sequence of interactions for a processing job, including status checks. |
| 60 | + |
| 61 | +```mermaid |
| 62 | +sequenceDiagram |
| 63 | + actor Client |
| 64 | + participant RESTful Service |
| 65 | + participant "MONAI Deploy App Thread" as AppThread |
| 66 | + participant AISpleenSegApp |
| 67 | +
|
| 68 | + Client->>+RESTful Service: POST /process (payload) |
| 69 | + RESTful Service-->>-Client: HTTP 202 Accepted |
| 70 | + RESTful Service->>+AppThread: Spawn thread(run_processing) |
| 71 | +
|
| 72 | + opt While processing is busy |
| 73 | + Client->>+RESTful Service: POST /process (payload) |
| 74 | + RESTful Service-->>-Client: HTTP 409 Conflict |
| 75 | +
|
| 76 | + Client->>+RESTful Service: GET /status |
| 77 | + RESTful Service-->>-Client: HTTP 200 OK ("status": "BUSY") |
| 78 | + end |
| 79 | +
|
| 80 | + AppThread->>+AISpleenSegApp: Create instance(status_callback) |
| 81 | + AppThread->>AISpleenSegApp: run() |
| 82 | + Note over AISpleenSegApp: Executes processing pipeline... |
| 83 | + AISpleenSegApp->>AISpleenSegApp: Formats success message |
| 84 | + AISpleenSegApp->>AppThread: status_callback (message) |
| 85 | +
|
| 86 | + AISpleenSegApp-->>AppThread: run() completes successfully |
| 87 | + deactivate AISpleenSegApp |
| 88 | + AppThread->>AppThread: Formats final message |
| 89 | + AppThread->>+Client: POST callback_url (Final Results) |
| 90 | + Client-->>-AppThread: HTTP 200 OK |
| 91 | +
|
| 92 | + Note over RESTful Service: Processing status set to IDLE. |
| 93 | + deactivate AppThread |
| 94 | +
|
| 95 | + Client->>+RESTful Service: GET /status |
| 96 | + RESTful Service-->>-Client: HTTP 200 OK ("status": "IDLE") |
| 97 | +``` |
14 | 98 |
|
15 | 99 | ## How to Run |
16 | 100 |
|
@@ -131,10 +215,25 @@ When processing is complete, the application will send a `POST` request to the ` |
131 | 215 | ```json |
132 | 216 | { |
133 | 217 | "run_success": true, |
134 | | - "result": "Processing completed successfully.", |
135 | | - "output_files": ["test.json", "seg.com"], |
| 218 | + "output_files": ["output_spleen/1.2.826.0.1.3680043.10.511.3.13787585732573161684951883631909444.dcm", "output_spleen/stl/spleen.stl"], |
136 | 219 | "error_message": null, |
137 | | - "error_code": null |
| 220 | + "error_code": null, |
| 221 | + "result": { |
| 222 | + "aggregated_results": { |
| 223 | + "name": "Spleen Segmentation", |
| 224 | + "algorithm_class": ["Measurement"] |
| 225 | + }, |
| 226 | + "detailed_results":{ |
| 227 | + "Spleen Segmentation": { |
| 228 | + "detection": null, |
| 229 | + "measurement": { |
| 230 | + "measurements_text": "Spleen segmentation completed successfully.", "key_slice_instance_uid": null, |
| 231 | + "key_measurement": null |
| 232 | + }, |
| 233 | + "classification": null |
| 234 | + } |
| 235 | + } |
| 236 | + } |
138 | 237 | } |
139 | 238 | ``` |
140 | 239 |
|
|
0 commit comments