From 489814248460cf7749e5ab63748fcc0a26603749 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Wed, 17 Sep 2025 12:27:22 +0200 Subject: [PATCH 01/16] doc(SLIM): slimrpc, slima2a and slimrpc-compiler Signed-off-by: Mauro Sardara --- docs/messaging/slim-a2a.md | 190 +++++++++++ docs/messaging/slim-rpc.md | 427 ++++++++++++++++++++++++ docs/messaging/slim-slimrpc-compiler.md | 405 ++++++++++++++++++++++ 3 files changed, 1022 insertions(+) create mode 100644 docs/messaging/slim-a2a.md create mode 100644 docs/messaging/slim-rpc.md create mode 100644 docs/messaging/slim-slimrpc-compiler.md diff --git a/docs/messaging/slim-a2a.md b/docs/messaging/slim-a2a.md new file mode 100644 index 00000000..75249765 --- /dev/null +++ b/docs/messaging/slim-a2a.md @@ -0,0 +1,190 @@ +# SLIMA2A + +SLIMA2A is a native integration of A2A built on top of SLIM. It utilizes SLIM +RPC and the SLIM RPC compiler to compile A2A protobuf file and generate the +necessary code to enable A2A functionality on SLIM. + +# What is SLIM RPC and SLIM RCP compiler + +SLIM RPC (SLIM Remote Procedure Call) is a framework that enables Protocol +Buffers (protobuf) Remote Procedure Calls (RPC) over SLIM. This is similar to +gRPC, which uses HTTP/2 as its transport layer for protobuf-based RPC. More +information can be found [here](../slimrpc/README.md) + +To compile a protobuf file and generate the clients and service stub you can use +the [SLIM RPC compiler](../../../srpc-compiler/README.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/main/specification/grpc/a2a.proto) +file using the SLIM RPC compiler. The generated code is in +[a2a_pb2_srpc.py](./slima2a/types/a2a_pb2_srpc.py). + +# How to use SLIM A2A + +Using SLIM A2A 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 repo. The version adapted to use SLIM A2A can be +found in [travel_planner_agent](./examples/travel_planner_agent/) folder. In the +following section, we highlight and explain the key differences between the +standard and SLIM A2A implementations. + +## Travel Planner: Server + +In this section we highlight the main differences between the SLIM A2A +[server](./examples/travel_planner_agent/server.py) implementation with respect +to the original implementation in the A2A repository. + +1. Import the SRPC package + +```python +import srpc +``` + +2. Create the SRPCHandler. 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], + ) + + request_handler = DefaultRequestHandler( + agent_executor=TravelPlannerAgentExecutor(), + task_store=InMemoryTaskStore(), + ) + + servicer = SRPCHandler(agent_card, request_handler) +``` + +3. Setup the srcp.Server. This is the only place where you need to setup few + parameters that are specific to SLIM + +```python + server = srpc.Server( + local="agntcy/demo/travel_planner_agent", + slim={ + "endpoint": "http://localhost:46357", + "tls": { + "insecure": True, + }, + }, + shared_secret="secret", + ) +``` + + • 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). + +For more information about these settings, see the SLIM RCP +[README](../slimrpc/README.md). + +4. Register the Service + +```python + add_A2AServiceServicer_to_server( + servicer, + server, + ) +``` + +Your A2A server is now ready to run on SLIM. + +## Travel Planner: Client + +These are the main differences between the +[client](./examples/travel_planner_agent/client.py) using SLIM A2A and the +standard one. + +1. Create a channel. This requires a configuration that is similar to the server + +```python + def channel_factory(topic: str) -> srpc.Channel: + channel = srpc.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 + client_config = ClientConfig( + supported_transports=["JSONRPC", "srpc"], + streaming=True, + httpx_client=httpx_client, + srpc_channel_factory=channel_factory, + ) + client_factory = ClientFactory(client_config) + client_factory.register("srpc", SRPCTransport.create) + agent_card = minimal_agent_card("agntcy/demo/travel_planner_agent", ["srpc"]) + client = client_factory.create(card=agent_card) +``` + + diff --git a/docs/messaging/slim-rpc.md b/docs/messaging/slim-rpc.md new file mode 100644 index 00000000..c73a2226 --- /dev/null +++ b/docs/messaging/slim-rpc.md @@ -0,0 +1,427 @@ +# SRPC (SLIM Remote Procedure Call) + +SRPC, or SLIM Remote Procedure Call, is a mechanism designed to enable Protocol +Buffers (protobuf) RPC over SLIM (Secure Low-latency Inter-process Messaging). +This is analogous to gRPC, which leverages HTTP/2 as its underlying transport +layer for protobuf RPC. + +A key advantage of SRPC lies in its ability to seamlessly integrate SLIM as the +transport protocol for inter-application message exchange. This significantly +simplifies development: a protobuf file can be compiled to generate code that +utilizes SLIM for communication. Application developers can then interact with +the generated code much like they would with standard gRPC, while benefiting +from the inherent security features and efficiency provided by the SLIM +protocol. + +This README provides a guide to understanding how SRPC functions and how you can +implement it in your applications. For detailed instructions on compiling a +protobuf file to obtain the necessary SRPC stub code, please refer to the +dedicated [README file of the SRPC compiler](../../../srpc-compiler/README.md). + +## SLIM naming in SRPC + +In SRPC, each service and its individual RPC handlers are assigned a SLIM name, +facilitating efficient message routing and processing. Consider the [example +protobuf](./srpc/examples/simple/example.proto) definition, which defines four +distinct services: + +``` +syntax = "proto3"; + +package example_service; + +service Test { + rpc ExampleUnaryUnary(ExampleRequest) returns (ExampleResponse); + rpc ExampleUnaryStream(ExampleRequest) returns (stream ExampleResponse); + rpc ExampleStreamUnary(stream ExampleRequest) returns (ExampleResponse); + rpc ExampleStreamStream(stream ExampleRequest) returns (stream ExampleResponse); +} +``` + +This example showcases the four primary communication patterns supported by +gRPC: Unary-Unary, Unary-Stream, Stream-Unary, and Stream-Stream. + +For SRPC, a specific SLIM name is generated for each handler within a service. +This naming convention allows an application exposing the service to listen for +and process messages intended for a particular RPC method. The format for these +names is: + +``` +{package-name}.{service-name}-{handler_name} +``` + +Based on the example_service.Test definition, the names for each handler would +be: + +``` +example_service.Test-ExampleUnaryUnary +example_service.Test-ExampleUnaryStream +example_service.Test-ExampleStreamUnary +example_service.Test-ExampleStreamStream +``` + +This handler name is appended to the second component of the SLIM name +associated with the running application. For instance, to receive messages for +`example_service.Test-ExampleUnaryUnary`, an application would subscribe to: + +``` +component[0]/component[1]/component[2]-example_service.Test-ExampleUnaryUnary/component[3] +``` + +The subscription process is entirely managed by the SRPC package. Application +developers are not required to explicitly handle SLIM name subscriptions. +Instead, they only need to implement the specific functions that will be invoked +when a message arrives for a defined RPC method. + +## Example + +This section provides a detailed walkthrough of a basic SRPC client-server +interaction, leveraging the simple example provided in +[example](./srpc/examples/simple/) folder. + +### Generated Code + +The foundation of this example is the `example.proto` file, which is a +standard Protocol Buffers definition file. This file is compiled using the SRPC +compiler (refer to the [SRPC Compiler README] for installation and usage +instructions) to generate the necessary Python stub code. The generated code is +available in two files: `example_pb2.py` and `example_pb2_srpc.py`. +Specifically, `example_pb2_srpc.py` contains the SRPC-specific stubs for +both client and server implementations. Below are the key classes and functions +generated by the compiler: + +_Client Stub (TestStub)_: The TestStub class represents the client-side +interface for interacting with the Test service. It provides methods for each +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. + """ + self.ExampleUnaryUnary = channel.unary_unary( + "/example_service.Test/ExampleUnaryUnary", + request_serializer=pb2.ExampleRequest.SerializeToString, + response_deserializer=pb2.ExampleResponse.FromString, + ) + self.ExampleUnaryStream = channel.unary_stream( + "/example_service.Test/ExampleUnaryStream", + request_serializer=pb2.ExampleRequest.SerializeToString, + response_deserializer=pb2.ExampleResponse.FromString, + ) + self.ExampleStreamUnary = channel.stream_unary( + "/example_service.Test/ExampleStreamUnary", + request_serializer=pb2.ExampleRequest.SerializeToString, + response_deserializer=pb2.ExampleResponse.FromString, + ) + self.ExampleStreamStream = channel.stream_stream( + "/example_service.Test/ExampleStreamStream", + request_serializer=pb2.ExampleRequest.SerializeToString, + response_deserializer=pb2.ExampleResponse.FromString, + ) +``` + +_Server Servicer (TestServicer)_: The TestServicer class defines the server-side +interface. Developers implement this class to provide the actual business logic +for each RPC method. + +```python +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( + 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( + 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( + 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( + code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" + ) +``` + +_Server Registration Function (add_TestServicer_to_server)_: This utility +function registers an implemented TestServicer instance with an SRPC server. 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, + ) +``` + +### Server implementation + +The server-side logic is defined in +[server.py](./srpc/examples/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 SRPC server: + +```python +def create_server( + local: str, + slim: dict, + enable_opentelemetry: bool = False, + shared_secret: str = "", +) -> Server: + """ + Create a new SRPC 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() +``` + +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. + +This name is then used to construct the full SLIM RPC names for each method: + +``` +agntcy/grpc/server-example_service.Test-ExampleUnaryUnary +agntcy/grpc/server-example_service.Test-ExampleUnaryStream +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. + +Finally, the add_TestServicer_to_server function is called to register the +implemented TestService with the SRPC server, making its RPC methods available. + +```python + # Create RPCs + add_TestServicer_to_server( + TestService(), + server, + ) +``` + +### Client implementation + +The client-side implementation, found in +[client.py](./srpc/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 SRPC channel: + +```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", + ), + ) + + channel = channel_factory.new_channel(remote="agntcy/grpc/server") + + # Stubs + stubs = TestStub(channel) +``` + +As for the server case the `local` parameter, set to +`agntcy/grpc/client`, assigns a SLIM name to the client application. This +results in the client's internal RPC handlers being named: + +``` +agntcy/grpc/client-example_service.Test-ExampleUnaryUnary +agntcy/grpc/client-example_service.Test-ExampleUnaryStream +agntcy/grpc/client-example_service.Test-ExampleStreamUnary +agntcy/grpc/client-example_service.Test-ExampleStreamStream +``` + +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` initialize +MLS to match the server setup. + +The remote parameter, set to "agntcy/grpc/server", explicitly identifies the +SLIM name of the target server application. This allows the SRPC channel to +correctly route messages to the appropriate server endpoint within the SLIM +network. + +## SRPC under the hood + +SRPC was introduced to simplify the integration of existing applications with +SLIM. From a developer's perspective, using SRPC or gRPC is almost identical. +Application developers do not need to manage endpoint names or connectivity +details, as these aspects are handled automatically by SRPC and SLIM. + +All RPC services underneath utilize a sticky point-to-point session. The SLIM +session creation is implemented in inside SRPC in +[channel.py](./srpc/channel.py): + +```python + # Create a session + session = await self.local_app.create_session( + slim_bindings.PySessionConfiguration.FireAndForget( + max_retries=10, + timeout=datetime.timedelta(seconds=1), + sticky=True, + ) + ) +``` + +This session used by SRPC is also reliable. For each message, the sender waits +for an acknowledgment (ACK) packet for 1 second +(`timeout=datetime.timedelta(seconds=1)`). If no acknowledgment is received, +the 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 +forwarded to the same application instance. Let's illustrate this with an +example using the client and server applications described above. + +Imagine two server instances running the same RPC service. In this example We'll +focus on the Stream-Unary service, which is served by both server instances +under the general name +`agntcy/grpc/server-example_service.Test-ExampleStreamUnary`. In SLIM, each +application receives a unique ID. Thus, the full service name will include a +fourth component containing the server's ID. This ID is generated by SLIM itself +(see the doc from more details). Here we will use server-1 and server-2 for +simplicity. So, the two full names for the services will be: + +- `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1` +- `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-2` + +Now, if a new client wants to use the Stream-Unary service it needs to knows +only the general name +`agntcy/grpc/server-example_service.Test-ExampleStreamUnary`. SRPC will +leverage SLIM's capabilities to first discover one of the available services, +and then SRPC will use its full, specific name to consistently communicate with +that same endpoint. + +```mermaid +sequenceDiagram + autonumber + + participant Client + participant SLIM Node + participant Server 1 + participant Server 2 + + + Note over Client,Server 1: Discovery + Client->>SLIM Node: Discover agntcy/grpc/server-example_service.Test-ExampleStreamUnary + SLIM Node->>Server 1: Discover agntcy/grpc/server-example_service.Test-ExampleStreamUnary + Server 1->>SLIM Node: Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + SLIM Node->>Client: Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + + Note over Client,Server 1: Stream + Client->>SLIM Node: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + SLIM Node->>Server 1: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + Server 1->>SLIM Node: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client + SLIM Node->>Client: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client + + Client->>SLIM Node: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + SLIM Node->>Server 1: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + Server 1->>SLIM Node: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client + SLIM Node->>Client: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client + + Note over Client,Server 1: Unary + Server 1->>SLIM Node: Mgs to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client + SLIM Node->>Client: Mgs to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client + Client->>SLIM Node: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + SLIM Node->>Server 1: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 +``` + +The initial messages in the sequence diagram are used for the discovery phase. +After this step, the client application knows the specific name of the service +running on server-1. It's important to note that the first message in the +discovery phase is sent in anycast from the SLIM node, meaning it could be +forwarded to either of the two running servers. For instance, a subsequent call +of the same RPC from the same client might be served by server-2. + +After the discovery, the client will always send messages to the same endpoint, +as demonstrated in the streaming session phase in the example. + +Finally, the server is expected to send one message to the client to close the +service. The server learns the client's address (where to forward the message) +by examining the source field of all received messages. diff --git a/docs/messaging/slim-slimrpc-compiler.md b/docs/messaging/slim-slimrpc-compiler.md new file mode 100644 index 00000000..69f0e3b7 --- /dev/null +++ b/docs/messaging/slim-slimrpc-compiler.md @@ -0,0 +1,405 @@ +# Slim RPC Compiler + +The Slim RPC Compiler (`protoc-slimrpc-plugin`) is a protoc plugin that +generates Python client stubs and server servicers for SRPC (Slim RPC) from +Protocol Buffer service definitions. This plugin enables you to build +high-performance RPC services using the SRPC framework. + +## Features + +- Generates Python client stubs for calling SlimRPC services +- Generates Python server servicers for implementing SlimRPC services +- Supports all gRPC streaming patterns: unary-unary, unary-stream, stream-unary, + and stream-stream +- Compatible with both `protoc` and `buf` build systems +- Automatic import resolution for Protocol Buffer dependencies + +## Installation + +### Option 1: Install via Cargo + +```bash +cargo install agntcy-protoc-slimrpc-plugin +``` + +This will install the `protoc-slimrpc-plugin` binary to your Cargo bin directory +(usually `~/.cargo/bin`). + +### Option 2: Compile from Source + +1. Clone the repository: + +```bash +git clone https://github.com/agntcy/slim.git +cd slim/data-plane/slimrpc-compiler +``` + +2. Build the plugin: + +```bash +cargo build --release +``` + +3. The compiled binary will be available at + `data-plane/target/release/protoc-slimrpc-plugin` + +## Usage + +### Example Protocol Buffer Definition + +Create a file called `example.proto`: + +```proto +syntax = "proto3"; + +package example_service; + +service Test { + rpc ExampleUnaryUnary(ExampleRequest) returns (ExampleResponse); + rpc ExampleUnaryStream(ExampleRequest) returns (stream ExampleResponse); + rpc ExampleStreamUnary(stream ExampleRequest) returns (ExampleResponse); + rpc ExampleStreamStream(stream ExampleRequest) returns (stream ExampleResponse); +} + +message ExampleRequest { + string example_string = 1; + int64 example_integer = 2; +} + +message ExampleResponse { + string example_string = 1; + int64 example_integer = 2; +} +``` + +### 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 SRPC files +protoc \ + --python_out=. \ + --pyi_out=. \ + --plugin=~/.cargo/bin/protoc-slimrpc-plugin \ + --slimrpc_out=. \ + example.proto +``` + +This will generate: + +- `example_pb2.py` - Standard protobuf Python bindings +- `example_pb2_slimrpc.py` - SRPC 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=~/.cargo/bin/protoc-slimrpc-plugin \ + --slimrpc_out=types_import="from a2a.grpc import a2a_pb2 as a2a__pb2":. \ + example.proto +``` + +### Using with buf + +#### Prerequisites + +- `buf` CLI installed +- `protoc-slimrpc-plugin` binary in your PATH + +#### 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: . +``` + +#### Generate Code + +```bash +buf generate +``` + +Or generate from a specific file: + +```bash +buf generate --path example.proto +``` + +#### Advanced buf Configuration + +For more complex setups with custom options: + +```yaml +version: v2 +managed: + enabled: true +plugins: + - local: protoc-slimrpc-plugin + out: generated + opt: + - types_import=from .pb2_types import example_pb2 as pb2 + strategy: all + - remote: buf.build/protocolbuffers/python + out: generated + strategy: all +``` + +## Generated Code Structure + +For the example above, the generated `example_pb2_slimrpc.py` will contain: + +### Client Stub + +```python +class TestStub: + """Client stub for Test.""" + def __init__(self, channel): + """Constructor. + + Args: + channel: A slimrpc.Channel. + """ + self.ExampleUnaryUnary = channel.unary_unary(...) + self.ExampleUnaryStream = channel.unary_stream(...) + # ... other methods +``` + +### Server Servicer + +```python +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( + code=code__pb2.UNIMPLEMENTED, message="Method not implemented!" + ) + # ... other methods +``` + +### Registration Function + +```python +def add_TestServicer_to_server(servicer, server: slimrpc.Server): + # Registers the servicer with the SRPC server + pass +``` + +## Plugin Parameters + +The plugin supports the following parameters: + +- `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 Usage in Python + +### Client Usage + +```python +import asyncio +import logging +from collections.abc import AsyncGenerator + +import slimrpc +from slimrpc.examples.simple.types.example_pb2 import ExampleRequest +from slimrpc.examples.simple.types.example_pb2_slimrpc import TestStub + +logger = logging.getLogger(__name__) + + +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", + ) + + # Stubs + stubs = TestStub(channel) + + # Call method + try: + request = ExampleRequest(example_integer=1, example_string="hello") + response = await stubs.ExampleUnaryUnary(request, timeout=2) + + logger.info(f"Response: {response}") + + responses = stubs.ExampleUnaryStream(request, timeout=2) + async for resp in responses: + logger.info(f"Stream Response: {resp}") + + async def stream_requests() -> AsyncGenerator[ExampleRequest]: + for i in range(10): + yield ExampleRequest(example_integer=i, example_string=f"Request {i}") + + 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") +``` + +### 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 SRPC 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() +``` + +## Troubleshooting + +### Plugin Not Found + +If you get an error that the plugin is not found: + +- Ensure `protoc-slimrpc-plugin` is in your PATH +- Or specify the full path: + `--plugin=protoc-gen-slimrpc=/full/path/to/protoc-slimrpc-plugin` + +### 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: + +- Ensure you have Rust and Cargo installed +- Check that all dependencies are available +- Try cleaning and rebuilding: `cargo clean && cargo build --release` + +## Contributing + +Please see the main repository's contributing guidelines at +[CONTRIBUTING.md](../../CONTRIBUTING.md). + +## License + +This project is licensed under the Apache 2.0 License - see the +[LICENSE.md](../../LICENSE.md) file for details. From b1c29674552c8196358837af705d2ab6a5fa05e0 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Wed, 17 Sep 2025 12:37:45 +0200 Subject: [PATCH 02/16] doc(SLIM): fix broken links Signed-off-by: Mauro Sardara --- docs/messaging/slim-a2a.md | 16 ++++++++-------- docs/messaging/slim-rpc.md | 12 ++++++------ docs/messaging/slim-slimrpc-compiler.md | 10 ---------- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/docs/messaging/slim-a2a.md b/docs/messaging/slim-a2a.md index 75249765..e4baaf48 100644 --- a/docs/messaging/slim-a2a.md +++ b/docs/messaging/slim-a2a.md @@ -9,16 +9,16 @@ necessary code to enable A2A functionality on SLIM. SLIM RPC (SLIM Remote Procedure Call) is a framework that enables Protocol Buffers (protobuf) Remote Procedure Calls (RPC) over SLIM. This is similar to gRPC, which uses HTTP/2 as its transport layer for protobuf-based RPC. More -information can be found [here](../slimrpc/README.md) +information can be found [here](./slim-rpc.md) To compile a protobuf file and generate the clients and service stub you can use -the [SLIM RPC compiler](../../../srpc-compiler/README.md). This works in a +the [SLIM RPC 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/main/specification/grpc/a2a.proto) file using the SLIM RPC compiler. The generated code is in -[a2a_pb2_srpc.py](./slima2a/types/a2a_pb2_srpc.py). +[a2a_pb2_srpc.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slima2a/slima2a/types/a2a_pb2_slimrpc.py). # How to use SLIM A2A @@ -26,14 +26,14 @@ Using SLIM A2A 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 repo. The version adapted to use SLIM A2A can be -found in [travel_planner_agent](./examples/travel_planner_agent/) folder. In the +found in [travel_planner_agent](https://github.com/agntcy/slim/tree/main/data-plane/python/integrations/slima2a/examples/travel_planner_agent) folder. In the following section, we highlight and explain the key differences between the standard and SLIM A2A implementations. ## Travel Planner: Server In this section we highlight the main differences between the SLIM A2A -[server](./examples/travel_planner_agent/server.py) implementation with respect +[server](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slima2a/examples/travel_planner_agent/server.py) implementation with respect to the original implementation in the A2A repository. 1. Import the SRPC package @@ -86,9 +86,9 @@ import srpc • shared_secret: Used to set up MLS (Message Layer Security). For more information about these settings, see the SLIM RCP -[README](../slimrpc/README.md). +[README](./slim-rpc.md). -4. Register the Service +1. Register the Service ```python add_A2AServiceServicer_to_server( @@ -102,7 +102,7 @@ Your A2A server is now ready to run on SLIM. ## Travel Planner: Client These are the main differences between the -[client](./examples/travel_planner_agent/client.py) using SLIM A2A and the +[client](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slima2a/examples/travel_planner_agent/client.py) using SLIM A2A and the standard one. 1. Create a channel. This requires a configuration that is similar to the server diff --git a/docs/messaging/slim-rpc.md b/docs/messaging/slim-rpc.md index c73a2226..b5a91ffb 100644 --- a/docs/messaging/slim-rpc.md +++ b/docs/messaging/slim-rpc.md @@ -16,13 +16,13 @@ protocol. This README provides a guide to understanding how SRPC functions and how you can implement it in your applications. For detailed instructions on compiling a protobuf file to obtain the necessary SRPC stub code, please refer to the -dedicated [README file of the SRPC compiler](../../../srpc-compiler/README.md). +dedicated [README file of the SRPC compiler](./slim-slimrpc-compiler.md). ## SLIM naming in SRPC In SRPC, each service and its individual RPC handlers are assigned a SLIM name, facilitating efficient message routing and processing. Consider the [example -protobuf](./srpc/examples/simple/example.proto) definition, which defines four +protobuf](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/example.proto) definition, which defines four distinct services: ``` @@ -77,7 +77,7 @@ when a message arrives for a defined RPC method. This section provides a detailed walkthrough of a basic SRPC client-server interaction, leveraging the simple example provided in -[example](./srpc/examples/simple/) folder. +[example](https://github.com/agntcy/slim/tree/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple) folder. ### Generated Code @@ -195,7 +195,7 @@ def add_TestServicer_to_server(servicer, server: slimrpc.Server): ### Server implementation The server-side logic is defined in -[server.py](./srpc/examples/simple/server.py). Similar to standard gRPC +[server.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/examples/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 @@ -281,7 +281,7 @@ implemented TestService with the SRPC server, making its RPC methods available. ### Client implementation The client-side implementation, found in -[client.py](./srpc/examples/simple/client.py), largely mirrors the structure of +[client.py](https://github.com/agntcy/slim/blob/main/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 SRPC channel: @@ -336,7 +336,7 @@ details, as these aspects are handled automatically by SRPC and SLIM. All RPC services underneath utilize a sticky point-to-point session. The SLIM session creation is implemented in inside SRPC in -[channel.py](./srpc/channel.py): +[channel.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/channel.py): ```python # Create a session diff --git a/docs/messaging/slim-slimrpc-compiler.md b/docs/messaging/slim-slimrpc-compiler.md index 69f0e3b7..fc7d799d 100644 --- a/docs/messaging/slim-slimrpc-compiler.md +++ b/docs/messaging/slim-slimrpc-compiler.md @@ -393,13 +393,3 @@ If the plugin fails to build: - Ensure you have Rust and Cargo installed - Check that all dependencies are available - Try cleaning and rebuilding: `cargo clean && cargo build --release` - -## Contributing - -Please see the main repository's contributing guidelines at -[CONTRIBUTING.md](../../CONTRIBUTING.md). - -## License - -This project is licensed under the Apache 2.0 License - see the -[LICENSE.md](../../LICENSE.md) file for details. From 4d0be501214e36a9ed47a144abfa72090e23bffa Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Wed, 17 Sep 2025 13:24:02 +0200 Subject: [PATCH 03/16] doc(SLIM): fix slimrpc Signed-off-by: Mauro Sardara --- docs/messaging/.index | 3 + docs/messaging/slim-rpc.md | 168 +++++++++++++++++++------------------ 2 files changed, 91 insertions(+), 80 deletions(-) diff --git a/docs/messaging/.index b/docs/messaging/.index index 7c5a21b0..6e10e4cd 100644 --- a/docs/messaging/.index +++ b/docs/messaging/.index @@ -5,4 +5,7 @@ nav: - SLIM Controller: slim-controller.md - SLIM Group Management: slim-group.md - SLIM Group Communication Tutorial: slim-group-tutorial.md + - SLIMRPC: slim-rpc.md + - SLIMRPC Compiler Plugin: slim-slimrpc-compiler.md + - SLIM A2A: slim-a2a.md - SLIM and MCP Integration: slim-mcp.md diff --git a/docs/messaging/slim-rpc.md b/docs/messaging/slim-rpc.md index b5a91ffb..4e7820ba 100644 --- a/docs/messaging/slim-rpc.md +++ b/docs/messaging/slim-rpc.md @@ -1,11 +1,11 @@ -# SRPC (SLIM Remote Procedure Call) +# SLIMRPC (SLIM Remote Procedure Call) -SRPC, or SLIM Remote Procedure Call, is a mechanism designed to enable Protocol +SLIMRPC, or SLIM Remote Procedure Call, is a mechanism designed to enable Protocol Buffers (protobuf) RPC over SLIM (Secure Low-latency Inter-process Messaging). This is analogous to gRPC, which leverages HTTP/2 as its underlying transport layer for protobuf RPC. -A key advantage of SRPC lies in its ability to seamlessly integrate SLIM as the +A key advantage of SLIMRPC lies in its ability to seamlessly integrate SLIM as the transport protocol for inter-application message exchange. This significantly simplifies development: a protobuf file can be compiled to generate code that utilizes SLIM for communication. Application developers can then interact with @@ -13,19 +13,19 @@ the generated code much like they would with standard gRPC, while benefiting from the inherent security features and efficiency provided by the SLIM protocol. -This README provides a guide to understanding how SRPC functions and how you can +This README provides a guide to understanding how SLIMRPC functions and how you can implement it in your applications. For detailed instructions on compiling a -protobuf file to obtain the necessary SRPC stub code, please refer to the -dedicated [README file of the SRPC compiler](./slim-slimrpc-compiler.md). +protobuf file to obtain the necessary SLIMRPC stub code, please refer to the +dedicated [SLIMRPC compiler documentation](./slim-slimrpc-compiler.md). -## SLIM naming in SRPC +## SLIM naming in SLIMRPC -In SRPC, each service and its individual RPC handlers are assigned a SLIM name, +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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/example.proto) definition, which defines four distinct services: -``` +```proto syntax = "proto3"; package example_service; @@ -36,12 +36,22 @@ service Test { rpc ExampleStreamUnary(stream ExampleRequest) returns (ExampleResponse); rpc ExampleStreamStream(stream ExampleRequest) returns (stream ExampleResponse); } + +message ExampleRequest { + string example_string = 1; + int64 example_integer = 2; +} + +message ExampleResponse { + string example_string = 1; + int64 example_integer = 2; +} ``` This example showcases the four primary communication patterns supported by gRPC: Unary-Unary, Unary-Stream, Stream-Unary, and Stream-Stream. -For SRPC, a specific SLIM name is generated for each handler within a service. +For SLIMRPC, a specific SLIM name is generated for each handler within a service. This naming convention allows an application exposing the service to listen for and process messages intended for a particular RPC method. The format for these names is: @@ -68,25 +78,24 @@ associated with the running application. For instance, to receive messages for component[0]/component[1]/component[2]-example_service.Test-ExampleUnaryUnary/component[3] ``` -The subscription process is entirely managed by the SRPC package. Application +The subscription process is entirely managed by the SLIMRPC package. Application developers are not required to explicitly handle SLIM name subscriptions. Instead, they only need to implement the specific functions that will be invoked -when a message arrives for a defined RPC method. +when a message arrives for a defined RPC method, exactly as they would with standard gRPC. ## Example -This section provides a detailed walkthrough of a basic SRPC client-server +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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple) folder. ### Generated Code The foundation of this example is the `example.proto` file, which is a -standard Protocol Buffers definition file. This file is compiled using the SRPC -compiler (refer to the [SRPC Compiler README] for installation and usage -instructions) to generate the necessary Python stub code. The generated code is -available in two files: `example_pb2.py` and `example_pb2_srpc.py`. -Specifically, `example_pb2_srpc.py` contains the SRPC-specific stubs for +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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/types/example_pb2.py) and [example_pb2_slimrpc.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/examples/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: @@ -156,7 +165,7 @@ class TestServicer(): ``` _Server Registration Function (add_TestServicer_to_server)_: This utility -function registers an implemented TestServicer instance with an SRPC server. It +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. @@ -203,7 +212,7 @@ methods. The SLIM-specific code and configuration is handled within the amain() asynchronous function. This function utilizes the create_server helper to -instantiate an SRPC server: +instantiate an SLIMRPC server: ```python def create_server( @@ -213,7 +222,7 @@ def create_server( shared_secret: str = "", ) -> Server: """ - Create a new SRPC server instance. + Create a new SLIMRPC server instance. """ server = Server( local=local, @@ -265,10 +274,10 @@ 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. +recommended, please refer to [the documentation for proper MLS configuration](./slim-group.md#identity-management). Finally, the add_TestServicer_to_server function is called to register the -implemented TestService with the SRPC server, making its RPC methods available. +implemented TestService with the SLIMRPC server, making its RPC methods available. ```python # Create RPCs @@ -283,7 +292,7 @@ implemented TestService with the SRPC server, making its RPC methods available. The client-side implementation, found in [client.py](https://github.com/agntcy/slim/blob/main/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 SRPC channel: +the creation of the SLIMRPC channel: ```python channel_factory = slimrpc.ChannelFactory( @@ -306,36 +315,31 @@ the creation of the SRPC channel: stubs = TestStub(channel) ``` -As for the server case the `local` parameter, set to -`agntcy/grpc/client`, assigns a SLIM name to the client application. This -results in the client's internal RPC handlers being named: - -``` -agntcy/grpc/client-example_service.Test-ExampleUnaryUnary -agntcy/grpc/client-example_service.Test-ExampleUnaryStream -agntcy/grpc/client-example_service.Test-ExampleStreamUnary -agntcy/grpc/client-example_service.Test-ExampleStreamStream -``` +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. 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` initialize -MLS to match the server setup. +consistent with the server's configuration, while `shared_secret` is used to initialize +MLS. The remote parameter, set to "agntcy/grpc/server", explicitly identifies the -SLIM name of the target server application. This allows the SRPC channel to +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. +network. Since both client and server use the same protobuf definition, the client +can invoke specific services and methods with type safety and consistency. -## SRPC under the hood +## SLIMRPC under the hood -SRPC was introduced to simplify the integration of existing applications with -SLIM. From a developer's perspective, using SRPC or gRPC is almost identical. +SLIMRPC was introduced to simplify the integration of existing applications with +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 SRPC and SLIM. +details, as these aspects are handled automatically by SLIMRPC and SLIM. All RPC services underneath utilize a sticky point-to-point session. The SLIM -session creation is implemented in inside SRPC in +session creation is implemented in inside SLIMRPC in [channel.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/channel.py): ```python @@ -349,7 +353,7 @@ session creation is implemented in inside SRPC in ) ``` -This session used by SRPC is also reliable. For each message, the sender waits +This session used by SLIMRPC is also reliable. For each message, the sender waits for an acknowledgment (ACK) packet for 1 second (`timeout=datetime.timedelta(seconds=1)`). If no acknowledgment is received, the message will be re-sent up to 10 times (`max_retries=10`) before @@ -365,63 +369,67 @@ under the general name `agntcy/grpc/server-example_service.Test-ExampleStreamUnary`. In SLIM, each application receives a unique ID. Thus, the full service name will include a fourth component containing the server's ID. This ID is generated by SLIM itself -(see the doc from more details). Here we will use server-1 and server-2 for +(see the [doc](./slim-data-plane.md) for more details). Here we will use instance-1 and instance-2 for simplicity. So, the two full names for the services will be: -- `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1` -- `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-2` +- `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1` +- `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-2` Now, if a new client wants to use the Stream-Unary service it needs to knows only the general name -`agntcy/grpc/server-example_service.Test-ExampleStreamUnary`. SRPC will +`agntcy/grpc/server-example_service.Test-ExampleStreamUnary`. SLIMRPC will leverage SLIM's capabilities to first discover one of the available services, -and then SRPC will use its full, specific name to consistently communicate with +and then SLIMRPC will use its full, specific name to consistently communicate with that same endpoint. +The client will register to SLIM with the name `agntcy/grpc/client`, and it will get an +unique ID assigned by SLIM, for example `instance-1`. So the full name of the client +will be `agntcy/grpc/client/instance-1`. + ```mermaid sequenceDiagram autonumber - participant Client + participant Client (instance-1) participant SLIM Node - participant Server 1 - participant Server 2 - - - Note over Client,Server 1: Discovery - Client->>SLIM Node: Discover agntcy/grpc/server-example_service.Test-ExampleStreamUnary - SLIM Node->>Server 1: Discover agntcy/grpc/server-example_service.Test-ExampleStreamUnary - Server 1->>SLIM Node: Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 - SLIM Node->>Client: Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 - - Note over Client,Server 1: Stream - Client->>SLIM Node: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 - SLIM Node->>Server 1: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 - Server 1->>SLIM Node: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client - SLIM Node->>Client: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client - - Client->>SLIM Node: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 - SLIM Node->>Server 1: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 - Server 1->>SLIM Node: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client - SLIM Node->>Client: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client - - Note over Client,Server 1: Unary - Server 1->>SLIM Node: Mgs to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client - SLIM Node->>Client: Mgs to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/client - Client->>SLIM Node: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 - SLIM Node->>Server 1: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/server-1 + participant Server (instance-1) + participant Server (instance-2) + + + Note over Client (instance-1),Server (instance-1): Discovery + Client (instance-1)->>SLIM Node: Discover agntcy/grpc/server-example_service.Test-ExampleStreamUnary + SLIM Node->>Server (instance-1): Discover agntcy/grpc/server-example_service.Test-ExampleStreamUnary + Server (instance-1)->>SLIM Node: Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + SLIM Node->>Client (instance-1): Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + + Note over Client (instance-1),Server (instance-1): Stream + Client (instance-1)->>SLIM Node: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + SLIM Node->>Server (instance-1): Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + Server (instance-1)->>SLIM Node: Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + SLIM Node->>Client (instance-1): Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + + Client (instance-1)->>SLIM Node: Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + SLIM Node->>Server (instance-1): Msg to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + Server (instance-1)->>SLIM Node: Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + SLIM Node->>Client (instance-1): Ack from agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + + Note over Client (instance-1),Server (instance-1): Unary + Server (instance-1)->>SLIM Node: Msg to agntcy/grpc/client/instance-1 + SLIM Node->>Client (instance-1): Msg to agntcy/grpc/client/instance-1 + Client (instance-1)->>SLIM Node: Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 + SLIM Node->>Server (instance-1): Ack to agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1 ``` The initial messages in the sequence diagram are used for the discovery phase. After this step, the client application knows the specific name of the service -running on server-1. It's important to note that the first message in the +running on instance-1. It's important to note that the first message in the discovery phase is sent in anycast from the SLIM node, meaning it could be -forwarded to either of the two running servers. For instance, a subsequent call -of the same RPC from the same client might be served by server-2. +forwarded to **either of the two running servers**. For instance, a subsequent call +of the same RPC from the same client might be served by `Server (instance-2)`. After the discovery, the client will always send messages to the same endpoint, as demonstrated in the streaming session phase in the example. Finally, the server is expected to send one message to the client to close the service. The server learns the client's address (where to forward the message) -by examining the source field of all received messages. +by examining the source field of the received messages. From 0fe5ff179a25f08b76642ad077729ab40672eff5 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Wed, 17 Sep 2025 13:27:22 +0200 Subject: [PATCH 04/16] doc(SLIM): fix slimrpc Signed-off-by: Mauro Sardara --- docs/messaging/slim-rpc.md | 147 ++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/docs/messaging/slim-rpc.md b/docs/messaging/slim-rpc.md index 4e7820ba..b5ef53d1 100644 --- a/docs/messaging/slim-rpc.md +++ b/docs/messaging/slim-rpc.md @@ -1,29 +1,30 @@ # SLIMRPC (SLIM Remote Procedure Call) -SLIMRPC, or SLIM Remote Procedure Call, is a mechanism designed to enable Protocol -Buffers (protobuf) RPC over SLIM (Secure Low-latency Inter-process Messaging). -This is analogous to gRPC, which leverages HTTP/2 as its underlying transport -layer for protobuf RPC. - -A key advantage of SLIMRPC lies in its ability to seamlessly integrate SLIM as the -transport protocol for inter-application message exchange. This significantly -simplifies development: a protobuf file can be compiled to generate code that -utilizes SLIM for communication. Application developers can then interact with -the generated code much like they would with standard gRPC, while benefiting -from the inherent security features and efficiency provided by the SLIM -protocol. - -This README provides a guide to understanding how SLIMRPC functions and how you can -implement it in your applications. For detailed instructions on compiling a +SLIMRPC, or SLIM Remote Procedure Call, is a mechanism designed to enable +Protocol Buffers (protobuf) RPC over SLIM (Secure Low-latency Inter-process +Messaging). This is analogous to gRPC, which leverages HTTP/2 as its underlying +transport layer for protobuf RPC. + +A key advantage of SLIMRPC lies in its ability to seamlessly integrate SLIM as +the transport protocol for inter-application message exchange. This +significantly simplifies development: a protobuf file can be compiled to +generate code that utilizes SLIM for communication. Application developers can +then interact with the generated code much like they would with standard gRPC, +while benefiting from the inherent security features and efficiency provided by +the SLIM protocol. + +This README provides a guide to understanding how SLIMRPC functions and how you +can implement it in your applications. For detailed instructions on compiling a protobuf file to obtain the necessary SLIMRPC stub code, please refer to the dedicated [SLIMRPC compiler documentation](./slim-slimrpc-compiler.md). ## SLIM naming in SLIMRPC -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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/example.proto) definition, which defines four -distinct services: +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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/example.proto) +definition, which defines four distinct services: ```proto syntax = "proto3"; @@ -51,10 +52,10 @@ message ExampleResponse { This example showcases the four primary communication patterns supported by gRPC: Unary-Unary, Unary-Stream, Stream-Unary, and Stream-Stream. -For SLIMRPC, a specific SLIM name is generated for each handler within a service. -This naming convention allows an application exposing the service to listen for -and process messages intended for a particular RPC method. The format for these -names is: +For SLIMRPC, a specific SLIM name is generated for each handler within a +service. This naming convention allows an application exposing the service to +listen for and process messages intended for a particular RPC method. The format +for these names is: ``` {package-name}.{service-name}-{handler_name} @@ -81,20 +82,25 @@ component[0]/component[1]/component[2]-example_service.Test-ExampleUnaryUnary/co The subscription process is entirely managed by the SLIMRPC package. Application developers are not required to explicitly handle SLIM name subscriptions. Instead, they only need to implement the specific functions that will be invoked -when a message arrives for a defined RPC method, exactly as they would with standard gRPC. +when a message arrives for a defined RPC method, exactly as they would with +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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple) folder. +[example](https://github.com/agntcy/slim/tree/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple) +folder. ### Generated Code -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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/types/example_pb2.py) and [example_pb2_slimrpc.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/types/example_pb2_slimrpc.py). +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/main/data-plane/python/integrations/slimrpc/slimrpc/examples/simple/types/example_pb2.py) +and +[example_pb2_slimrpc.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/examples/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: @@ -165,9 +171,9 @@ class TestServicer(): ``` _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. +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. ```python def add_TestServicer_to_server(servicer, server: slimrpc.Server): @@ -204,11 +210,11 @@ def add_TestServicer_to_server(servicer, server: slimrpc.Server): ### Server implementation The server-side logic is defined in -[server.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/examples/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. +[server.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slimrpc/slimrpc/examples/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 @@ -269,15 +275,17 @@ 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#identity-management). +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#identity-management). Finally, the add_TestServicer_to_server function is called to register the -implemented TestService with the SLIMRPC server, making its RPC methods available. +implemented TestService with the SLIMRPC server, making its RPC methods +available. ```python # Create RPCs @@ -290,9 +298,9 @@ implemented TestService with the SLIMRPC server, making its RPC methods availabl ### Client implementation The client-side implementation, found in -[client.py](https://github.com/agntcy/slim/blob/main/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/main/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: ```python channel_factory = slimrpc.ChannelFactory( @@ -316,20 +324,21 @@ the creation of the SLIMRPC 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. +`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. 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. +consistent with the server's configuration, while `shared_secret` is used to +initialize MLS. 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. +network. Since both client and server use the same protobuf definition, the +client can invoke specific services and methods with type safety and +consistency. ## SLIMRPC under the hood @@ -353,11 +362,11 @@ session creation is implemented in inside SLIMRPC in ) ``` -This session used by SLIMRPC is also reliable. For each message, the sender waits -for an acknowledgment (ACK) packet for 1 second -(`timeout=datetime.timedelta(seconds=1)`). If no acknowledgment is received, -the message will be re-sent up to 10 times (`max_retries=10`) before -notifying the application of a communication error. +This session used by SLIMRPC is also reliable. For each message, the sender +waits for an acknowledgment (ACK) packet for 1 second +(`timeout=datetime.timedelta(seconds=1)`). If no acknowledgment is received, the +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 forwarded to the same application instance. Let's illustrate this with an @@ -369,8 +378,9 @@ under the general name `agntcy/grpc/server-example_service.Test-ExampleStreamUnary`. In SLIM, each application receives a unique ID. Thus, the full service name will include a fourth component containing the server's ID. This ID is generated by SLIM itself -(see the [doc](./slim-data-plane.md) for more details). Here we will use instance-1 and instance-2 for -simplicity. So, the two full names for the services will be: +(see the [doc](./slim-data-plane.md) for more details). Here we will use +instance-1 and instance-2 for simplicity. So, the two full names for the +services will be: - `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-1` - `agntcy/grpc/server-example_service.Test-ExampleStreamUnary/instance-2` @@ -379,12 +389,12 @@ Now, if a new client wants to use the Stream-Unary service it needs to knows only the general name `agntcy/grpc/server-example_service.Test-ExampleStreamUnary`. SLIMRPC will leverage SLIM's capabilities to first discover one of the available services, -and then SLIMRPC will use its full, specific name to consistently communicate with -that same endpoint. +and then SLIMRPC will use its full, specific name to consistently communicate +with that same endpoint. -The client will register to SLIM with the name `agntcy/grpc/client`, and it will get an -unique ID assigned by SLIM, for example `instance-1`. So the full name of the client -will be `agntcy/grpc/client/instance-1`. +The client will register to SLIM with the name `agntcy/grpc/client`, and it will +get an unique ID assigned by SLIM, for example `instance-1`. So the full name of +the client will be `agntcy/grpc/client/instance-1`. ```mermaid sequenceDiagram @@ -424,8 +434,9 @@ The initial messages in the sequence diagram are used for the discovery phase. After this step, the client application knows the specific name of the service running on instance-1. It's important to note that the first message in the discovery phase is sent in anycast from the SLIM node, meaning it could be -forwarded to **either of the two running servers**. For instance, a subsequent call -of the same RPC from the same client might be served by `Server (instance-2)`. +forwarded to **either of the two running servers**. For instance, a subsequent +call of the same RPC from the same client might be served by `Server +(instance-2)`. After the discovery, the client will always send messages to the same endpoint, as demonstrated in the streaming session phase in the example. From 0ce38ee9c95a0e01bf9c831b351727aefea9f426 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Wed, 17 Sep 2025 13:28:39 +0200 Subject: [PATCH 05/16] doc(SLIM): fix slimrpc Signed-off-by: Mauro Sardara --- docs/messaging/slim-rpc.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/messaging/slim-rpc.md b/docs/messaging/slim-rpc.md index b5ef53d1..29d54bf9 100644 --- a/docs/messaging/slim-rpc.md +++ b/docs/messaging/slim-rpc.md @@ -13,10 +13,11 @@ then interact with the generated code much like they would with standard gRPC, while benefiting from the inherent security features and efficiency provided by the SLIM protocol. -This README provides a guide to understanding how SLIMRPC functions and how you -can implement it in your applications. For detailed instructions on compiling a -protobuf file to obtain the necessary SLIMRPC stub code, please refer to the -dedicated [SLIMRPC compiler documentation](./slim-slimrpc-compiler.md). +This documentation provides a guide to understanding how SLIMRPC functions and +how you can implement it in your applications. For detailed instructions on +compiling a protobuf file to obtain the necessary SLIMRPC stub code, please +refer to the dedicated [SLIMRPC compiler +documentation](./slim-slimrpc-compiler.md). ## SLIM naming in SLIMRPC From fe5f23591dde1e620390df5307b32b2458e59cdc Mon Sep 17 00:00:00 2001 From: Michele Papalini Date: Wed, 17 Sep 2025 14:18:23 +0200 Subject: [PATCH 06/16] docs: fixes Signed-off-by: Michele Papalini --- docs/messaging/.index | 2 +- docs/messaging/slim-a2a.md | 61 ++++++++++++------------- docs/messaging/slim-rpc.md | 18 ++++---- docs/messaging/slim-slimrpc-compiler.md | 2 +- 4 files changed, 41 insertions(+), 42 deletions(-) diff --git a/docs/messaging/.index b/docs/messaging/.index index 6e10e4cd..f2199854 100644 --- a/docs/messaging/.index +++ b/docs/messaging/.index @@ -7,5 +7,5 @@ nav: - SLIM Group Communication Tutorial: slim-group-tutorial.md - SLIMRPC: slim-rpc.md - SLIMRPC Compiler Plugin: slim-slimrpc-compiler.md - - SLIM A2A: slim-a2a.md + - SLIMA2A: slim-a2a.md - SLIM and MCP Integration: slim-mcp.md diff --git a/docs/messaging/slim-a2a.md b/docs/messaging/slim-a2a.md index e4baaf48..0506326a 100644 --- a/docs/messaging/slim-a2a.md +++ b/docs/messaging/slim-a2a.md @@ -1,28 +1,28 @@ # SLIMA2A -SLIMA2A is a native integration of A2A built on top of SLIM. It utilizes SLIM -RPC and the SLIM RPC compiler to compile A2A protobuf file and generate the +SLIMA2A is a native integration of A2A built on top of SLIM. It utilizes SLIMRPC +and the SLIMRPC compiler to compile A2A protobuf file and generate the necessary code to enable A2A functionality on SLIM. -# What is SLIM RPC and SLIM RCP compiler +# What is SLIMRPC and SLIMRCP compiler -SLIM RPC (SLIM Remote Procedure Call) is a framework that enables Protocol +SLIMRPC (SLIM Remote Procedure Call) is a framework that enables Protocol Buffers (protobuf) Remote Procedure Calls (RPC) over SLIM. This is similar to gRPC, which uses HTTP/2 as its transport layer for protobuf-based RPC. More information can be found [here](./slim-rpc.md) To compile a protobuf file and generate the clients and service stub you can use -the [SLIM RPC compiler](./slim-slimrpc-compiler.md). This works in a +the [SLIMRPC compiler](./slim-slimrpc-compiler.md). This works in a similar way to the protoc compiler. -For SLIM A2A we compiled the +For SLIMA2A we compiled the [a2a.proto](https://github.com/a2aproject/A2A/blob/main/specification/grpc/a2a.proto) file using the SLIM RPC compiler. The generated code is in [a2a_pb2_srpc.py](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slima2a/slima2a/types/a2a_pb2_slimrpc.py). -# How to use SLIM A2A +# How to use SLIMA2A -Using SLIM A2A is very similar to using the standard A2A implementation. As a +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 repo. The version adapted to use SLIM A2A can be @@ -36,16 +36,16 @@ In this section we highlight the main differences between the SLIM A2A [server](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slima2a/examples/travel_planner_agent/server.py) implementation with respect to the original implementation in the A2A repository. + 1. Import the SRPC package -```python -import srpc -``` + ```python + import srpc + ``` -2. Create the SRPCHandler. Notice that the definitions for `AgentCard` and - `DefaultRequestHandler` remain unchanged from the original A2A example +2. Create the SRPCHandler. Notice that the definitions for `AgentCard` and `DefaultRequestHandler` remain unchanged from the original A2A example -```python + ```python agent_card = AgentCard( name="travel planner Agent", description="travel planner", @@ -63,12 +63,11 @@ import srpc ) servicer = SRPCHandler(agent_card, request_handler) -``` + ``` -3. Setup the srcp.Server. This is the only place where you need to setup few - parameters that are specific to SLIM +3. Setup the srcp.Server. This is the only place where you need to setup few parameters that are specific to SLIM -```python + ```python server = srpc.Server( local="agntcy/demo/travel_planner_agent", slim={ @@ -79,23 +78,22 @@ import srpc }, shared_secret="secret", ) -``` + ``` - • 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). + - 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). -For more information about these settings, see the SLIM RCP -[README](./slim-rpc.md). + For more information about these settings, see the [SLIMRCP](./slim-rpc.md). -1. Register the Service +4. Register the Service -```python + ```python add_A2AServiceServicer_to_server( servicer, server, ) -``` + ``` Your A2A server is now ready to run on SLIM. @@ -105,9 +103,10 @@ These are the main differences between the [client](https://github.com/agntcy/slim/blob/main/data-plane/python/integrations/slima2a/examples/travel_planner_agent/client.py) using SLIM A2A and the standard one. + 1. Create a channel. This requires a configuration that is similar to the server -```python + ```python def channel_factory(topic: str) -> srpc.Channel: channel = srpc.Channel( local="agntcy/demo/client", @@ -121,11 +120,11 @@ standard one. shared_secret="secret", ) return channel -``` + ``` 2. Add SLIM RPC in the supported transports. -```python + ```python client_config = ClientConfig( supported_transports=["JSONRPC", "srpc"], streaming=True, @@ -136,7 +135,7 @@ standard one. client_factory.register("srpc", SRPCTransport.create) agent_card = minimal_agent_card("agntcy/demo/travel_planner_agent", ["srpc"]) client = client_factory.create(card=agent_card) -``` + ``` From 03184ec6975f27824e72ba429d66f9b4b7558f2f Mon Sep 17 00:00:00 2001 From: Michele Papalini Date: Wed, 17 Sep 2025 14:44:50 +0200 Subject: [PATCH 11/16] docs(SLIM): integrations menu Signed-off-by: Michele Papalini --- docs/messaging/.index | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/messaging/.index b/docs/messaging/.index index 0bad91ac..6dec050d 100644 --- a/docs/messaging/.index +++ b/docs/messaging/.index @@ -5,7 +5,9 @@ nav: - SLIM Controller: slim-controller.md - SLIM Group Management: slim-group.md - SLIM Group Communication Tutorial: slim-group-tutorial.md - - SLIMRPC: slim-rpc.md - - SLIMRPC Protoc Plugin: slim-slimrpc-compiler.md - - SLIMA2A: slim-a2a.md - - SLIM and MCP Integration: slim-mcp.md + - SLIM Integrations: + - SLIMRPC: slim-rpc.md + - SLIMRPC Protoc Plugin: slim-slimrpc-compiler.md + - SLIMA2A: slim-a2a.md + - MCP over SLIM: slim-mcp.md + From 402e18c01d5d18a396e8b87964349864f275fa05 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Wed, 17 Sep 2025 14:59:20 +0200 Subject: [PATCH 12/16] doc(SLIM): index Signed-off-by: Mauro Sardara --- docs/messaging/.index | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/messaging/.index b/docs/messaging/.index index 6dec050d..2a55f22b 100644 --- a/docs/messaging/.index +++ b/docs/messaging/.index @@ -6,8 +6,9 @@ nav: - SLIM Group Management: slim-group.md - SLIM Group Communication Tutorial: slim-group-tutorial.md - SLIM Integrations: - - SLIMRPC: slim-rpc.md - - SLIMRPC Protoc Plugin: slim-slimrpc-compiler.md + - SLIMRPC: + - Overview: slim-rpc.md + - Protoc Plugin: slim-slimrpc-compiler.md - SLIMA2A: slim-a2a.md - MCP over SLIM: slim-mcp.md From b9ee74961c7abed9cf6a23d2d1e39dbadd6d0234 Mon Sep 17 00:00:00 2001 From: Mauro Sardara Date: Wed, 17 Sep 2025 14:59:37 +0200 Subject: [PATCH 13/16] doc(SLIM): index Signed-off-by: Mauro Sardara --- docs/messaging/.index | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/messaging/.index b/docs/messaging/.index index 2a55f22b..413f1d2c 100644 --- a/docs/messaging/.index +++ b/docs/messaging/.index @@ -6,7 +6,7 @@ nav: - SLIM Group Management: slim-group.md - SLIM Group Communication Tutorial: slim-group-tutorial.md - SLIM Integrations: - - SLIMRPC: + - SLIMRPC: - Overview: slim-rpc.md - Protoc Plugin: slim-slimrpc-compiler.md - SLIMA2A: slim-a2a.md From 99522ad1aa17a151e04fdbbb3ffcb229a6ca0ad5 Mon Sep 17 00:00:00 2001 From: Michele Papalini Date: Wed, 17 Sep 2025 17:43:56 +0200 Subject: [PATCH 14/16] docs (SLIM): fix list in a2a Signed-off-by: Michele Papalini --- docs/messaging/slim-a2a.md | 159 +++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 87 deletions(-) diff --git a/docs/messaging/slim-a2a.md b/docs/messaging/slim-a2a.md index 0c9c9d48..2b3e1beb 100644 --- a/docs/messaging/slim-a2a.md +++ b/docs/messaging/slim-a2a.md @@ -38,65 +38,53 @@ In this section we highlight the main differences between the SLIM A2A implementation with respect to the original implementation in the A2A repository. -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], - ) - - request_handler = DefaultRequestHandler( - agent_executor=TravelPlannerAgentExecutor(), - task_store=InMemoryTaskStore(), - ) - - servicer = SLIMRPCHandler(agent_card, request_handler) - ``` - -3. Setup the srcp.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", - ) - ``` - - - 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). - - For more information about these settings, see the [SLIMRCP](./slim-rpc.md). +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], + ) + request_handler = DefaultRequestHandler( + agent_executor=TravelPlannerAgentExecutor(), + task_store=InMemoryTaskStore(), + ) + servicer = SLIMRPCHandler(agent_card, request_handler) + ``` +3. Setup the srcp.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", + ) + ``` + - 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). + For more information about these settings, see the [SLIMRCP](./slim-rpc.md). 4. Register the Service - - ```python - add_A2AServiceServicer_to_server( - servicer, - server, - ) - ``` + ```python + add_A2AServiceServicer_to_server( + servicer, + server, + ) + ``` Your A2A server is now ready to run on SLIM. @@ -107,37 +95,34 @@ These are the main differences between the using SLIM A2A and the standard one. 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 - ``` - + ```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 - client_config = ClientConfig( - supported_transports=["JSONRPC", "slimrpc"], - streaming=True, - httpx_client=httpx_client, - slimrpc_channel_factory=channel_factory, - ) - client_factory = ClientFactory(client_config) - client_factory.register("slimrpc", SLIMRPCTransport.create) - agent_card = minimal_agent_card("agntcy/demo/travel_planner_agent", ["slimrpc"]) - client = client_factory.create(card=agent_card) - ``` + ```python + client_config = ClientConfig( + supported_transports=["JSONRPC", "slimrpc"], + streaming=True, + httpx_client=httpx_client, + slimrpc_channel_factory=channel_factory, + ) + client_factory = ClientFactory(client_config) + client_factory.register("slimrpc", SLIMRPCTransport.create) + agent_card = minimal_agent_card("agntcy/demo/travel_planner_agent", ["slimrpc"]) + client = client_factory.create(card=agent_card) + ``` + ``` \ No newline at end of file