2
2
import time
3
3
import json
4
4
import logging
5
- from typing import Callable , Generator , Optional , Any
5
+ from typing import Callable , Generator , Optional
6
6
from pydantic import BaseModel
7
-
8
7
import typer
9
- from fastapi import APIRouter , HTTPException , Query
8
+ from fastapi import APIRouter , HTTPException
10
9
from fastapi .responses import StreamingResponse
11
10
from makefun import with_signature
12
11
from rb .lib .stdout import Capturing # type: ignore
21
20
BatchTextResponse ,
22
21
BatchDirectoryResponse ,
23
22
)
24
-
23
+ from rb . api . models import API_APPMETDATA , API_ROUTES , PLUGIN_SCHEMA_SUFFIX
25
24
from rescuebox .main import app as rescuebox_app
26
-
27
25
logging .basicConfig (level = logging .DEBUG )
28
26
logger = logging .getLogger (__name__ )
29
27
30
- cli_router = APIRouter ()
28
+ cli_to_api_router = APIRouter ()
31
29
32
30
33
31
def static_endpoint (callback : Callable , * args , ** kwargs ) -> ResponseBody :
@@ -36,14 +34,24 @@ def static_endpoint(callback: Callable, *args, **kwargs) -> ResponseBody:
36
34
try :
37
35
logger .debug (f"Executing CLI command: { callback .__name__ } with args={ args } , kwargs={ kwargs } " )
38
36
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 )} " )
40
39
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
42
43
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 ?
43
51
raise ValueError (f"Invalid return type from Typer command: { type (result )} " )
44
52
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
47
55
status_code = 400 ,
48
56
detail = {"error" : f"Typer CLI aborted { e } " , "stdout" : stdout [- 10 :]},
49
57
)
@@ -52,7 +60,7 @@ def static_endpoint(callback: Callable, *args, **kwargs) -> ResponseBody:
52
60
def streaming_endpoint (callback : Callable , * args , ** kwargs ) -> Generator :
53
61
"""Execute a CLI command and stream the results with proper response handling"""
54
62
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 } " )
56
64
57
65
for line in capture_stdout_as_generator (callback , * args , ** kwargs ):
58
66
try :
@@ -96,20 +104,11 @@ def streaming_endpoint(callback: Callable, *args, **kwargs) -> Generator:
96
104
time .sleep (0.01 )
97
105
98
106
99
-
100
107
def command_callback (command : typer .models .CommandInfo ):
101
108
"""Create a FastAPI endpoint handler for a Typer CLI command with `ResponseBody`"""
102
109
103
110
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 ())
113
112
114
113
streaming_param = inspect .Parameter (
115
114
"streaming" ,
@@ -144,14 +143,30 @@ def wrapper(*args, **kwargs) -> ResponseBody:
144
143
router = APIRouter ()
145
144
146
145
for command in plugin .typer_instance .registered_commands :
147
- logger .debug (f"Registering FastAPI route for CLI command: { command .callback .__name__ } " )
148
146
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 ])
0 commit comments