Files
2026-07-13 13:17:40 +08:00

99 lines
2.8 KiB
Python

import argparse
import asyncio
import logging
import requests
from fastapi import FastAPI
from starlette.responses import StreamingResponse
import ray
from ray import serve
logger = logging.getLogger("ray.serve")
fastapi_app = FastAPI()
# Input: a prompt of words
# Output: each word reversed and produced N times.
@serve.deployment(
num_replicas=6, ray_actor_options={"num_cpus": 0.01, "memory": 10 * 1024 * 1024}
)
class ReverseAndDupEachWord:
def __init__(self, dup_times: int):
self.dup_times = dup_times
async def __call__(self, prompt: str):
for word in prompt.split():
rev = word[::-1]
for _ in range(self.dup_times):
await asyncio.sleep(0.001)
# Ideally we want to do " ".join(words), but for the sake of
# simplicity we also have an extra trailing space.
yield rev + " "
@serve.deployment(
num_replicas=6, ray_actor_options={"num_cpus": 0.01, "memory": 10 * 1024 * 1024}
)
@serve.ingress(fastapi_app)
class Textbot:
def __init__(self, llm):
self.llm = llm.options(stream=True)
@fastapi_app.post("/")
async def handle_request(self, prompt: str) -> StreamingResponse:
logger.info(f'Got prompt with size "{len(prompt)}"')
return StreamingResponse(self.llm.remote(prompt), media_type="text/plain")
@ray.remote(num_cpus=0.1, memory=10 * 1024 * 1024)
def make_http_query(num_words, num_queries):
for _ in range(num_queries):
words = "Lorem ipsum dolor sit amet".split()
prompt_words = [words[i % len(words)] for i in range(num_words)]
prompt = " ".join(prompt_words)
expected_words = [word[::-1] for word in prompt_words for _ in range(2)]
response = requests.post(f"http://localhost:8000/?prompt={prompt}", stream=True)
response.raise_for_status()
content = response.content.decode()
assert content == " ".join(expected_words) + " ", content
def main():
parser = argparse.ArgumentParser(description="Generates HTTP workloads with Ray.")
parser.add_argument("--num_tasks", type=int, required=True, help="Number of tasks.")
parser.add_argument(
"--num_queries_per_task",
type=int,
required=True,
help="Number of queries per task.",
)
parser.add_argument(
"--num_words_per_query",
type=int,
required=True,
help="Number of words per query",
)
args = parser.parse_args()
# Run the serve, run the client, then showdown serve.
llm = ReverseAndDupEachWord.bind(2)
app = Textbot.bind(llm)
serve.run(app)
objs = [
make_http_query.remote(args.num_words_per_query, args.num_queries_per_task)
for _ in range(args.num_tasks)
]
ray.get(objs)
serve.shutdown()
main()