From 9172ea8e13ab506d6781308f1d1e618f32895e3a Mon Sep 17 00:00:00 2001 From: Diptanu Gon Choudhury Date: Wed, 2 Oct 2024 22:34:09 -0700 Subject: [PATCH] update readme --- README.md | 43 ++++++++++--------- examples/readme/readme_example.py | 50 ++++++++++------------- python-sdk/indexify/remote_graph.py | 2 +- python-sdk/tests/test_graph_behaviours.py | 10 ++--- 4 files changed, 48 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 59b83ac1d..8194e37f4 100644 --- a/README.md +++ b/README.md @@ -42,29 +42,18 @@ class Total(BaseModel): def generate_numbers(a: int) -> List[int]: return [i for i in range(a)] -@indexify_function(accumulate=Total) -def add(total: Total, new: int) -> Total: - total.val += new - return total - @indexify_function() def square(total: Total) -> int: return total.val ** 2 -@indexify_function() -def cube(total: Total) -> int: - return total.val ** 3 - -@indexify_router() -def dynamic_router(val: Total) -> List[Union[square, cube]]: - if val.val % 2: - return [square] - return [cube] +@indexify_function(accumulate=Total) +def add(total: Total, new: int) -> Total: + total.val += new + return total g = Graph(name="sequence_summer", start_node=generate_numbers, description="Simple Sequence Summer") -g.add_edge(generate_numbers, add) -g.add_edge(add, dynamic_router) -g.route(dynamic_router, [square, cube]) +g.add_edge(generate_numbers, square) +g.add_edge(square, add) ``` You can separate heavy tasks like local inference of LLMs from database write operations to prevent reprocessing data if a write fails. Indexify caches each function's output, so when you retry downstream processes, previous steps aren't repeated. @@ -72,7 +61,7 @@ You can separate heavy tasks like local inference of LLMs from database write op #### 2: Test the Graph In-Process ```python invocation_id = g.run(a=10) -result = g.get_outputs(invocation_id, "squared") +result = g.get_output(invocation_id, "add") print(result) ``` @@ -99,16 +88,26 @@ Change the code above to deploy the graph as an API - ```python from indexify import RemoteGraph -graph = RemoteGraph.new(g) +graph = RemoteGraph.deploy(g) invocation_id = graph.run(block_until_done=True, a=10) -result = graph.get_outputs(invocation_id, "squared") +result = graph.get_output(invocation_id, "add") print(result) ``` This serializes your Graph code and uploads it to the server, and instantiates a new endpoint. Everything else, remains the same in your application code that invokes the Graph to process data and retrieve outputs! -#### 4: Deploying Graph Endpoints using Docker Compose +#### 4: Call Remote Graphs from Applications + +You can call these remote graphs from any application. Think about them like an extension of your application. + +```python +graph = RemoteGraph.by_name("sequence_summer") +invocation_id = graph.run(block_until_done=True, a=5) +print(graph.get_output(invocation_id, "add")) +``` + +#### 5: Deploying Graph Endpoints using Docker Compose You can spin up the server and executor using docker compose, and deploy and run in a production-like environment. Copy the [docker-compose.yaml file from here](https://raw.githubusercontent.com/tensorlakeai/indexify/refs/heads/main/docker-compose.yaml). ```bash @@ -119,7 +118,7 @@ This starts the server and two replicas of the exeuctor in separate containers. This uses a default executor container based on Debian and a vanilla Python installation. -#### 5: Building Executor Images with Custom Dependencies +#### 6: Building Executor Images with Custom Dependencies You can build custom images for functions that require additional Python or System dependencies. diff --git a/examples/readme/readme_example.py b/examples/readme/readme_example.py index bd5698f1e..5bdb885df 100644 --- a/examples/readme/readme_example.py +++ b/examples/readme/readme_example.py @@ -1,6 +1,6 @@ from pydantic import BaseModel -from indexify import indexify_function, Graph, indexify_router -from typing import List, Union +from indexify import indexify_function, indexify_router, Graph +from typing import List class Total(BaseModel): val: int = 0 @@ -9,38 +9,30 @@ class Total(BaseModel): def generate_numbers(a: int) -> List[int]: return [i for i in range(a)] +@indexify_function() +def square(x: int) -> int: + return x ** 2 + @indexify_function(accumulate=Total) def add(total: Total, new: int) -> Total: total.val += new return total -@indexify_function() -def square(total: Total) -> int: - return total.val ** 2 - -@indexify_function() -def cube(total: Total) -> int: - return total.val ** 3 - -@indexify_router() -def dynamic_router(val: Total) -> List[Union[square, cube]]: - if val.val % 2: - return [square] - return [cube] +g = Graph(name="sequence_summer", start_node=generate_numbers, description="Simple Sequence Summer") +g.add_edge(generate_numbers, square) +g.add_edge(square, add) -if __name__ == '__main__': - g = Graph(name="sequence_summer", start_node=generate_numbers, description="Simple Sequence Summer") - g.add_edge(generate_numbers, add) - g.add_edge(add, dynamic_router) - g.route(dynamic_router, [square, cube]) - - from indexify import create_client - - client = create_client(in_process=True) - client.register_compute_graph(g) - - invocation_id = client.invoke_graph_with_object("sequence_summer", block_until_done=True, a=4) - result = client.graph_outputs("sequence_summer", invocation_id, "squared") +if __name__ == "__main__": + invocation_id = g.run(a=10) + result = g.get_output(invocation_id, "add") print(result) - result = client.graph_outputs("sequence_summer", invocation_id, "tripled") + + from indexify import RemoteGraph + graph = RemoteGraph.deploy(g) + invocation_id = graph.run(block_until_done=True, a=10) + result = graph.get_output(invocation_id, "add") print(result) + + graph = RemoteGraph.by_name("sequence_summer") + invocation_id = graph.run(block_until_done=True, a=5) + print(graph.get_output(invocation_id, "add")) \ No newline at end of file diff --git a/python-sdk/indexify/remote_graph.py b/python-sdk/indexify/remote_graph.py index 596102b38..fb3588060 100644 --- a/python-sdk/indexify/remote_graph.py +++ b/python-sdk/indexify/remote_graph.py @@ -34,7 +34,7 @@ def foo(x: int) -> int: ) @classmethod - def new(cls, g: Graph, server_url: Optional[str] = "http://localhost:8090"): + def deploy(cls, g: Graph, server_url: Optional[str] = "http://localhost:8090"): """ Create a new RemoteGraph from a local Graph object. :param g: The local Graph object. diff --git a/python-sdk/tests/test_graph_behaviours.py b/python-sdk/tests/test_graph_behaviours.py index b039747d1..eea85a11e 100644 --- a/python-sdk/tests/test_graph_behaviours.py +++ b/python-sdk/tests/test_graph_behaviours.py @@ -101,14 +101,14 @@ def test_simple_function(self): graph = Graph( name="test_simple_function", description="test", start_node=simple_function ) - graph = RemoteGraph.new(graph) + graph = RemoteGraph.deploy(graph) invocation_id = graph.run(block_until_done=True, x=MyObject(x="a")) output = graph.get_output(invocation_id, "simple_function") self.assertEqual(output, [MyObject(x="ab")]) def test_map_operation(self): graph = create_pipeline_graph_with_map() - graph = RemoteGraph.new(graph) + graph = RemoteGraph.deploy(graph) invocation_id = graph.run(block_until_done=True, x=3) output_seq = graph.get_output(invocation_id, "generate_seq") self.assertEqual(sorted(output_seq), [0, 1, 2]) @@ -117,7 +117,7 @@ def test_map_operation(self): def test_map_reduce_operation(self): graph = create_pipeline_graph_with_map_reduce() - graph = RemoteGraph.new(graph) + graph = RemoteGraph.deploy(graph) invocation_id = graph.run(block_until_done=True, x=3) output_sum_sq = graph.get_output(invocation_id, "sum_of_squares") @@ -126,7 +126,7 @@ def test_map_reduce_operation(self): self.assertEqual(output_str, ["5"]) def test_router_graph_behavior(self): - graph = RemoteGraph.new(create_router_graph()) + graph = RemoteGraph.deploy(create_router_graph()) invocation_id = graph.run(block_until_done=True, x=3) output_add_two = graph.get_output(invocation_id, "add_two") @@ -145,7 +145,7 @@ def test_invoke_file(self): graph = Graph( name="test_handle_file", description="test", start_node=handle_file ) - graph = RemoteGraph.new(graph) + graph = RemoteGraph.deploy(graph) import os data = Path(os.path.dirname(__file__) + "/test_file").read_text()