Skip to content

Commit f03f8be

Browse files
authored
docs: hello_world python binding example (#2083)
1 parent a2874fd commit f03f8be

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<!--
2+
SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
SPDX-License-Identifier: Apache-2.0
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
18+
# Hello World Example
19+
20+
This is the simplest Dynamo example demonstrating a basic service using Dynamo's distributed runtime. It showcases the fundamental concepts of creating endpoints and workers in the Dynamo runtime system.
21+
22+
## Architecture
23+
24+
```text
25+
Client (dynamo_worker)
26+
27+
28+
┌─────────────┐
29+
│ Backend │ Dynamo endpoint (/generate)
30+
└─────────────┘
31+
```
32+
33+
## Components
34+
35+
- **Backend**: A Dynamo service with an endpoint that receives text input and streams back greetings for each comma-separated word
36+
- **Client**: A Dynamo worker that connects to and sends requests to the backend service, then prints out the response
37+
38+
## Implementation Details
39+
40+
The example demonstrates:
41+
42+
- **Endpoint Definition**: Using the `@dynamo_endpoint` decorator to create streaming endpoints
43+
- **Worker Setup**: Using the `@dynamo_worker()` decorator to create distributed runtime workers
44+
- **Service Creation**: Creating services and endpoints using the distributed runtime API
45+
- **Streaming Responses**: Yielding data for real-time streaming
46+
- **Client Integration**: Connecting to services and processing streams
47+
- **Logging**: Basic logging configuration with `configure_dynamo_logging`
48+
49+
## Getting Started
50+
51+
## Prerequisites
52+
53+
Before running this example, ensure you have the following services running:
54+
55+
- **etcd**: A distributed key-value store used for service discovery and metadata storage
56+
- **NATS**: A high-performance message broker for inter-component communication
57+
58+
You can start these services using Docker Compose:
59+
60+
```bash
61+
# clone the dynamo repository if necessary
62+
# git clone https://github.com/ai-dynamo/dynamo.git
63+
cd dynamo
64+
docker compose -f deploy/metrics/docker-compose.yml up -d
65+
```
66+
67+
### Running the Example
68+
69+
First, start the backend service:
70+
```bash
71+
cd examples/runtime/hello_world
72+
python hello_world.py
73+
```
74+
75+
Second, in a separate terminal, run the client:
76+
```bash
77+
cd examples/runtime/hello_world
78+
python client.py
79+
```
80+
81+
The client will connect to the backend service and print the streaming results.
82+
83+
### Expected Output
84+
85+
When running the client, you should see streaming output like:
86+
```text
87+
Hello world!
88+
Hello sun!
89+
Hello moon!
90+
Hello star!
91+
```
92+
93+
## Code Structure
94+
95+
### Backend Service (`hello_world.py`)
96+
97+
- **`content_generator`**: A dynamo endpoint that processes text input and yields greetings
98+
- **`worker`**: A dynamo worker that sets up the service, creates the endpoint, and serves it
99+
100+
### Client (`client.py`)
101+
102+
- **`worker`**: A dynamo worker that connects to the backend service and processes the streaming response
103+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import asyncio
17+
18+
import uvloop
19+
20+
from dynamo.runtime import DistributedRuntime, dynamo_worker
21+
22+
23+
@dynamo_worker()
24+
async def worker(runtime: DistributedRuntime):
25+
# Get endpoint
26+
endpoint = (
27+
runtime.namespace("hello_world").component("backend").endpoint("generate")
28+
)
29+
30+
# Create client and wait for service to be ready
31+
client = await endpoint.client()
32+
await client.wait_for_instances()
33+
34+
# Issue request and process the stream
35+
stream = await client.generate("world,sun,moon,star")
36+
async for response in stream:
37+
print(response.data())
38+
39+
40+
if __name__ == "__main__":
41+
uvloop.install()
42+
asyncio.run(worker())
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import asyncio
17+
import logging
18+
19+
import uvloop
20+
21+
from dynamo.runtime import DistributedRuntime, dynamo_endpoint, dynamo_worker
22+
from dynamo.runtime.logging import configure_dynamo_logging
23+
24+
logger = logging.getLogger(__name__)
25+
configure_dynamo_logging(service_name="backend")
26+
27+
28+
@dynamo_endpoint(str, str)
29+
async def content_generator(request: str):
30+
logger.info(f"Received request: {request}")
31+
for word in request.split(","):
32+
await asyncio.sleep(1)
33+
yield f"Hello {word}!"
34+
35+
36+
@dynamo_worker()
37+
async def worker(runtime: DistributedRuntime):
38+
namespace_name = "hello_world"
39+
component_name = "backend"
40+
endpoint_name = "generate"
41+
lease_id = runtime.etcd_client().primary_lease_id()
42+
43+
component = runtime.namespace(namespace_name).component(component_name)
44+
await component.create_service()
45+
46+
logger.info(f"Created service {namespace_name}/{component_name}")
47+
48+
endpoint = component.endpoint(endpoint_name)
49+
50+
logger.info(f"Serving endpoint {endpoint_name} on lease {lease_id}")
51+
await endpoint.serve_endpoint(content_generator)
52+
53+
54+
if __name__ == "__main__":
55+
uvloop.install()
56+
asyncio.run(worker())

0 commit comments

Comments
 (0)