From c9413b1a8e297327d8ebff8586164ed105fab96f Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Thu, 12 Feb 2026 19:00:43 +0100 Subject: [PATCH 1/4] docs: slimrpc updates Signed-off-by: Mauro Sardara --- docs/slim/slim-rpc.md | 413 +++++++++++++++++++++++++++--------------- 1 file changed, 262 insertions(+), 151 deletions(-) diff --git a/docs/slim/slim-rpc.md b/docs/slim/slim-rpc.md index ef2b2e6..095edd2 100644 --- a/docs/slim/slim-rpc.md +++ b/docs/slim/slim-rpc.md @@ -24,7 +24,7 @@ documentation](./slim-slimrpc-compiler.md). In SLIMRPC, each service and its individual RPC handlers are assigned a SLIM name, facilitating efficient message routing and processing. Consider the [example -protobuf](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/example.proto) +protobuf](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple/example.proto) definition, which defines four distinct services: ```proto @@ -94,8 +94,8 @@ standard gRPC. ## Example This section provides a detailed walkthrough of a basic SLIMRPC client-server -interaction, leveraging the simple example provided in -[example](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/integrations/slimrpc/slimrpc/examples/simple) +interaction in python, leveraging the simple example provided in +[example](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple) folder. ### Generated Code @@ -118,32 +118,90 @@ RPC defined in example.proto, allowing clients to initiate calls to the server. ```python class TestStub: """Client stub for Test.""" + def __init__(self, channel): """Constructor. Args: - channel: A slimrpc.Channel. + channel: A slim_bindings.Channel. """ - self.ExampleUnaryUnary = channel.unary_unary( - "/example_service.Test/ExampleUnaryUnary", - request_serializer=pb2.ExampleRequest.SerializeToString, - response_deserializer=pb2.ExampleResponse.FromString, + self._channel = channel + + async def ExampleUnaryUnary(self, request: pb2.ExampleRequest, timeout: Optional[timedelta] = None, metadata: Optional[dict[str, str]] = None) -> pb2.ExampleResponse: + """Call ExampleUnaryUnary method.""" + response_bytes = await self._channel.call_unary_async( + "example_service.Test", + "ExampleUnaryUnary", + pb2.ExampleRequest.SerializeToString(request), + timeout, + metadata, ) - self.ExampleUnaryStream = channel.unary_stream( - "/example_service.Test/ExampleUnaryStream", - request_serializer=pb2.ExampleRequest.SerializeToString, - response_deserializer=pb2.ExampleResponse.FromString, + return pb2.ExampleResponse.FromString(response_bytes) + + async def ExampleUnaryStream(self, request: pb2.ExampleRequest, timeout: Optional[timedelta] = None, metadata: Optional[dict[str, str]] = None): + """Call ExampleUnaryStream method.""" + response_stream = await self._channel.call_unary_stream_async( + "example_service.Test", + "ExampleUnaryStream", + pb2.ExampleRequest.SerializeToString(request), + timeout, + metadata, ) - self.ExampleStreamUnary = channel.stream_unary( - "/example_service.Test/ExampleStreamUnary", - request_serializer=pb2.ExampleRequest.SerializeToString, - response_deserializer=pb2.ExampleResponse.FromString, + while True: + stream_msg = await response_stream.next_async() + if stream_msg.is_end(): + break + if stream_msg.is_error(): + raise stream_msg[0] + if stream_msg.is_data(): + yield pb2.ExampleResponse.FromString(stream_msg[0]) + + async def ExampleStreamUnary(self, request_iterator, timeout: Optional[timedelta] = None, metadata: Optional[dict[str, str]] = None) -> pb2.ExampleResponse: + """Call ExampleStreamUnary method.""" + request_stream = self._channel.call_stream_unary( + "example_service.Test", + "ExampleStreamUnary", + timeout, + metadata, ) - self.ExampleStreamStream = channel.stream_stream( - "/example_service.Test/ExampleStreamStream", - request_serializer=pb2.ExampleRequest.SerializeToString, - response_deserializer=pb2.ExampleResponse.FromString, + async for request in request_iterator: + await request_stream.send_async(pb2.ExampleRequest.SerializeToString(request)) + response_bytes = await request_stream.finalize_async() + return pb2.ExampleResponse.FromString(response_bytes) + + async def ExampleStreamStream(self, request_iterator, timeout: Optional[timedelta] = None, metadata: Optional[dict[str, str]] = None): + """Call ExampleStreamStream method.""" + bidi_stream = self._channel.call_stream_stream( + "example_service.Test", + "ExampleStreamStream", + timeout, + metadata, ) + + async def send_requests(): + async for request in request_iterator: + await bidi_stream.send_async(pb2.ExampleRequest.SerializeToString(request)) + await bidi_stream.close_send_async() + + async def receive_responses(): + while True: + stream_msg = await bidi_stream.recv_async() + if stream_msg.is_end(): + break + if stream_msg.is_error(): + raise stream_msg[0] + if stream_msg.is_data(): + yield pb2.ExampleResponse.FromString(stream_msg[0]) + + # Start sending in background + import asyncio + send_task = asyncio.create_task(send_requests()) + + try: + async for response in receive_responses(): + yield response + finally: + await send_task ``` _Server Servicer (TestServicer)_: The TestServicer class defines the server-side @@ -151,27 +209,30 @@ interface. Developers implement this class to provide the actual logic for each RPC method. ```python -class TestServicer(): +class TestServicer: """Server servicer for Test. Implement this class to provide your service logic.""" def ExampleUnaryUnary(self, request, context): """Method for ExampleUnaryUnary. Implement your service logic here.""" - raise slimrpc_rpc.SRPCResponseError( + raise slim_bindings.SRPCResponseError( code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" ) + def ExampleUnaryStream(self, request, context): """Method for ExampleUnaryStream. Implement your service logic here.""" - raise slimrpc_rpc.SRPCResponseError( + raise slim_bindings.SRPCResponseError( code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" ) + def ExampleStreamUnary(self, request_iterator, context): """Method for ExampleStreamUnary. Implement your service logic here.""" - raise slimrpc_rpc.SRPCResponseError( + raise slim_bindings.SRPCResponseError( code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" ) + def ExampleStreamStream(self, request_iterator, context): """Method for ExampleStreamStream. Implement your service logic here.""" - raise slimrpc_rpc.SRPCResponseError( + raise slim_bindings.SRPCResponseError( code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" ) ``` @@ -182,97 +243,93 @@ It maps RPC method names to their corresponding handlers and specifies the request deserialization and response serialization routines. ```python -def add_TestServicer_to_server(servicer, server: slimrpc.Server): - rpc_method_handlers = { - "ExampleUnaryUnary": slimrpc.unary_unary_rpc_method_handler( - behaviour=servicer.ExampleUnaryUnary, - request_deserializer=pb2.ExampleRequest.FromString, - response_serializer=pb2.ExampleResponse.SerializeToString, - ), - "ExampleUnaryStream": slimrpc.unary_stream_rpc_method_handler( - behaviour=servicer.ExampleUnaryStream, - request_deserializer=pb2.ExampleRequest.FromString, - response_serializer=pb2.ExampleResponse.SerializeToString, - ), - "ExampleStreamUnary": slimrpc.stream_unary_rpc_method_handler( - behaviour=servicer.ExampleStreamUnary, - request_deserializer=pb2.ExampleRequest.FromString, - response_serializer=pb2.ExampleResponse.SerializeToString, - ), - "ExampleStreamStream": slimrpc.stream_stream_rpc_method_handler( - behaviour=servicer.ExampleStreamStream, - request_deserializer=pb2.ExampleRequest.FromString, - response_serializer=pb2.ExampleResponse.SerializeToString, - ), - - } - - server.register_method_handlers( - "example_service.Test", - rpc_method_handlers, +def add_TestServicer_to_server(servicer, server: slim_bindings.Server): + server.register_unary_unary( + service_name="example_service.Test", + method_name="ExampleUnaryUnary", + handler=_TestServicer_ExampleUnaryUnary_Handler(servicer), + ) + server.register_unary_stream( + service_name="example_service.Test", + method_name="ExampleUnaryStream", + handler=_TestServicer_ExampleUnaryStream_Handler(servicer), + ) + server.register_stream_unary( + service_name="example_service.Test", + method_name="ExampleStreamUnary", + handler=_TestServicer_ExampleStreamUnary_Handler(servicer), + ) + server.register_stream_stream( + service_name="example_service.Test", + method_name="ExampleStreamStream", + handler=_TestServicer_ExampleStreamStream_Handler(servicer), ) ``` ### Server implementation The server-side logic is defined in -[server.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/server.py). +[server.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple/server.py). Similar to standard gRPC implementations, the core service functionality is provided by the TestService class, which inherits from TestServicer (as introduced in the previous section). This class contains the concrete implementations for each of the defined RPC methods. The SLIM-specific code and configuration is handled within the amain() -asynchronous function. This function utilizes the create_server helper to -instantiate an SLIMRPC server: +asynchronous function: ```python -def create_server( - local: str, - slim: dict, - enable_opentelemetry: bool = False, - shared_secret: str = "", -) -> Server: - """ - Create a new SLIMRPC server instance. - """ - server = Server( - local=local, - slim=slim, - enable_opentelemetry=enable_opentelemetry, - shared_secret=shared_secret, - ) +async def amain() -> None: + slim_bindings.uniffi_set_event_loop(asyncio.get_running_loop()) - return server + # Initialize service + tracing_config = slim_bindings.new_tracing_config() + runtime_config = slim_bindings.new_runtime_config() + service_config = slim_bindings.new_service_config() + tracing_config.log_level = "info" -async def amain() -> None: - server = create_server( - local="agntcy/grpc/server", - slim={ - "endpoint": "http://localhost:46357", - "tls": { - "insecure": True, - }, - }, - enable_opentelemetry=False, - shared_secret="my_shared_secret", + slim_bindings.initialize_with_configs( + tracing_config=tracing_config, + runtime_config=runtime_config, + service_config=[service_config], ) - # Create RPCs - add_TestServicer_to_server( - TestService(), - server, + service = slim_bindings.get_global_service() + + # Create local name + local_name = slim_bindings.Name("agntcy", "grpc", "server") + + # Connect to SLIM + client_config = slim_bindings.new_insecure_client_config("http://localhost:46357") + conn_id = await service.connect_async(client_config) + + # Create app with shared secret + local_app = service.create_app_with_secret( + local_name, "my_shared_secret_for_testing_purposes_only" ) - await server.run() + # Subscribe to local name + await local_app.subscribe_async(local_name, conn_id) + + # Create server + server = slim_bindings.Server.new_with_connection(local_app, local_name, conn_id) + + # Add servicer + add_TestServicer_to_server(TestService(), server) + + # Run server + await server.serve_async() ``` -A new server application is created using the `create_server` function. The -local parameter, set to "agntcy/grpc/server", assigns a SLIM name to this server -application. +The server setup begins by setting the event loop for the SLIM bindings with +`uniffi_set_event_loop()`, which is required for proper async operation. The +service is then initialized with tracing, runtime, and service configurations. +The log level is set to "info" for appropriate logging verbosity. -This name is then used to construct the full SLIMRPC names for each method: +A local SLIM name is created using `slim_bindings.Name("agntcy", "grpc", +"server")`. This name is used to construct the full SLIMRPC names for each +method: ``` agntcy/grpc/server-example_service.Test-ExampleUnaryUnary @@ -281,70 +338,118 @@ agntcy/grpc/server-example_service.Test-ExampleStreamUnary agntcy/grpc/server-example_service.Test-ExampleStreamStream ``` -Additionally, the `slim` dictionary configures the server to connect to a SLIM -node running at `http://localhost:46357`. The tls setting `insecure: True` -disables TLS for simplicity in this example. The `shared_secret` parameter is -used for initializing the Message Layer Security (MLS) protocol. Note that using -a hardcoded shared_secret like "my_shared_secret" is not recommended, please -refer to [the documentation for proper MLS -configuration](./slim-group.md). +The server connects to a SLIM node running at `http://localhost:46357` using an +insecure client configuration (TLS disabled for simplicity in this example). A +local app is created with a shared secret for initializing the Message Layer +Security (MLS) protocol. -Finally, the add_TestServicer_to_server function is called to register the -implemented TestService with the SLIMRPC server, making its RPC methods -available. +After subscribing to the local name on the connection, a SLIMRPC server is +created with `slim_bindings.Server.new_with_connection()`. The +`add_TestServicer_to_server()` function is then called to register the +implemented `TestService` with the server, making its RPC methods available: ```python - # Create RPCs - add_TestServicer_to_server( - TestService(), - server, - ) + # Add servicer + add_TestServicer_to_server(TestService(), server) ``` +Finally, the server starts serving requests with `await server.serve_async()`, +which runs indefinitely until interrupted. + ### Client implementation The client-side implementation, found in -[client.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/client.py), -largely mirrors the structure of a standard gRPC client. The primary distinction -and SLIM-specific aspect lies in the creation of the SLIMRPC channel: +[client.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple/client.py), +largely mirrors the structure of a standard gRPC client. The client +initialization follows the same pattern as the server: ```python - channel_factory = slimrpc.ChannelFactory( - slim_app_config=slimrpc.SLIMAppConfig( - identity="agntcy/grpc/client", - slim_client_config={ - "endpoint": "http://localhost:46357", - "tls": { - "insecure": True, - }, - }, - enable_opentelemetry=False, - shared_secret="my_shared_secret", - ), +async def amain() -> None: + # Initialize service + tracing_config = slim_bindings.new_tracing_config() + runtime_config = slim_bindings.new_runtime_config() + service_config = slim_bindings.new_service_config() + + tracing_config.log_level = "info" + + slim_bindings.initialize_with_configs( + tracing_config=tracing_config, + runtime_config=runtime_config, + service_config=[service_config], + ) + + service = slim_bindings.get_global_service() + + # Create local and remote names + local_name = slim_bindings.Name("agntcy", "grpc", "client") + remote_name = slim_bindings.Name("agntcy", "grpc", "server") + + # Connect to SLIM + client_config = slim_bindings.new_insecure_client_config("http://localhost:46357") + conn_id = await service.connect_async(client_config) + + # Create app with shared secret + local_app = service.create_app_with_secret( + local_name, "my_shared_secret_for_testing_purposes_only" ) - channel = channel_factory.new_channel(remote="agntcy/grpc/server") + # Subscribe to local name + await local_app.subscribe_async(local_name, conn_id) + + # Create channel + channel = slim_bindings.Channel.new_with_connection(local_app, remote_name, conn_id) - # Stubs + # Create stubs stubs = TestStub(channel) ``` -As opposite to the server, the client application only register its local name -`agntcy/grpc/client` in the SLIM network. This is done through the `identity` -parameter in the `SLIMAppConfig` class. This name will be then used by the -server to send back the response to the client. +The client initialization is very similar to the server setup. After initializing +the service with configurations, the client creates both a local name +(`agntcy/grpc/client`) and a remote name (`agntcy/grpc/server`). The local name +identifies the client in the SLIM network and is used by the server to send back +responses. + +Like the server, the client connects to the SLIM node at +`http://localhost:46357` using an insecure client configuration, creates an app +with a shared secret for MLS initialization, and subscribes to its local name. + +The key distinction from the server is the channel creation. The client uses +`slim_bindings.Channel.new_with_connection()` with the remote name +(`agntcy/grpc/server`) to establish a channel to the target server. This channel +is then used to create the `TestStub`, which provides type-safe access to all +the RPC methods defined in the protobuf. + +Once the stub is created, the client can invoke RPC methods using async/await +syntax: -Also, like in the case of the server application, the `slim` dictionary -specifies the SLIM node endpoint (`http://localhost:46357`) and TLS settings, -consistent with the server's configuration, while `shared_secret` is used to -initialize MLS. +```python + # Unary-Unary call + request = ExampleRequest(example_integer=1, example_string="hello") + response = await stubs.ExampleUnaryUnary(request, timeout=timedelta(seconds=2)) + + # Unary-Stream call + async for resp in stubs.ExampleUnaryStream(request, timeout=timedelta(seconds=2)): + logger.info(f"Stream Response: {resp}") + + # Stream-Unary call + async def stream_requests() -> AsyncGenerator[ExampleRequest, None]: + for i in range(10): + yield ExampleRequest(example_integer=i, example_string=f"Request {i}") + + response = await stubs.ExampleStreamUnary( + stream_requests(), timeout=timedelta(seconds=2) + ) -The remote parameter, set to `agntcy/grpc/server`, explicitly identifies the -SLIM name of the target server application. This allows the SLIMRPC channel to -correctly route messages to the appropriate server endpoint within the SLIM -network. Since both client and server use the same protobuf definition, the -client can invoke specific services and methods with type safety and -consistency. + # Stream-Stream call + async for resp in stubs.ExampleStreamStream( + stream_requests(), timeout=timedelta(seconds=2) + ): + logger.info(f"Stream Stream Response: {resp}") +``` + +All RPC calls support timeout parameters and use Python's async/await patterns. +Streaming requests are provided as async generators, and streaming responses are +consumed using `async for` loops. ## SLIMRPC under the Hood @@ -353,19 +458,25 @@ SLIM. From a developer's perspective, using SLIMRPC or gRPC is almost identical. Application developers do not need to manage endpoint names or connectivity details, as these aspects are handled automatically by SLIMRPC and SLIM. -All RPC services underneath utilize a sticky point-to-point session. The SLIM +All RPC services underneath utilize a point-to-point session. The SLIM session creation is implemented in inside SLIMRPC in -[channel.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slimrpc/slimrpc/channel.py): - -```python - # Create a session - session = await self.local_app.create_session( - slim_bindings.SessionConfiguration.FireAndForget( - max_retries=10, - timeout=datetime.timedelta(seconds=1), - sticky=True, - ) - ) +[channel.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/rust/src/slimrpc/channel.rs#L657-L670): + +```rust +let slim_config = slim_session::session_config::SessionConfig { + session_type: ProtoSessionType::PointToPoint, + mls_enabled: true, + max_retries: Some(10), + interval: Some(Duration::from_secs(1)), + initiator: true, + metadata: ctx.metadata(), +}; + +// Create session to the method-specific subscription name +let (session_ctx, completion) = app + .create_session(slim_config, method_subscription_name.clone(), None) + .await + .map_err(|e| Status::unavailable(format!("Failed to create session: {}", e)))?; ``` This session used by SLIMRPC is also reliable. For each message, the sender @@ -374,7 +485,7 @@ waits for an acknowledgment (ACK) packet for 1 second message will be re-sent up to 10 times (`max_retries=10`) before notifying the application of a communication error. -Since the session is sticky, all messages in a streaming communication will be +All messages in a streaming communication will be forwarded to the same application instance. Let's illustrate this with an example using the client and server applications described above. From 53dd994480cd2e0b6f4582d08388703e9b4ac435 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Thu, 12 Feb 2026 20:25:15 +0100 Subject: [PATCH 2/4] docs: slimrpc updates Signed-off-by: Mauro Sardara --- docs/slim/slim-slimrpc-compiler.md | 509 ++++++++++++++--------------- 1 file changed, 241 insertions(+), 268 deletions(-) diff --git a/docs/slim/slim-slimrpc-compiler.md b/docs/slim/slim-slimrpc-compiler.md index 8a18a06..d2b9caa 100644 --- a/docs/slim/slim-slimrpc-compiler.md +++ b/docs/slim/slim-slimrpc-compiler.md @@ -1,50 +1,150 @@ # SLIMRPC Compiler -The Slim RPC Compiler (`protoc-slimrpc-plugin`) is a protoc plugin that -generates Python client stubs and server servicers for [SLIMRPC (Slim -RPC)](./slim-rpc.md) from Protocol Buffer service definitions. This plugin -enables you to build high-performance RPC services using the SLIMRPC framework. +The Slim RPC Compiler is a collection of protoc plugins that generate client +stubs and server handlers for [SLIMRPC (Slim RPC)](./slim-rpc.md) from Protocol +Buffer service definitions. These plugins enable you to build high-performance +RPC services using the SLIMRPC framework. + +## Supported Languages + +- **Python**: `protoc-gen-slimrpc-python` +- **Go**: `protoc-gen-slimrpc-go` ## Features The Slim RPC Compiler has the following features: -- Generates Python client stubs for calling SlimRPC services -- Generates Python server servicers for implementing SlimRPC services +- Generates type-safe client stubs and server handlers for slimrpc services - Supports all gRPC streaming patterns: unary-unary, unary-stream, stream-unary, and stream-stream -- Compatible with both `protoc` and `buf` build systems +- Compatible with both `protoc` and `buf` build systems (buf recommended) - Automatic import resolution for Protocol Buffer dependencies ## Installation -There are two ways to install the Slim RPC Compiler: +You can install the Slim RPC Compiler either by downloading pre-built binaries or building from source using Cargo. -### Install via Cargo +=== "Pre-built Binaries (Python)" -```bash -cargo install agntcy-protoc-slimrpc-plugin -``` + Download the pre-built binary for your platform from the [latest release](https://github.com/agntcy/slim/releases/tag/protoc-slimrpc-plugin-v1.0.0): -This will install the `protoc-slimrpc-plugin` binary to your Cargo bin directory -(usually `~/.cargo/bin`). + === "Linux (x86_64)" -### Compile from Source + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-linux-x86_64.tar.gz + tar -xzf protoc-gen-slimrpc-python-linux-x86_64.tar.gz + chmod +x protoc-gen-slimrpc-python + sudo mv protoc-gen-slimrpc-python /usr/local/bin/ + ``` -1. Clone the repository: + === "Linux (ARM64)" - ```bash - git clone --branch slim-v0.6.0 https://github.com/agntcy/slim.git - cd slim/data-plane/slimrpc-compiler - ``` + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-linux-arm64.tar.gz + tar -xzf protoc-gen-slimrpc-python-linux-arm64.tar.gz + chmod +x protoc-gen-slimrpc-python + sudo mv protoc-gen-slimrpc-python /usr/local/bin/ + ``` + + === "macOS (ARM64)" + + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-macos-arm64.tar.gz + tar -xzf protoc-gen-slimrpc-python-macos-arm64.tar.gz + chmod +x protoc-gen-slimrpc-python + sudo mv protoc-gen-slimrpc-python /usr/local/bin/ + ``` + + === "macOS (x86_64)" + + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-macos-x86_64.tar.gz + tar -xzf protoc-gen-slimrpc-python-macos-x86_64.tar.gz + chmod +x protoc-gen-slimrpc-python + sudo mv protoc-gen-slimrpc-python /usr/local/bin/ + ``` + + === "Windows (x86_64)" + + ```powershell + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-windows-x86_64.zip" -OutFile "protoc-gen-slimrpc-python-windows-x86_64.zip" + Expand-Archive -Path protoc-gen-slimrpc-python-windows-x86_64.zip -DestinationPath . + # Add the binary to your PATH or move it to a directory in your PATH + ``` + + === "Windows (ARM64)" + + ```powershell + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-windows-arm64.zip" -OutFile "protoc-gen-slimrpc-python-windows-arm64.zip" + Expand-Archive -Path protoc-gen-slimrpc-python-windows-arm64.zip -DestinationPath . + # Add the binary to your PATH or move it to a directory in your PATH + ``` + +=== "Pre-built Binaries (Go)" + + Download the pre-built binary for your platform from the [latest release](https://github.com/agntcy/slim/releases/tag/protoc-slimrpc-plugin-v1.0.0): + + === "Linux (x86_64)" + + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-linux-x86_64.tar.gz + tar -xzf protoc-gen-slimrpc-go-linux-x86_64.tar.gz + chmod +x protoc-gen-slimrpc-go + sudo mv protoc-gen-slimrpc-go /usr/local/bin/ + ``` + + === "Linux (ARM64)" + + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-linux-arm64.tar.gz + tar -xzf protoc-gen-slimrpc-go-linux-arm64.tar.gz + chmod +x protoc-gen-slimrpc-go + sudo mv protoc-gen-slimrpc-go /usr/local/bin/ + ``` + + === "macOS (ARM64)" + + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-macos-arm64.tar.gz + tar -xzf protoc-gen-slimrpc-go-macos-arm64.tar.gz + chmod +x protoc-gen-slimrpc-go + sudo mv protoc-gen-slimrpc-go /usr/local/bin/ + ``` + + === "macOS (x86_64)" + + ```bash + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-macos-x86_64.tar.gz + tar -xzf protoc-gen-slimrpc-go-macos-x86_64.tar.gz + chmod +x protoc-gen-slimrpc-go + sudo mv protoc-gen-slimrpc-go /usr/local/bin/ + ``` + + === "Windows (x86_64)" + + ```powershell + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-windows-x86_64.zip" -OutFile "protoc-gen-slimrpc-go-windows-x86_64.zip" + Expand-Archive -Path protoc-gen-slimrpc-go-windows-x86_64.zip -DestinationPath . + # Add the binary to your PATH or move it to a directory in your PATH + ``` -1. Build the plugin: + === "Windows (ARM64)" + + ```powershell + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-windows-arm64.zip" -OutFile "protoc-gen-slimrpc-go-windows-arm64.zip" + Expand-Archive -Path protoc-gen-slimrpc-go-windows-arm64.zip -DestinationPath . + # Add the binary to your PATH or move it to a directory in your PATH + ``` + +=== "Build from Source (Cargo)" + + You can build and install the plugin from source using Cargo: ```bash - cargo build --release + cargo install agntcy-protoc-slimrpc-plugin ``` -1. The compiled binary is available at `data-plane/target/release/protoc-slimrpc-plugin`. + This will install the `protoc-slimrpc-plugin` binaries to your Cargo bin directory (usually `~/.cargo/bin`). ## Usage @@ -75,71 +175,58 @@ message ExampleResponse { } ``` -### Using with protoc - -#### Prerequisites - -Make sure you have: - -- `protoc` (Protocol Buffer compiler) installed -- The `protoc-slimrpc-plugin` binary in your PATH or specify its full path - -#### Generate Python Files - -```bash -# Generate both the protobuf Python files and SLIMRPC files -protoc \ - --python_out=. \ - --pyi_out=. \ - --plugin=protoc-gen-slimrpc=${HOME}/.cargo/bin/protoc-slimrpc-plugin \ - --slimrpc_out=. \ - example.proto -``` - -This will generate: - -- `example_pb2.py` - Standard protobuf Python bindings -- `example_pb2_slimrpc.py` - SLIMRPC client stubs and server servicers - -#### With Custom Types Import - -You can specify a custom import for the types module. This allows to import the -types from an external package. - -For instance, if you don't want to generate the types and you want to import -them from a2a.grpc.a2a_pb2`, you can do: - -```bash -protoc \ - --plugin=protoc-gen-slimrpc=${HOME}/.cargo/bin/protoc-slimrpc-plugin \ - --slimrpc_out=types_import="from a2a.grpc import a2a_pb2 as a2a__pb2":. \ - example.proto -``` +If using golang, you might need to specify your go package as well +as shown in [the simple example.](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/go/examples/slimrpc/simple/example.proto) -### Using with buf +### Using with buf (Recommended) #### Prerequisites - `buf` CLI [installed](https://buf.build/docs/cli/installation/) -- `protoc-slimrpc-plugin` binary in your PATH, or specify the full path in the - `buf.gen.yaml` file +- The appropriate `protoc-gen-slimrpc-python` or `protoc-gen-slimrpc-go` binary in your PATH, or specify the full path in the `buf.gen.yaml` file #### Create buf.gen.yaml Create a `buf.gen.yaml` file in your project root: -```yaml -version: v2 -managed: - enabled: true -inputs: - - proto_file: example.proto -plugins: - - local: /path/to/protoc-slimrpc-plugin - out: . - - remote: buf.build/protocolbuffers/python - out: . -``` +=== "Python" + + ```yaml + version: v2 + managed: + enabled: true + inputs: + - proto_file: example.proto + plugins: + # Generate slimrpc stubs + - local: protoc-gen-slimrpc-python + out: types + # Generate standard protobuf code + - remote: buf.build/protocolbuffers/python:v29.3 + out: types + # Generate type stubs + - remote: buf.build/protocolbuffers/pyi:v31.1 + out: types + ``` + +=== "Go" + + ```yaml + version: v2 + managed: + enabled: true + plugins: + # Generate standard .pb.go files + - remote: buf.build/protocolbuffers/go + out: types + opt: + - paths=source_relative + # Generate slimrpc stubs + - local: protoc-gen-slimrpc-go + out: types + opt: + - paths=source_relative + ``` #### Generate Code @@ -147,9 +234,14 @@ plugins: buf generate ``` -#### Advanced buf Configuration +This will generate: + +- **Python**: `*_pb2.py` (protobuf types), `*_pb2.pyi` (type stubs), and `*_pb2_slimrpc.py` (slimrpc stubs) +- **Go**: `*.pb.go` (protobuf types) and `*_slimrpc.pb.go` (slimrpc stubs) + +#### Advanced buf Configuration (Python) -As before, you can customize the types import. For example, to use existing +You can customize the types import for Python. For example, to use existing types from `a2a.grpc.a2a_pb2`, you can modify the `buf.gen.yaml` as follows: ```yaml @@ -157,7 +249,7 @@ version: v2 managed: enabled: true plugins: - - local: protoc-slimrpc-plugin + - local: protoc-gen-slimrpc-python out: generated opt: - types_import=from a2a.grpc import a2a_pb2 as a2a__pb2 @@ -165,205 +257,93 @@ plugins: out: generated ``` -## Generated Code Structure +### Using with protoc -For the example above, the generated `example_pb2_slimrpc.py` will contain: +#### Prerequisites -### Client Stub +Make sure you have: -```python -class TestStub: - """Client stub for Test.""" - def __init__(self, channel): - """Constructor. +- `protoc` (Protocol Buffer compiler) installed +- The appropriate `protoc-gen-slimrpc-python` or `protoc-gen-slimrpc-go` binary in your PATH or specify its full path - Args: - channel: A slimrpc.Channel. - """ - self.ExampleUnaryUnary = channel.unary_unary(...) - self.ExampleUnaryStream = channel.unary_stream(...) - # ... other methods -``` +#### Generate Files -### Server Servicer +=== "Python" -```python -class TestServicer(): - """Server servicer for Test. Implement this class to provide your service logic.""" + ```bash + # Generate both the protobuf Python files and SLIMRPC files + protoc \ + --python_out=. \ + --pyi_out=. \ + --plugin=protoc-gen-slimrpc-python=/usr/local/bin/protoc-gen-slimrpc-python \ + --slimrpc-python_out=. \ + example.proto + ``` - def ExampleUnaryUnary(self, request, context): - """Method for ExampleUnaryUnary. Implement your service logic here.""" - raise slimrpc_rpc.SLIMRPCResponseError( - code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" - ) - # ... other methods -``` + This will generate: -### Registration Function + - `example_pb2.py` - Standard protobuf Python bindings + - `example_pb2_slimrpc.py` - SLIMRPC client stubs and server servicers -```python -def add_TestServicer_to_server(servicer, server: slimrpc.Server): - # Registers the servicer with the SLIMRPC server - pass -``` +=== "Go" -## Plugin Parameters + ```bash + # Generate both the protobuf Go files and SLIMRPC files + protoc \ + --go_out=. \ + --plugin=protoc-gen-slimrpc-go=/usr/local/bin/protoc-gen-slimrpc-go \ + --slimrpc-go_out=. \ + example.proto + ``` -The plugin supports the following parameters: + This will generate: -- `types_import`: Customize how protobuf types are imported - - Example: `types_import="from my_package import types_pb2 as pb2"` - - Default: Uses local import based on the proto file name + - `example.pb.go` - Standard protobuf Go bindings + - `example_slimrpc.pb.go` - SLIMRPC client stubs and server servicers -## Example Usage in Python +#### With Custom Types Import (Python) -### Client Usage +You can specify a custom import for the types module. This allows to import the +types from an external package. + +For instance, if you don't want to generate the types and you want to import +them from `a2a.grpc.a2a_pb2`, you can do: + +```bash +protoc \ + --plugin=protoc-gen-slimrpc-python=/usr/local/bin/protoc-gen-slimrpc-python \ + --slimrpc-python_out=types_import="from a2a.grpc import a2a_pb2 as a2a__pb2":. \ + example.proto +``` -```python -import asyncio -import logging -from collections.abc import AsyncGenerator +## Examples -import slimrpc -from slimrpc.examples.simple.types.example_pb2 import ExampleRequest -from slimrpc.examples.simple.types.example_pb2_slimrpc import TestStub +Complete working examples are available in the repository: -logger = logging.getLogger(__name__) +- **Python**: [bindings/python/examples/slimrpc/simple](https://github.com/agntcy/slim/tree/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple) +- **Go**: [bindings/go/examples/slimrpc/simple](https://github.com/agntcy/slim/tree/slim-bindings-v1.1.0/data-plane/bindings/go/examples/slimrpc/simple) +Both examples demonstrate all four RPC patterns with comprehensive client and server implementations. -async def amain() -> None: - channel = slimrpc.Channel( - local="agntcy/grpc/client", - slim={ - "endpoint": "http://localhost:46357", - "tls": { - "insecure": True, - }, - }, - enable_opentelemetry=False, - shared_secret="my_shared_secret", - remote="agntcy/grpc/server", - ) +## Generated Code Structure - # Stubs - stubs = TestStub(channel) +For detailed information about the generated code structure, including client stubs, server handlers, and registration functions for both Python and Go, please refer to the [SLIMRPC documentation](./slim-rpc.md#generated-code). - # Call method - try: - request = ExampleRequest(example_integer=1, example_string="hello") - response = await stubs.ExampleUnaryUnary(request, timeout=2) +The compiler generates type-safe client stubs and server handlers that support all gRPC streaming patterns (unary-unary, unary-stream, stream-unary, and stream-stream) with language-specific idioms and async/await patterns. - logger.info(f"Response: {response}") +## Plugin Parameters - responses = stubs.ExampleUnaryStream(request, timeout=2) - async for resp in responses: - logger.info(f"Stream Response: {resp}") +### Python Plugin - async def stream_requests() -> AsyncGenerator[ExampleRequest]: - for i in range(10): - yield ExampleRequest(example_integer=i, example_string=f"Request {i}") +- `types_import`: Customize how protobuf types are imported + - Example: `types_import="from my_package import types_pb2 as pb2"` + - Default: Uses local import based on the proto file name - response = await stubs.ExampleStreamUnary(stream_requests(), timeout=2) - logger.info(f"Stream Unary Response: {response}") - except asyncio.TimeoutError: - logger.error("timeout while waiting for response") -``` +### Go Plugin -### Server Usage - -```python -import asyncio -import logging -from collections.abc import AsyncIterable - -from slimrpc.context import Context -from slimrpc.examples.simple.types.example_pb2 import ExampleRequest, ExampleResponse -from slimrpc.examples.simple.types.example_pb2_slimrpc import ( - TestServicer, - add_TestServicer_to_server, -) -from slimrpc.server import Server - -logger = logging.getLogger(__name__) - - -class TestService(TestServicer): - async def ExampleUnaryUnary( - self, request: ExampleRequest, context: Context - ) -> ExampleResponse: - logger.info(f"Received unary-unary request: {request}") - - return ExampleResponse(example_integer=1, example_string="Hello, World!") - - async def ExampleUnaryStream( - self, request: ExampleRequest, context: Context - ) -> AsyncIterable[ExampleResponse]: - logger.info(f"Received unary-stream request: {request}") - - # generate async responses stream - for i in range(5): - logger.info(f"Sending response {i}") - yield ExampleResponse(example_integer=i, example_string=f"Response {i}") - - async def ExampleStreamUnary( - self, request_iterator: AsyncIterable[ExampleRequest], context: Context - ) -> ExampleResponse: - logger.info(f"Received stream-unary request: {request_iterator}") - - async for request in request_iterator: - logger.info(f"Received stream-unary request: {request}") - response = ExampleResponse( - example_integer=1, example_string="Stream Unary Response" - ) - return response - - async def ExampleStreamStream( - self, request_iterator: AsyncIterable[ExampleRequest], context: Context - ) -> AsyncIterable[ExampleResponse]: - """Missing associated documentation comment in .proto file.""" - raise NotImplementedError("Method not implemented!") - - -def create_server( - local: str, - slim: dict, - enable_opentelemetry: bool = False, - shared_secret: str = "", -) -> Server: - """ - Create a new SLIMRPC server instance. - """ - server = Server( - local=local, - slim=slim, - enable_opentelemetry=enable_opentelemetry, - shared_secret=shared_secret, - ) - - return server - - -async def amain() -> None: - server = create_server( - local="agntcy/grpc/server", - slim={ - "endpoint": "http://localhost:46357", - "tls": { - "insecure": True, - }, - }, - enable_opentelemetry=False, - shared_secret="my_shared_secret", - ) - - # Create RPCs - add_TestServicer_to_server( - TestService(), - server, - ) - - await server.run() -``` +- `paths`: Control output path strategy + - `source_relative`: Generate files relative to the proto file location + - Default: Uses Go package paths ## Troubleshooting @@ -371,22 +351,15 @@ async def amain() -> None: If you get an error that the plugin is not found: -- Ensure `protoc-slimrpc-plugin` is in your PATH +- Ensure `protoc-gen-slimrpc-python` or `protoc-gen-slimrpc-go` is in your PATH - Or specify the full path: - `--plugin=protoc-gen-slimrpc=/full/path/to/protoc-slimrpc-plugin` + - For Python: `--plugin=protoc-gen-slimrpc=/full/path/to/protoc-gen-slimrpc-python` + - For Go: `--plugin=protoc-gen-slimrpc=/full/path/to/protoc-gen-slimrpc-go` ### Import Errors -If you encounter Python import errors: - -- Make sure the generated `*_pb2.py` files are in your Python path. -- Use the `types_import` parameter to customize import paths. -- Ensure all Protocol Buffer dependencies are generated. - -### Build Errors - -If the plugin fails to build: +If you encounter import errors: -- Ensure you have Rust and Cargo installed. -- Check that all dependencies are available. -- Try cleaning and rebuilding: `cargo clean && cargo build --release`. +- **Python**: Make sure the generated `*_pb2.py` files are in your Python path. Use the `types_import` parameter to customize import paths. +- **Go**: Ensure `go.mod` is properly configured with correct module paths. +- Verify all Protocol Buffer dependencies are generated. From 59e59e96ba1c1a4e47e49ded94281e10adf53bcc Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Fri, 13 Feb 2026 15:50:02 +0100 Subject: [PATCH 3/4] feat: upgrade to slim-bindings 1.1.1 Signed-off-by: Mauro Sardara --- docs/slim/slim-rpc.md | 69 ++++++++++++++++++++++-------- docs/slim/slim-slimrpc-compiler.md | 34 +++++++-------- 2 files changed, 69 insertions(+), 34 deletions(-) diff --git a/docs/slim/slim-rpc.md b/docs/slim/slim-rpc.md index 095edd2..87114c7 100644 --- a/docs/slim/slim-rpc.md +++ b/docs/slim/slim-rpc.md @@ -24,7 +24,7 @@ documentation](./slim-slimrpc-compiler.md). In SLIMRPC, each service and its individual RPC handlers are assigned a SLIM name, facilitating efficient message routing and processing. Consider the [example -protobuf](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple/example.proto) +protobuf](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/python/examples/slimrpc/simple/example.proto) definition, which defines four distinct services: ```proto @@ -95,7 +95,7 @@ standard gRPC. This section provides a detailed walkthrough of a basic SLIMRPC client-server interaction in python, leveraging the simple example provided in -[example](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple) +[example](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/python/examples/slimrpc/simple) folder. ### Generated Code @@ -104,9 +104,9 @@ The foundation of this example is the `example.proto` file, which is a standard Protocol Buffers definition file. This file is compiled using the [SLIMRPC compiler](./slim-slimrpc-compiler.md) to generate the necessary Python stub code. The generated code is available in two files: -[example_pb2.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/types/example_pb2.py) +[example_pb2.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/python/examples/slimrpc/simple/types/example_pb2.py) and -[example_pb2_slimrpc.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/types/example_pb2_slimrpc.py). +[example_pb2_slimrpc.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/python/examples/slimrpc/simple/types/example_pb2_slimrpc.py). Specifically, `example_pb2_slimrpc.py` contains the SLIMRPC-specific stubs for both client and server implementations. Below are the key classes and functions generated by the compiler: @@ -214,33 +214,68 @@ class TestServicer: def ExampleUnaryUnary(self, request, context): """Method for ExampleUnaryUnary. Implement your service logic here.""" - raise slim_bindings.SRPCResponseError( - code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" + raise slim_bindings.RpcError.Rpc( + code=slim_bindings.RpcCode.UNIMPLEMENTED, + message="Method not implemented!", + details=None ) def ExampleUnaryStream(self, request, context): """Method for ExampleUnaryStream. Implement your service logic here.""" - raise slim_bindings.SRPCResponseError( - code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" + raise slim_bindings.RpcError.Rpc( + code=slim_bindings.RpcCode.UNIMPLEMENTED, + message="Method not implemented!", + details=None ) def ExampleStreamUnary(self, request_iterator, context): """Method for ExampleStreamUnary. Implement your service logic here.""" - raise slim_bindings.SRPCResponseError( - code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" + raise slim_bindings.RpcError.Rpc( + code=slim_bindings.RpcCode.UNIMPLEMENTED, + message="Method not implemented!", + details=None ) def ExampleStreamStream(self, request_iterator, context): """Method for ExampleStreamStream. Implement your service logic here.""" - raise slim_bindings.SRPCResponseError( - code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" + raise slim_bindings.RpcError.Rpc( + code=slim_bindings.RpcCode.UNIMPLEMENTED, + message="Method not implemented!", + details=None ) ``` +_Handler Classes_: The generated code includes internal handler classes that wrap +each servicer method. These handlers manage request deserialization, response +serialization, and error handling. Below is an example of the unary-unary handler: + +```python +class _TestServicer_ExampleUnaryUnary_Handler: + def __init__(self, servicer): + self.servicer = servicer + + async def handle(self, request: bytes, context: slim_bindings.Context) -> bytes: + try: + request_msg = pb2.ExampleRequest.FromString(request) + response = await self.servicer.ExampleUnaryUnary(request_msg, context) + return pb2.ExampleResponse.SerializeToString(response) + except slim_bindings.RpcError: + raise + except Exception as e: + raise slim_bindings.RpcError.Rpc( + code=slim_bindings.RpcCode.INTERNAL, + message=str(e), + details=None + ) +``` + +Similar handler classes are generated for unary-stream, stream-unary, and +stream-stream patterns, each implementing the appropriate async handling logic +for their respective communication patterns. + _Server Registration Function (add_TestServicer_to_server)_: This utility function registers an implemented TestServicer instance with an SLIMRPC server. -It maps RPC method names to their corresponding handlers and specifies the -request deserialization and response serialization routines. +It maps RPC method names to their corresponding handler instances. ```python def add_TestServicer_to_server(servicer, server: slim_bindings.Server): @@ -269,7 +304,7 @@ def add_TestServicer_to_server(servicer, server: slim_bindings.Server): ### Server implementation The server-side logic is defined in -[server.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple/server.py). +[server.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/python/examples/slimrpc/simple/server.py). Similar to standard gRPC implementations, the core service functionality is provided by the TestService class, which inherits from TestServicer (as introduced in the previous section). This class contains the concrete @@ -359,7 +394,7 @@ which runs indefinitely until interrupted. ### Client implementation The client-side implementation, found in -[client.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple/client.py), +[client.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/python/examples/slimrpc/simple/client.py), largely mirrors the structure of a standard gRPC client. The client initialization follows the same pattern as the server: @@ -460,7 +495,7 @@ details, as these aspects are handled automatically by SLIMRPC and SLIM. All RPC services underneath utilize a point-to-point session. The SLIM session creation is implemented in inside SLIMRPC in -[channel.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/rust/src/slimrpc/channel.rs#L657-L670): +[channel.py](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/rust/src/slimrpc/channel.rs#L657-L670): ```rust let slim_config = slim_session::session_config::SessionConfig { diff --git a/docs/slim/slim-slimrpc-compiler.md b/docs/slim/slim-slimrpc-compiler.md index d2b9caa..41cdbc1 100644 --- a/docs/slim/slim-slimrpc-compiler.md +++ b/docs/slim/slim-slimrpc-compiler.md @@ -26,12 +26,12 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "Pre-built Binaries (Python)" - Download the pre-built binary for your platform from the [latest release](https://github.com/agntcy/slim/releases/tag/protoc-slimrpc-plugin-v1.0.0): + Download the pre-built binary for your platform from the [latest release](https://github.com/agntcy/slim/releases/tag/protoc-slimrpc-plugin-v1.0.2): === "Linux (x86_64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-linux-x86_64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-python-linux-x86_64.tar.gz tar -xzf protoc-gen-slimrpc-python-linux-x86_64.tar.gz chmod +x protoc-gen-slimrpc-python sudo mv protoc-gen-slimrpc-python /usr/local/bin/ @@ -40,7 +40,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "Linux (ARM64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-linux-arm64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-python-linux-arm64.tar.gz tar -xzf protoc-gen-slimrpc-python-linux-arm64.tar.gz chmod +x protoc-gen-slimrpc-python sudo mv protoc-gen-slimrpc-python /usr/local/bin/ @@ -49,7 +49,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "macOS (ARM64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-macos-arm64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-python-macos-arm64.tar.gz tar -xzf protoc-gen-slimrpc-python-macos-arm64.tar.gz chmod +x protoc-gen-slimrpc-python sudo mv protoc-gen-slimrpc-python /usr/local/bin/ @@ -58,7 +58,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "macOS (x86_64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-macos-x86_64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-python-macos-x86_64.tar.gz tar -xzf protoc-gen-slimrpc-python-macos-x86_64.tar.gz chmod +x protoc-gen-slimrpc-python sudo mv protoc-gen-slimrpc-python /usr/local/bin/ @@ -67,7 +67,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "Windows (x86_64)" ```powershell - Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-windows-x86_64.zip" -OutFile "protoc-gen-slimrpc-python-windows-x86_64.zip" + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-python-windows-x86_64.zip" -OutFile "protoc-gen-slimrpc-python-windows-x86_64.zip" Expand-Archive -Path protoc-gen-slimrpc-python-windows-x86_64.zip -DestinationPath . # Add the binary to your PATH or move it to a directory in your PATH ``` @@ -75,19 +75,19 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "Windows (ARM64)" ```powershell - Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-python-windows-arm64.zip" -OutFile "protoc-gen-slimrpc-python-windows-arm64.zip" + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-python-windows-arm64.zip" -OutFile "protoc-gen-slimrpc-python-windows-arm64.zip" Expand-Archive -Path protoc-gen-slimrpc-python-windows-arm64.zip -DestinationPath . # Add the binary to your PATH or move it to a directory in your PATH ``` === "Pre-built Binaries (Go)" - Download the pre-built binary for your platform from the [latest release](https://github.com/agntcy/slim/releases/tag/protoc-slimrpc-plugin-v1.0.0): + Download the pre-built binary for your platform from the [latest release](https://github.com/agntcy/slim/releases/tag/protoc-slimrpc-plugin-v1.0.2): === "Linux (x86_64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-linux-x86_64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-go-linux-x86_64.tar.gz tar -xzf protoc-gen-slimrpc-go-linux-x86_64.tar.gz chmod +x protoc-gen-slimrpc-go sudo mv protoc-gen-slimrpc-go /usr/local/bin/ @@ -96,7 +96,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "Linux (ARM64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-linux-arm64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-go-linux-arm64.tar.gz tar -xzf protoc-gen-slimrpc-go-linux-arm64.tar.gz chmod +x protoc-gen-slimrpc-go sudo mv protoc-gen-slimrpc-go /usr/local/bin/ @@ -105,7 +105,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "macOS (ARM64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-macos-arm64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-go-macos-arm64.tar.gz tar -xzf protoc-gen-slimrpc-go-macos-arm64.tar.gz chmod +x protoc-gen-slimrpc-go sudo mv protoc-gen-slimrpc-go /usr/local/bin/ @@ -114,7 +114,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "macOS (x86_64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-macos-x86_64.tar.gz + curl -LO https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-go-macos-x86_64.tar.gz tar -xzf protoc-gen-slimrpc-go-macos-x86_64.tar.gz chmod +x protoc-gen-slimrpc-go sudo mv protoc-gen-slimrpc-go /usr/local/bin/ @@ -123,7 +123,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "Windows (x86_64)" ```powershell - Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-windows-x86_64.zip" -OutFile "protoc-gen-slimrpc-go-windows-x86_64.zip" + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-go-windows-x86_64.zip" -OutFile "protoc-gen-slimrpc-go-windows-x86_64.zip" Expand-Archive -Path protoc-gen-slimrpc-go-windows-x86_64.zip -DestinationPath . # Add the binary to your PATH or move it to a directory in your PATH ``` @@ -131,7 +131,7 @@ You can install the Slim RPC Compiler either by downloading pre-built binaries o === "Windows (ARM64)" ```powershell - Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.0/protoc-gen-slimrpc-go-windows-arm64.zip" -OutFile "protoc-gen-slimrpc-go-windows-arm64.zip" + Invoke-WebRequest -Uri "https://github.com/agntcy/slim/releases/download/protoc-slimrpc-plugin-v1.0.2/protoc-gen-slimrpc-go-windows-arm64.zip" -OutFile "protoc-gen-slimrpc-go-windows-arm64.zip" Expand-Archive -Path protoc-gen-slimrpc-go-windows-arm64.zip -DestinationPath . # Add the binary to your PATH or move it to a directory in your PATH ``` @@ -176,7 +176,7 @@ message ExampleResponse { ``` If using golang, you might need to specify your go package as well -as shown in [the simple example.](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.0/data-plane/bindings/go/examples/slimrpc/simple/example.proto) +as shown in [the simple example.](https://github.com/agntcy/slim/blob/slim-bindings-v1.1.1/data-plane/bindings/go/examples/slimrpc/simple/example.proto) ### Using with buf (Recommended) @@ -320,8 +320,8 @@ protoc \ Complete working examples are available in the repository: -- **Python**: [bindings/python/examples/slimrpc/simple](https://github.com/agntcy/slim/tree/slim-bindings-v1.1.0/data-plane/bindings/python/examples/slimrpc/simple) -- **Go**: [bindings/go/examples/slimrpc/simple](https://github.com/agntcy/slim/tree/slim-bindings-v1.1.0/data-plane/bindings/go/examples/slimrpc/simple) +- **Python**: [bindings/python/examples/slimrpc/simple](https://github.com/agntcy/slim/tree/slim-bindings-v1.1.1/data-plane/bindings/python/examples/slimrpc/simple) +- **Go**: [bindings/go/examples/slimrpc/simple](https://github.com/agntcy/slim/tree/slim-bindings-v1.1.1/data-plane/bindings/go/examples/slimrpc/simple) Both examples demonstrate all four RPC patterns with comprehensive client and server implementations. From 40b07cc06297a83eacb9e90891c72693871ba0a8 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Fri, 13 Feb 2026 18:10:10 +0100 Subject: [PATCH 4/4] feat: add a2a Signed-off-by: Mauro Sardara --- docs/slim/slim-a2a.md | 321 +++++++++++++++++++++++++++++++++--------- 1 file changed, 253 insertions(+), 68 deletions(-) diff --git a/docs/slim/slim-a2a.md b/docs/slim/slim-a2a.md index 4c452ce..7cc07ea 100644 --- a/docs/slim/slim-a2a.md +++ b/docs/slim/slim-a2a.md @@ -8,102 +8,287 @@ SLIMRPC is a framework that enables Protocol Buffers (protobuf) Remote Procedure To compile a protobuf file and generate the clients and service stub you can use the [SLIMRPC compiler](./slim-slimrpc-compiler.md). This works in a similar way to the protoc compiler. -For SLIM A2A we compiled the [a2a.proto](https://github.com/a2aproject/A2A/blob/v0.3.0/specification/grpc/a2a.proto) file using the SLIM RPC compiler. The generated code is in [a2a_pb2_slimrpc.py](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/integrations/slima2a/examples/travel_planner_agent). +For SLIM A2A we compiled the [a2a.proto](https://github.com/a2aproject/A2A/blob/v0.3.0/specification/grpc/a2a.proto) file using the SLIMRPC compiler. The generated code is in [a2a_pb2_slimrpc.py](https://github.com/agntcy/slim-a2a-python/blob/main/slima2a/types/a2a_pb2_slimrpc.py). ## How to use SLIMA2A -Using SLIMA2A is very similar to using the standard A2A implementation. As a reference example here we use the [travel planner agent](https://github.com/a2aproject/a2a-samples/tree/main/samples/python/agents/travel_planner_agent) available on the A2A samples repository. The version adapted to use SLIM A2A can be found in [travel_planner_agent](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/integrations/slima2a/examples/travel_planner_agent) folder. In the following section, we highlight and explain the key difference between the standard and SLIM A2A implementations. +Using SLIMA2A is very similar to using the standard A2A implementation. As a reference example here we use the [echo agent](https://github.com/agntcy/slim-a2a-python/tree/main/examples/echo_agent) available in the SLIM A2A Python repository. In the following sections, we highlight and explain the key differences between the standard and SLIM A2A implementations. -### Travel Planner: Server +### Server Implementation -In this section we highlight the main differences between the SLIM A2A [server](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slima2a/examples/travel_planner_agent/server.py) implementation with respect to the original implementation in the A2A repository. +SLIMA2A provides two approaches for setting up an A2A server: a quick start method using helper functions, and an advanced method for more control. + +#### Quick Start (Recommended) + +The quick start approach uses the `setup_slim_client` helper function to simplify SLIM initialization: + +```python +import asyncio +import slim_bindings +from a2a.server.request_handlers import DefaultRequestHandler +from a2a.server.tasks import InMemoryTaskStore +from slima2a import setup_slim_client +from slima2a.handler import SRPCHandler +from slima2a.types.a2a_pb2_slimrpc import add_A2AServiceServicer_to_server + +# Initialize and connect to SLIM (simplified helper) +service, local_app, local_name, conn_id = await setup_slim_client( + namespace="agntcy", + group="demo", + name="echo_agent", +) + +# Create request handler +agent_executor = MyAgentExecutor() +task_store = InMemoryTaskStore() +request_handler = DefaultRequestHandler( + agent_executor=agent_executor, + task_store=task_store, +) + +# Create servicer +servicer = SRPCHandler(agent_card, request_handler) + +# Create server +server = slim_bindings.Server.new_with_connection(local_app, local_name, conn_id) + +add_A2AServiceServicer_to_server(servicer, server) + +# Run server +await server.serve_async() +``` + +#### Advanced Setup (Manual Configuration) + +??? note "Advanced Setup - Click to expand" + + For more control over the SLIM configuration, you can manually initialize all components: -1. Import the SLIMRPC package. - ```python - import slimrpc - ``` -2. Create the SLIMRPCHandler. Notice that the definitions for `AgentCard` and `DefaultRequestHandler` remain unchanged from the original A2A example. ```python - agent_card = AgentCard( - name="travel planner Agent", - description="travel planner", - url="http://localhost:10001/", - version="1.0.0", - default_input_modes=["text"], - default_output_modes=["text"], - capabilities=AgentCapabilities(streaming=True), - skills=[skill], + import asyncio + import slim_bindings + from a2a.server.request_handlers import DefaultRequestHandler + from a2a.server.tasks import InMemoryTaskStore + from slima2a.handler import SRPCHandler + from slima2a.types.a2a_pb2_slimrpc import add_A2AServiceServicer_to_server + + # Set the event loop for slim_bindings + slim_bindings.slim_bindings.uniffi_set_event_loop(asyncio.get_running_loop()) + + # Initialize slim_bindings service + tracing_config = slim_bindings.new_tracing_config() + runtime_config = slim_bindings.new_runtime_config() + service_config = slim_bindings.new_service_config() + tracing_config.log_level = "info" + + slim_bindings.initialize_with_configs( + tracing_config=tracing_config, + runtime_config=runtime_config, + service_config=[service_config], ) - request_handler = DefaultRequestHandler( - agent_executor=TravelPlannerAgentExecutor(), - task_store=InMemoryTaskStore(), + + service = slim_bindings.get_global_service() + + # Create local name + local_name = slim_bindings.Name("agntcy", "demo", "echo_agent") + + # Connect to SLIM + client_config = slim_bindings.new_insecure_client_config("http://localhost:46357") + conn_id = await service.connect_async(client_config) + + # Create app with shared secret + local_app = service.create_app_with_secret( + local_name, "secretsecretsecretsecretsecretsecret" ) - servicer = SLIMRPCHandler(agent_card, request_handler) - ``` -3. Setup the `slimrpc.Server`. This is the only place where you need to setup few parameters that are specific to SLIM. - ```python - server = slimrpc.Server( - local="agntcy/demo/travel_planner_agent", - slim={ - "endpoint": "http://localhost:46357", - "tls": { - "insecure": True, - }, - }, - shared_secret="secret", + # Subscribe to local name + await local_app.subscribe_async(local_name, conn_id) + + # Create request handler + agent_executor = MyAgentExecutor() + task_store = InMemoryTaskStore() + request_handler = DefaultRequestHandler( + agent_executor=agent_executor, + task_store=task_store, ) + + # Create servicer + servicer = SRPCHandler(agent_card, request_handler) + + # Create server + server = slim_bindings.Server.new_with_connection(local_app, local_name, conn_id) + + add_A2AServiceServicer_to_server(servicer, server) + + # Run server + await server.serve_async() ``` - Where: + Key differences from the standard A2A implementation: + + 1. **SLIM Bindings Initialization**: Set up the event loop and initialize `slim_bindings` with tracing and runtime configurations. + 2. **SLIM Name Creation**: Create a `slim_bindings.Name` object with namespace, group, and name components. + 3. **Connection Setup**: Connect to the SLIM server and create an app with a shared secret for MLS (Message Layer Security). + 4. **Server Creation**: Use `slim_bindings.Server.new_with_connection()` instead of a standard HTTP server. + 5. **Service Registration**: Register the servicer using `add_A2AServiceServicer_to_server()` from the generated SLIMRPC code. + +### Client Implementation + +Similar to the server, SLIMA2A provides both quick start and advanced client setup options. + +#### Quick Start (Recommended) - - `local`: Name of the local application. - - `slim`: Dictionary specifying how to connect to the SLIM node. - - `shared_secret`: Used to set up MLS (Message Layer Security). +```python +import asyncio +import httpx +from a2a.client import ClientFactory, minimal_agent_card +from a2a.types import Message, Part, Role, TextPart +from slima2a import setup_slim_client +from slima2a.client_transport import ClientConfig, SRPCTransport, slimrpc_channel_factory - For more information about these settings, see the [SLIMRPC](./slim-rpc.md). +# Initialize and connect to SLIM (simplified helper) +service, slim_local_app, local_name, conn_id = await setup_slim_client( + namespace="agntcy", + group="demo", + name="client", +) -4. Register the Service. +# Create client config +httpx_client = httpx.AsyncClient() +client_config = ClientConfig( + supported_transports=["JSONRPC", "slimrpc"], + streaming=True, + httpx_client=httpx_client, + slimrpc_channel_factory=slimrpc_channel_factory(slim_local_app, conn_id), +) + +# Create client factory and register transport +client_factory = ClientFactory(client_config) +client_factory.register("slimrpc", SRPCTransport.create) + +# Create client with minimal agent card +agent_card = minimal_agent_card("agntcy/demo/echo_agent", ["slimrpc"]) +client = client_factory.create(card=agent_card) + +# Send message +request = Message( + role=Role.user, + message_id="request-id", + parts=[Part(root=TextPart(text="Hello, world!"))], +) + +async for event in client.send_message(request=request): + if isinstance(event, Message): + for part in event.parts: + if isinstance(part.root, TextPart): + print(part.root.text) +``` + +#### Advanced Setup (Manual Configuration) + +??? note "Advanced Setup - Click to expand" ```python - add_A2AServiceServicer_to_server( - servicer, - server, + import asyncio + import httpx + import slim_bindings + from a2a.client import ClientFactory, minimal_agent_card + from a2a.types import Message, Part, Role, TextPart + from slima2a.client_transport import ClientConfig, SRPCTransport, slimrpc_channel_factory + + # Set the event loop for slim_bindings + slim_bindings.slim_bindings.uniffi_set_event_loop(asyncio.get_running_loop()) + + # Initialize slim_bindings service + tracing_config = slim_bindings.new_tracing_config() + runtime_config = slim_bindings.new_runtime_config() + service_config = slim_bindings.new_service_config() + tracing_config.log_level = "info" + + slim_bindings.initialize_with_configs( + tracing_config=tracing_config, + runtime_config=runtime_config, + service_config=[service_config], ) - ``` -Your A2A server is now ready to run on SLIM. + service = slim_bindings.get_global_service() -### Travel Planner: Client + # Create local name + local_name = slim_bindings.Name("agntcy", "demo", "client") -These are the main differences between the [client](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/integrations/slima2a/examples/travel_planner_agent/client.py) using SLIM A2A and the standard one. + # Connect to SLIM + client_config_slim = slim_bindings.new_insecure_client_config("http://localhost:46357") + conn_id = await service.connect_async(client_config_slim) -1. Create a channel. This requires a configuration that is similar to the server - ```python - def channel_factory(topic: str) -> slimrpc.Channel: - channel = slimrpc.Channel( - local="agntcy/demo/client", - remote=topic, - slim={ - "endpoint": "http://localhost:46357", - "tls": { - "insecure": True, - }, - }, - shared_secret="secret", - ) - return channel - ``` -2. Add SLIM RPC in the supported transports. - ```python + # Create app with shared secret + slim_local_app = service.create_app_with_secret( + local_name, "secretsecretsecretsecretsecretsecret" + ) + + # Subscribe to local name + await slim_local_app.subscribe_async(local_name, conn_id) + + # Create client config + httpx_client = httpx.AsyncClient() client_config = ClientConfig( supported_transports=["JSONRPC", "slimrpc"], streaming=True, httpx_client=httpx_client, - slimrpc_channel_factory=channel_factory, + slimrpc_channel_factory=slimrpc_channel_factory(slim_local_app, conn_id), ) + + # Create client factory and register transport client_factory = ClientFactory(client_config) - client_factory.register("slimrpc", SLIMRPCTransport.create) - agent_card = minimal_agent_card("agntcy/demo/travel_planner_agent", ["slimrpc"]) + client_factory.register("slimrpc", SRPCTransport.create) + + # Create client with minimal agent card + agent_card = minimal_agent_card("agntcy/demo/echo_agent", ["slimrpc"]) client = client_factory.create(card=agent_card) + + # Send message + request = Message( + role=Role.user, + message_id="request-id", + parts=[Part(root=TextPart(text="Hello, world!"))], + ) + + async for event in client.send_message(request=request): + if isinstance(event, Message): + for part in event.parts: + if isinstance(part.root, TextPart): + print(part.root.text) ``` + + Key differences from the standard A2A client: + + 1. **Channel Factory**: Use `slimrpc_channel_factory` instead of manually creating channels, which handles the SLIM connection details. + 2. **Transport Registration**: Register the `SRPCTransport` as the "slimrpc" transport in the client factory. + 3. **Agent Card Configuration**: Specify "slimrpc" as the transport protocol in the agent card. + 4. **Supported Transports**: Add "slimrpc" to the list of supported transports alongside "JSONRPC". + +### Helper Functions + +The `slima2a` package provides convenient helper functions to simplify SLIM setup: + +- **`setup_slim_client(namespace, group, name, slim_url="http://localhost:46357", secret="...", log_level="info")`** - Complete SLIM client setup in one call. Returns `(service, local_app, local_name, conn_id)`. +- **`initialize_slim_service(log_level="info")`** - Initialize SLIM service with default configuration. +- **`connect_and_subscribe(service, local_name, slim_url="http://localhost:46357", secret="...")`** - Connect to SLIM server and subscribe to a local name. +- **`slimrpc_channel_factory(local_app, conn_id)`** - Creates a channel factory function for use with A2A client configuration. + +## Configuration Parameters + +### SLIM Connection + +- **`namespace`**: The namespace component of the SLIM name (e.g., "agntcy") +- **`group`**: The group component of the SLIM name (e.g., "demo") +- **`name`**: The name component of the SLIM name (e.g., "echo_agent") +- **`slim_url`**: The endpoint URL for the SLIM server (default: "http://localhost:46357") +- **`shared_secret`**: A secret string used for MLS (Message Layer Security). Must be at least 32 characters. + +### Logging + +Set the log level through `tracing_config.log_level` with values like "info", "debug", "warn", or "error". + +## Running the Example + +See the [echo agent example](https://github.com/agntcy/slim-a2a-python/tree/main/examples/echo_agent) in the SLIM A2A Python repository for a complete working implementation.