From 6bf474922d40204c1fc561b9f6f9ad8b0af18397 Mon Sep 17 00:00:00 2001 From: Ben Cherry Date: Wed, 30 Oct 2024 10:35:05 -0700 Subject: [PATCH] decorator --- README.md | 2 +- examples/rpc.py | 8 +-- livekit-rtc/livekit/rtc/participant.py | 81 +++++++++++--------------- 3 files changed, 39 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 78ef902f..baf7bda2 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ This feature is especially powerful when used with [Agents](https://docs.livekit The participant who implements the method and will receive its calls must first register support: ```python -@room.local_participant.rpc_method("greet") +@room.local_participant.register_rpc_method("greet") async def handle_greet(request_id: str, caller_identity: str, payload: str, response_timeout: float): print(f"Received greeting from {caller_identity}: {payload}") return f"Hello, {caller_identity}!" diff --git a/examples/rpc.py b/examples/rpc.py index 18bb9357..ac1ce337 100644 --- a/examples/rpc.py +++ b/examples/rpc.py @@ -89,7 +89,7 @@ async def main(): def register_receiver_methods(greeters_room: rtc.Room, math_genius_room: rtc.Room): - @greeters_room.local_participant.rpc_method("arrival") + @greeters_room.local_participant.register_rpc_method("arrival") async def arrival_method( data: RpcInvocationData, ): @@ -97,7 +97,7 @@ async def arrival_method( await asyncio.sleep(2) return "Welcome and have a wonderful day!" - @math_genius_room.local_participant.rpc_method("square-root") + @math_genius_room.local_participant.register_rpc_method("square-root") async def square_root_method( data: RpcInvocationData, ): @@ -114,7 +114,7 @@ async def square_root_method( print(f"[Math Genius] Aha! It's {result}") return json.dumps({"result": result}) - @math_genius_room.local_participant.rpc_method("divide") + @math_genius_room.local_participant.register_rpc_method("divide") async def divide_method( data: RpcInvocationData, ): @@ -128,7 +128,7 @@ async def divide_method( result = dividend / divisor return json.dumps({"result": result}) - @math_genius_room.local_participant.rpc_method("long-calculation") + @math_genius_room.local_participant.register_rpc_method("long-calculation") async def long_calculation_method( data: RpcInvocationData, ): diff --git a/livekit-rtc/livekit/rtc/participant.py b/livekit-rtc/livekit/rtc/participant.py index de7ba249..8a6aeb13 100644 --- a/livekit-rtc/livekit/rtc/participant.py +++ b/livekit-rtc/livekit/rtc/participant.py @@ -292,70 +292,57 @@ async def perform_rpc( def register_rpc_method( self, - method: str, - handler: Callable[[RpcInvocationData], Union[Awaitable[str], str]], - ) -> None: + method_name: str, + handler: Optional[Callable[[RpcInvocationData], Union[Awaitable[str], str]]] = None, + ) -> Union[None, Callable]: """ Establishes the participant as a receiver for calls of the specified RPC method. - Will overwrite any existing callback for the same method. + Can be used either as a decorator or a regular method. + + The handler will recieve one argument of type `RpcInvocationData` and should return a string response which will be forwarded back to the caller. + + The handler may be synchronous or asynchronous. + + If unable to respond within `response_timeout`, the caller will hang up and receive an error on their side. + + You may raise errors of type `RpcError` in the handler, and they will be forwarded to the caller. + + Other errors raised in your handler will be caught and forwarded to the caller as "1500 Application Error". Args: - method (str): The name of the indicated RPC method - handler (Callable): Will be invoked when an RPC request for this method is received + method_name (str): The name of the indicated RPC method. + handler (Optional[Callable]): Handler to be invoked whenever an RPC request for this method is received. Omit this argument to use the decorator syntax. Returns: - None - - Raises: - RpcError: On failure. Details in `message`. + None (when used as a decorator it returns the decorator function) Example: + # As a decorator: + @room.local_participant.register_rpc_method("greet") async def greet_handler(data: RpcInvocationData) -> str: print(f"Received greeting from {data.caller_identity}: {data.payload}") return f"Hello, {data.caller_identity}!" - - await room.local_participant.register_rpc_method('greet', greet_handler) - - The handler should return a string or a coroutine that resolves to a string. - - If unable to respond within `response_timeout`, the caller will hang up and receive an error on their side. - - You may raise errors of type `RpcError` with a string `message` in the handler, - and they will be received on the caller's side with the message intact. - Other errors raised in your handler will not be transmitted as-is, and will instead arrive to the caller as `1500` ("Application Error"). - """ - self._rpc_handlers[method] = handler - - req = proto_ffi.FfiRequest() - req.register_rpc_method.local_participant_handle = self._ffi_handle.handle - req.register_rpc_method.method = method - - FfiClient.instance.request(req) - - def rpc_method(self, method: str): - """ - Decorator form of `register_rpc_method` - - Args: - method (str): The name of the indicated RPC method - - Example: - @local_participant.rpc_method("greet") + + # As a regular method: async def greet_handler(data: RpcInvocationData) -> str: print(f"Received greeting from {data.caller_identity}: {data.payload}") - return f"Hello, {params.caller_identity}!" + return f"Hello, {data.caller_identity}!" - See Also: - `register_rpc_method` for more details + room.local_participant.register_rpc_method('greet', greet_handler) """ - def decorator( - handler: Callable[[RpcInvocationData], Union[Awaitable[str], str]], - ): - self.register_rpc_method(method, handler) - return handler + def register(handler_func): + self._rpc_handlers[method_name] = handler_func + req = proto_ffi.FfiRequest() + req.register_rpc_method.local_participant_handle = self._ffi_handle.handle + req.register_rpc_method.method = method_name + FfiClient.instance.request(req) - return decorator + if handler is not None: + register(handler) + else: + # Called as a decorator + return register def unregister_rpc_method(self, method: str) -> None: """