Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LangServe #11046

Merged
merged 19 commits into from
Sep 28, 2023
Merged
82 changes: 82 additions & 0 deletions .github/workflows/langserve_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
name: libs/langserve CI

on:
push:
branches: [ master ]
pull_request:
paths:
- '.github/actions/poetry_setup/action.yml'
- '.github/tools/**'
- '.github/workflows/_lint.yml'
- '.github/workflows/_test.yml'
- '.github/workflows/langserve_ci.yml'
- 'libs/langserve/**'
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI

# If another push to the same PR or branch happens while this workflow is still running,
# cancel the earlier run in favor of the next run.
#
# There's no point in testing an outdated version of the code. GitHub only allows
# a limited number of job runners to be active at the same time, so it's better to cancel
# pointless jobs early so that more useful jobs can run sooner.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
POETRY_VERSION: "1.5.1"
WORKDIR: "libs/langserve"

jobs:
lint:
uses:
./.github/workflows/_lint.yml
with:
working-directory: libs/langserve
secrets: inherit

test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ env.WORKDIR }}
strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
name: Python ${{ matrix.python-version }} extended tests
steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
uses: "./.github/actions/poetry_setup"
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
working-directory: libs/langserve
cache-key: langserve-all

- name: Install dependencies
shell: bash
run: |
echo "Running extended tests, installing dependencies with poetry..."
poetry install --with test,lint --extras all

- name: Run tests
run: make test

- name: Ensure the tests did not create any additional files
shell: bash
run: |
set -eu

STATUS="$(git status)"
echo "$STATUS"

# grep will exit non-zero if the target message isn't found,
# and `set -e` above will cause the step to fail.
echo "$STATUS" | grep 'nothing to commit, working tree clean'
21 changes: 21 additions & 0 deletions libs/langserve/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 LangChain

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
54 changes: 54 additions & 0 deletions libs/langserve/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.PHONY: all lint format test help

# Default target executed when no arguments are given to make.
all: help

######################
# TESTING AND COVERAGE
######################

# Define a variable for the test file path.
TEST_FILE ?= tests/unit_tests/

test:
poetry run pytest --disable-socket --allow-unix-socket $(TEST_FILE)

######################
# LINTING AND FORMATTING
######################

# Define a variable for Python and notebook files.
PYTHON_FILES=.
lint format: PYTHON_FILES=.
lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/langserve --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$')

lint lint_diff:
poetry run ruff .
poetry run black $(PYTHON_FILES) --check

format format_diff:
poetry run black $(PYTHON_FILES)
poetry run ruff --select I --fix $(PYTHON_FILES)

spell_check:
poetry run codespell --toml pyproject.toml

spell_fix:
poetry run codespell --toml pyproject.toml -w

######################
# HELP
######################

help:
@echo '===================='
@echo '-- LINTING --'
@echo 'format - run code formatters'
@echo 'lint - run linters'
@echo 'spell_check - run codespell on the project'
@echo 'spell_fix - run codespell on the project and fix the errors'
@echo '-- TESTS --'
@echo 'coverage - run unit tests and generate coverage report'
@echo 'test - run unit tests'
@echo 'test TEST_FILE=<test_file> - run all tests in file'
@echo '-- DOCUMENTATION tasks are from the top-level Makefile --'
124 changes: 124 additions & 0 deletions libs/langserve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# LangServe 🦜️🔗

## Overview

`LangServe` is a library that allows developers to host their Langchain runnables /
call into them remotely from a runnable interface.

## Examples

For more examples, see the [examples](./examples) directory.

### Server

```python
#!/usr/bin/env python
from fastapi import FastAPI
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatAnthropic, ChatOpenAI
from langserve import add_routes
from typing_extensions import TypedDict


app = FastAPI(
title="LangChain Server",
version="1.0",
description="A simple api server using Langchain's Runnable interfaces",
)


# Serve Open AI and Anthropic models
LLMInput = Union[List[Union[SystemMessage, HumanMessage, str]], str]

add_routes(
app,
ChatOpenAI(),
path="/openai",
input_type=LLMInput,
config_keys=[],
)
add_routes(
app,
ChatAnthropic(),
path="/anthropic",
input_type=LLMInput,
config_keys=[],
)

# Serve a joke chain
class ChainInput(TypedDict):
"""The input to the chain."""

topic: str
"""The topic of the joke."""

model = ChatAnthropic()
prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
add_routes(app, prompt | model, path="/chain", input_type=ChainInput)

if __name__ == "__main__":
import uvicorn

uvicorn.run(app, host="localhost", port=8000)
```


### Client

```python

from langchain.schema import SystemMessage, HumanMessage
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnableMap
from langserve import RemoteRunnable

openai = RemoteRunnable("http://localhost:8000/openai/")
anthropic = RemoteRunnable("http://localhost:8000/anthropic/")
joke_chain = RemoteRunnable("http://localhost:8000/chain/")

joke_chain.invoke({"topic": "parrots"})

# or async
await joke_chain.ainvoke({"topic": "parrots"})

prompt = [
SystemMessage(content='Act like either a cat or a parrot.'),
HumanMessage(content='Hello!')
]

# Supports astream
async for msg in anthropic.astream(prompt):
print(msg, end="", flush=True)

prompt = ChatPromptTemplate.from_messages(
[("system", "Tell me a long story about {topic}")]
)

# Can define custom chains
chain = prompt | RunnableMap({
"openai": openai,
"anthropic": anthropic,
})

chain.batch([{ "topic": "parrots" }, { "topic": "cats" }])
```

## Installation

```bash
# pip install langserve[all] -- has not been published to pypi yet
```

or use `client` extra for client code, and `server` extra for server code.

## Features

- Deploy runnables with FastAPI
- Client can use remote runnables almost as if they were local
- Supports async
- Supports batch
- Supports stream

### Limitations

- Chain callbacks cannot be passed from the client to the server
4 changes: 4 additions & 0 deletions libs/langserve/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .client import RemoteRunnable
from .server import add_routes

__all__ = ["RemoteRunnable", "add_routes"]
Loading