Skip to content

Commit

Permalink
Add in code documentation (#25)
Browse files Browse the repository at this point in the history
Add some in code documentation
  • Loading branch information
eyurtsev authored Oct 11, 2023
1 parent 90ec2e1 commit 6ec7cea
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 15 deletions.
94 changes: 88 additions & 6 deletions langserve/server.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
"""FastAPI integration for langchain runnables.
This code contains integration for langchain runnables with FastAPI.
The main entry point is the `add_routes` function which adds the routes to an existing
FastAPI app or APIRouter.
"""
from inspect import isclass
from typing import (
Any,
Expand Down Expand Up @@ -66,7 +73,7 @@ def _unpack_input(validated_model: BaseModel) -> Any:
_MODEL_REGISTRY = {}


def _resolve_model(type_: Union[Type, BaseModel], default_name: str) -> BaseModel:
def _resolve_model(type_: Union[Type, BaseModel], default_name: str) -> Type[BaseModel]:
"""Resolve the input type to a BaseModel."""
if isclass(type_) and issubclass(type_, BaseModel):
model = type_
Expand All @@ -82,7 +89,10 @@ def _resolve_model(type_: Union[Type, BaseModel], default_name: str) -> BaseMode


def _add_namespace_to_model(namespace: str, model: Type[BaseModel]) -> Type[BaseModel]:
"""Create a unique name for the given model.
"""Prefix the name of the given model with the given namespace.
Code is used to help avoid name collisions when hosting multiple runnables
that may use the same underlying models.
Args:
namespace: The namespace to use for the model.
Expand Down Expand Up @@ -128,6 +138,17 @@ def add_routes(
) -> None:
"""Register the routes on the given FastAPI app or APIRouter.
The following routes are added per runnable under the specified `path`:
* /invoke - for invoking a runnable with a single input
* /batch - for invoking a runnable with multiple inputs
* /stream - for streaming the output of a runnable
* /stream_log - for streaming intermediate outputs for a runnable
* /input_schema - for returning the input schema of the runnable
* /output_schema - for returning the output schema of the runnable
* /config_schema - for returning the config schema of the runnable
Args:
app: The FastAPI app or APIRouter to which routes should be added.
runnable: The runnable to wrap, must not be stateful.
Expand Down Expand Up @@ -209,7 +230,37 @@ async def batch(request: Annotated[BatchRequest, BatchRequest]) -> BatchResponse
async def stream(
request: Annotated[StreamRequest, StreamRequest],
) -> EventSourceResponse:
"""Invoke the runnable stream the output."""
"""Invoke the runnable stream the output.
This endpoint allows to stream the output of the runnable.
The endpoint uses a server sent event stream to stream the output.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
Important: Set the "text/event-stream" media type for request headers if
not using an existing SDK.
This endpoint uses two different types of events:
* data - for streaming the output of the runnable
{
"event": "data",
"data": {
...
}
}
* end - for signaling the end of the stream.
This helps the client to know when to stop listening for events and
know that the streaming has ended successfully.
{
"event": "end",
}
"""
# Request is first validated using InvokeRequest which takes into account
# config_keys as well as input_type.
# After validation, the input is loaded using LangChain's load function.
Expand All @@ -232,7 +283,38 @@ async def _stream() -> AsyncIterator[dict]:
async def stream_log(
request: Annotated[StreamLogRequest, StreamLogRequest],
) -> EventSourceResponse:
"""Invoke the runnable stream the output."""
"""Invoke the runnable stream_log the output.
This endpoint allows to stream the output of the runnable, including
the output of all intermediate steps.
The endpoint uses a server sent event stream to stream the output.
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
Important: Set the "text/event-stream" media type for request headers if
not using an existing SDK.
This endpoint uses two different types of events:
* data - for streaming the output of the runnable
{
"event": "data",
"data": {
...
}
}
* end - for signaling the end of the stream.
This helps the client to know when to stop listening for events and
know that the streaming has ended successfully.
{
"event": "end",
}
"""
# Request is first validated using InvokeRequest which takes into account
# config_keys as well as input_type.
# After validation, the input is loaded using LangChain's load function.
Expand Down Expand Up @@ -288,10 +370,10 @@ async def input_schema() -> Any:

@app.get(f"{namespace}/output_schema")
async def output_schema() -> Any:
"""Return the input schema of the runnable."""
"""Return the output schema of the runnable."""
return runnable.output_schema.schema()

@app.get(f"{namespace}/config_schema")
async def config_schema() -> Any:
"""Return the input schema of the runnable."""
"""Return the config schema of the runnable."""
return runnable.config_schema(include=config_keys).schema()
41 changes: 32 additions & 9 deletions langserve/validation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
"""Code to dynamically create pydantic models for validating requests and responses.
Requests share the same basic shape of input, config, and kwargs.
Invoke and Batch responses use an `output` key for the output, other keys may
be added to the response at a later date.
Responses for stream and stream_log are specified as those endpoints use
a streaming response.
Type information for input, config and output can be specified by the user
per runnable. This type information will be used for validation of the input and
output and will appear in the OpenAPI spec for the corresponding endpoint.
Models are created with a namespace to avoid name collisions when hosting
multiple runnables. When present the name collisions prevent fastapi from
generating OpenAPI specs.
"""
from typing import List, Optional, Sequence, Union

try:
Expand All @@ -7,15 +25,16 @@

from typing_extensions import Type, TypedDict

InputValidator = Union[Type[BaseModel], type]
# The following langchain objects are considered to be safe to load.
# Type that is either a python annotation or a pydantic model that can be
# used to validate the input or output of a runnable.
Validator = Union[Type[BaseModel], type]

# PUBLIC API


def create_invoke_request_model(
namespace: str,
input_type: InputValidator,
input_type: Validator,
config: TypedDict,
) -> Type[BaseModel]:
"""Create a pydantic model for the invoke request."""
Expand All @@ -31,10 +50,10 @@ def create_invoke_request_model(

def create_stream_request_model(
namespace: str,
input_type: InputValidator,
input_type: Validator,
config: TypedDict,
) -> Type[BaseModel]:
"""Create a pydantic model for the invoke request."""
"""Create a pydantic model for the stream request."""
stream_request_model = create_model(
f"{namespace}StreamRequest",
input=(input_type, ...),
Expand All @@ -47,7 +66,7 @@ def create_stream_request_model(

def create_batch_request_model(
namespace: str,
input_type: InputValidator,
input_type: Validator,
config: TypedDict,
) -> Type[BaseModel]:
"""Create a pydantic model for the batch request."""
Expand All @@ -63,7 +82,7 @@ def create_batch_request_model(

def create_stream_log_request_model(
namespace: str,
input_type: InputValidator,
input_type: Validator,
config: TypedDict,
) -> Type[BaseModel]:
"""Create a pydantic model for the invoke request."""
Expand All @@ -86,9 +105,11 @@ def create_stream_log_request_model(

def create_invoke_response_model(
namespace: str,
output_type: InputValidator,
output_type: Validator,
) -> Type[BaseModel]:
"""Create a pydantic model for the invoke response."""
# The invoke response uses a key called `output` for the output, so
# other information can be added to the response at a later date.
invoke_response_type = create_model(
f"{namespace}InvokeResponse",
output=(output_type, ...),
Expand All @@ -99,9 +120,11 @@ def create_invoke_response_model(

def create_batch_response_model(
namespace: str,
output_type: InputValidator,
output_type: Validator,
) -> Type[BaseModel]:
"""Create a pydantic model for the batch response."""
# The response uses a key called `output` for the output, so
# other information can be added to the response at a later date.
batch_response_type = create_model(
f"{namespace}BatchResponse",
output=(List[output_type], ...),
Expand Down

0 comments on commit 6ec7cea

Please sign in to comment.