Skip to content

Commit

Permalink
Add docs on custom chains
Browse files Browse the repository at this point in the history
  • Loading branch information
rogeriochaves committed Jul 25, 2023
1 parent f95a0aa commit 47f6848
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 3 deletions.
76 changes: 76 additions & 0 deletions docs/docs/chain-basics/custom_chains.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
sidebar_position: 7
---

# Custom Chains

If you have been following the guides, now you know how to create chains, how to compose them together, and everything, however, what if you want to change the core behaviour of the chain, how do you do it? Well, turns out, **there are no "custom chains" really**, it's all just composition.

For example, let's say you want a chain that retries on error, using the [`@retry`](https://pypi.org/project/retry/) library annotation, you can simply create a function that wraps the chain to be retried:

```python
from litechain import Chain
from retry import retry
from typing import TypeVar

T = TypeVar("T")
U = TypeVar("U")

def retriable(chain: Chain[T, U]) -> Chain[T, U]:
@retry(tries=3)
def call_wrapped_chain(input: T):
return chain(input)

return Chain[T, U]("RetriableChain", call_wrapped_chain)
```

And use it like this:

```python
from litechain import collect_final_output

attempts = 0

def division_by_attempts(input: int):
global attempts
attempts += 1
return input / (attempts - 1)

chain = retriable(
Chain[int, float]("BrokenChain", division_by_attempts)
).map(lambda x: x + 1)

await collect_final_output(chain(25))
#=> [26]
```

This chain will first divide by zero, causing a `ZeroDivisionError`, but thanks to our little `retriable` wrapper, it will try again an succeed next time, returning `26`.

So that's it, because chains are just input and output, a simple function will do, if you want to write a class to fit more the type system and be more pythonic, you also can, and the only method you need to override is `__init__`:

```python
class RetriableChain(Chain[T, U]):
def __init__(self, chain: Chain[T, U], tries=3):
@retry(tries=tries)
def call_wrapped_chain(input: T):
return chain(input)

super().__init__("RetriableChain", call_wrapped_chain)
```

This will work exactly the same as the function:

```python
chain = RetriableChain(
Chain[int, float]("BrokenChain", division_by_attempts)
).map(lambda x: x + 1)

await collect_final_output(chain(25))
#=> [26]
```

As a proof that this is enough, take a look at [how the OpenAICompletionChain is implemented](https://github.com/rogeriochaves/litechain/blob/main/litechain/contrib/llms/open_ai.py#L26), it's a simple wrapper of OpenAI's API under `__init__` and that's it.

## Next Steps

This concludes the guides for Chain Basics, congratulations! On the next steps, we are going to build some real application with real LLMs, stay tuned!
4 changes: 1 addition & 3 deletions docs/docs/chain-basics/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,4 @@ You can then keep composing chains after the `on_error`, using methods like `map

For a more complete example, check out the [Weather Bot with Error Handling](../examples/weather-bot-error-handling) example.

## Next Steps

This concludes the guides for Chain Basics, congratulations! On the next steps, we are going to build some real application with real LLMs, stay tuned!
Now go to the next step to figure out how to build your own custom chains!

0 comments on commit 47f6848

Please sign in to comment.