Skip to content
Open
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
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fastapi==0.95.2
uvicorn[standard]
uvicorn[standard]==0.32.1
sse-starlette==1.6.5
pyinstaller==5.13.0
pyinstaller==5.13.0
pydantic==1.10.19
74 changes: 58 additions & 16 deletions src/backends/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,16 @@ def kill_process():
os.kill(os.getpid(), signal.SIGINT) # This force closes this script.


# Graceful shutdown handler
async def shutdown_server():
global server_instance
if server_instance:
print("[sidecar] Shutting down server gracefully...", flush=True)
await server_instance.shutdown()


# Programmatically startup the api server
def start_api_server(**kwargs):
async def start_api_server_async(**kwargs):
global server_instance
port = kwargs.get("port", PORT_API)
try:
Expand All @@ -74,32 +82,56 @@ def start_api_server(**kwargs):
config = Config(app, host="0.0.0.0", port=port, log_level="info")
server_instance = Server(config)
# Start the ASGI server
asyncio.run(server_instance.serve())
await server_instance.serve()
else:
print(
"[sidecar] Failed to start new server. Server instance already running.",
flush=True,
)
except KeyboardInterrupt:
print("[sidecar] Received shutdown signal, stopping server...", flush=True)
await shutdown_server()
except Exception as e:
print(f"[sidecar] Error, failed to start API server {e}", flush=True)


def start_api_server(**kwargs):
try:
asyncio.run(start_api_server_async(**kwargs))
except KeyboardInterrupt:
print("[sidecar] Server shutdown complete.", flush=True)
except Exception as e:
print(f"[sidecar] Unexpected error during server operation: {e}", flush=True)


# Handle the stdin event loop. This can be used like a CLI.
def stdin_loop():
print("[sidecar] Waiting for commands...", flush=True)
while True:
# Read input from stdin.
user_input = sys.stdin.readline().strip()

# Check if the input matches one of the available functions
match user_input:
case "sidecar shutdown":
print("[sidecar] Received 'sidecar shutdown' command.", flush=True)
kill_process()
case _:
print(
f"[sidecar] Invalid command [{user_input}]. Try again.", flush=True
)
try:
# Read input from stdin.
user_input = sys.stdin.readline().strip()

# If stdin is closed (EOF), break the loop
if not user_input and sys.stdin.closed:
break

# Check if the input matches one of the available functions
match user_input:
case "sidecar shutdown":
print("[sidecar] Received 'sidecar shutdown' command.", flush=True)
kill_process()
case _:
if user_input: # Only print if there's actual input
print(
f"[sidecar] Invalid command [{user_input}]. Try again.",
flush=True,
)
except (EOFError, KeyboardInterrupt):
print("[sidecar] Input handler shutting down...", flush=True)
break
except Exception as e:
print(f"[sidecar] Error in input handler: {e}", flush=True)


# Start the input loop in a separate thread
Expand All @@ -108,11 +140,21 @@ def start_input_thread():
input_thread = threading.Thread(target=stdin_loop)
input_thread.daemon = True # so it exits when the main program exits
input_thread.start()
except:
print("[sidecar] Failed to start input handler.", flush=True)
except Exception as e:
print(f"[sidecar] Failed to start input handler: {e}", flush=True)


# Signal handler for graceful shutdown
def signal_handler(signum, frame):
print(f"[sidecar] Received signal {signum}, initiating shutdown...", flush=True)
sys.exit(0)


if __name__ == "__main__":
# Set up signal handlers for graceful shutdown
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

# You can spawn sub-processes here before the main process.
# new_command = ["python", "-m", "some_script", "--arg", "argValue"]
# subprocess.Popen(new_command)
Expand Down