def test_http_we_provide_default_route_prefix_func(serve_instance): """Ensure the default ingress deployment route is '/' instead of driver function name """ func_dag = combine.bind(1, 2) deployments = pipeline_build(func_dag) ingress_deployment = get_and_validate_ingress_deployment(deployments) assert ingress_deployment.route_prefix == "/"
def test_http_reconfigure_non_default_route_prefix_on_root(serve_instance): with InputNode() as dag_input: m1 = Model.bind(1) m2 = Model.bind(2) m1_output = m1.forward.bind(dag_input[0]) m2_output = m2.forward.bind(dag_input[1]) combine_output = combine.bind(m1_output, m2_output) serve_dag = Driver.bind(combine_output) deployments = pipeline_build(serve_dag) non_root_deployment = deployments[-1].options(route_prefix="/yoo") deployments[-1] = non_root_deployment _ = get_and_validate_ingress_deployment(deployments)
def test_http_user_bring_own_driver_route_prefix(serve_instance): with InputNode() as dag_input: m1 = Model.bind(1) m2 = Model.bind(2) m1_output = m1.forward.bind(dag_input[0]) m2_output = m2.forward.bind(dag_input[0]) combine_output = combine.bind(m1_output, m2_output) serve_dag = Driver.options(route_prefix="/hello").bind(combine_output) deployments = pipeline_build(serve_dag) ingress_deployment = get_and_validate_ingress_deployment(deployments) assert ingress_deployment.route_prefix == "/hello" for deployment in deployments[:-1]: assert deployment.route_prefix is None
def test_http_we_provide_default_route_prefix_cls(serve_instance): """Ensure the default ingress deployment route is '/' instead of driver class name """ with InputNode() as dag_input: m1 = Model.bind(1) m2 = Model.bind(1) m1_output = m1.forward.bind(dag_input[0]) m2_output = m2.forward.bind(dag_input[0]) combine_output = combine.bind(m1_output, m2_output) serve_dag = Driver.bind(combine_output) deployments = pipeline_build(serve_dag) ingress_deployment = get_and_validate_ingress_deployment(deployments) assert ingress_deployment.route_prefix == "/" for deployment in deployments[:-1]: assert deployment.route_prefix is None
def test_http_only_one_ingress_deployment(serve_instance): with InputNode() as dag_input: m1 = Model.bind(1) m2 = Model.bind(1) m1_output = m1.forward.bind(dag_input[0]) m2_output = m2.forward.bind(dag_input[0]) combine_output = combine.bind(m1_output, m2_output) serve_dag = Driver.bind(combine_output) deployments = pipeline_build(serve_dag) non_root_deployment = deployments[0].options(route_prefix="/") deployments[0] = non_root_deployment with pytest.raises( ValueError, match=( "Only one deployment in an Serve Application or DAG can have " "non-None route prefix" ), ): _ = get_and_validate_ingress_deployment(deployments)
def run( target: Union[ClassNode, FunctionNode], _blocking: bool = True, *, host: str = DEFAULT_HTTP_HOST, port: int = DEFAULT_HTTP_PORT, ) -> Optional[RayServeHandle]: """Run a Serve application and return a ServeHandle to the ingress. Either a ClassNode, FunctionNode, or a pre-built application can be passed in. If a node is passed in, all of the deployments it depends on will be deployed. If there is an ingress, its handle will be returned. Args: target (Union[ClassNode, FunctionNode, Application]): A user-built Serve Application or a ClassNode that acts as the root node of DAG. By default ClassNode is the Driver deployment unless user provides a customized one. host (str): The host passed into serve.start(). port (int): The port passed into serve.start(). Returns: RayServeHandle: A regular ray serve handle that can be called by user to execute the serve DAG. """ client = start(detached=True, http_options={"host": host, "port": port}) if isinstance(target, Application): deployments = list(target.deployments.values()) ingress = target.ingress # Each DAG should always provide a valid Driver ClassNode elif isinstance(target, ClassNode): deployments = pipeline_build(target) ingress = get_and_validate_ingress_deployment(deployments) # Special case where user is doing single function serve.run(func.bind()) elif isinstance(target, FunctionNode): deployments = pipeline_build(target) ingress = get_and_validate_ingress_deployment(deployments) if len(deployments) != 1: raise ValueError( "We only support single function node in serve.run, ex: " "serve.run(func.bind()). For more than one nodes in your DAG, " "Please provide a driver class and bind it as entrypoint to " "your Serve DAG." ) elif isinstance(target, DAGNode): raise ValueError( "Invalid DAGNode type as entry to serve.run(), " f"type: {type(target)}, accepted: ClassNode, " "FunctionNode please provide a driver class and bind it " "as entrypoint to your Serve DAG." ) else: raise TypeError( "Expected a ClassNode, FunctionNode, or Application as target. " f"Got unexpected type {type(target)} instead." ) parameter_group = [] for deployment in deployments: deployment_parameters = { "name": deployment._name, "func_or_class": deployment._func_or_class, "init_args": deployment.init_args, "init_kwargs": deployment.init_kwargs, "ray_actor_options": deployment._ray_actor_options, "config": deployment._config, "version": deployment._version, "prev_version": deployment._prev_version, "route_prefix": deployment.route_prefix, "url": deployment.url, } parameter_group.append(deployment_parameters) client.deploy_group( parameter_group, _blocking=_blocking, remove_past_deployments=True ) if ingress is not None: return ingress.get_handle()