ym88659208ym87991671
GigaServe | Документация для разработчиков

GigaServe

Обновлено 14 ноября 2024

GigaServe — это python-библиотека, которая позволяет размещать цепочки и runnable-интерфейсы GigaChain с предоставлением к ним доступа через REST API.

Библиотека GigaServe интегрирована с FastAPI и использует для валидации данных Pydantic.

Возможности GigaServe

Библиотека дает следующие возможности:

  • Автоматическое определение схем ввода и вывода основе объекта GigaChain. Схемы применяются для каждого запроса к API и обеспечивают подробные сообщения об ошибках.
  • Страница API-документации с JSONSchema и Swagger.
  • Эндпоинты с поддержкой множества одновременных запросов на одном сервере /invoke, /batch и /stream.
  • Эндпоинт /stream_log для потоковой передачи всех или выбранных промежуточных шагов работы цепочки/агента.
  • Интерактивная песочница /playground с потоковым отображением и демонстрацией промежуточных шагов.
  • Использование проверенных open-source библиотек Python таких, как FastAPI, Pydantic, uvloop и asyncio.
  • Клиентский SDK, который позволяет обращаться к серверу GigaServe также как к локальному runnable-интерфейсу или напрямую с помощью HTTP API.

Ограничения

  • Колбэки клиента не поддерживаются для событий, происходящих на сервере.
  • OpenAPI-спецификация не генерируется, если вы используете Pydantic V2. Это связанно с тем, что Fast API не поддерживает смешивание пространств имен pydantic v1 и v2. Подробнее в разделе ниже.

Установка

Для одновременной установки клиента и сервера используйте команду:

pip install "gigaserve[all]"

Вы можете установить клиент и сервер по отдельности с помощью команд:

# Команда установки клиента
pip install "gigaserve[client]"

# Команда установки сервера
pip gigaserve "langserve[server]"

Примеры

Для быстрого старта GigaServe используйте шаблоны GigaChain.

Больше примеров шаблонов вы найдете в репозитории.

Сервер

Пример ниже разворачивает чат-модели GigaChat и других LLM, а также цепочку, которая генерирует шутку по заданной теме (topic) с помощью модели Anthropic.

#!/usr/bin/env python
from fastapi import FastAPI
from langchain.prompts import ChatPromptTemplate
from langchain_gigachat.chat_models import GigaChat, ChatAnthropic, ChatOpenAI

from langserve import add_routes


app = FastAPI(
title="GigaChain Server",
version="1.0",
description="Простой API-сервер, использующий runnable-интерфейсы GigaChain",
)

add_routes(
app,
GigaChat(... креды ...),
path="/gigachat",
)

add_routes(
app,
ChatOpenAI(),
path="/openai",
)

add_routes(
app,
ChatAnthropic(),
path="/anthropic",
)

model = ChatAnthropic()
prompt = ChatPromptTemplate.from_template("расскажи шутку о {topic}")
add_routes(
app,
prompt | model,
path="/joke",
)

if __name__ == "__main__":
import uvicorn

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

Документация

Сгенерированная OpenAPI-документация к серверу, развернутому с помощью предыдущего примера, доступна по адресу:

curl localhost:8000/docs

При этом, адрес localhost:8000 будет возвращать ошибку 404, пока вы не определите @app.get("/").

При использовании pydantic v2 документация не генерируется для эндпоинтов /invoke, /batch, /stream и stream_log.

Клиент

Пример клиента на основе Python SDK:

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/joke/")

# Синхронный вызов

joke_chain.invoke({"topic": "попугаи"})

# Асинхронный вызов
await joke_chain.ainvoke({"topic": "попугаи"})

prompt = [
SystemMessage(content='Веди себя как кошка или попугай.'),
HumanMessage(content='Привет!')
]

# Поддержка astream
async for msg in anthropic.astream(prompt):
print(msg, end="", flush=True)

prompt = ChatPromptTemplate.from_messages(
[("system", "Расскажи мне длинную историю о {topic}")]
)

# Определение собственных цепочек
chain = prompt | RunnableMap({
"openai": openai,
"anthropic": anthropic,
})

chain.batch([{ "topic": "попугаи" }, { "topic": "кошки" }])

Клиент, использующий Python-библиотеку requests:

import requests
response = requests.post(
"http://localhost:8000/joke/invoke/",
json={'input': {'topic': 'кошки'}}
)
response.json()

Использование cURL:

curl --location --request POST 'http://localhost:8000/joke/invoke' \
--header 'Content-Type: application/json' \
--data-raw '{
"input": {
"topic": "кошки"
}
}'

Эндпоинты

С помощью примера ниже вы можете добавить на сервер заранее подготовленные эндпоинты для работы с runnable-интерфейсами:

...
add_routes(
app,
runnable,
path="/my_runnable",
)

Список эндпоинтов:

  • POST /my_runnable/invoke — вызвать runnable-интерфейс для единичных входных данных;
  • POST /my_runnable/batch — вызвать runnable-интерфейс для набора входных данных;
  • POST /my_runnable/stream — вызвать для единичных входных данных с потоковым выводом;
  • POST /my_runnable/stream_log — вызвать для единичных входных данных с потоковым выводом, включая вывод промежуточных шагов по ходу генерации;
  • GET /my_runnable/input_schema — получить JSON-схему входных данных runnable-интерфейса;
  • GET /my_runnable/output_schema — получить JSON-схему выходных данных runnable-интерфейса;
  • GET /my_runnable/config_schema — получить JSON-схему параметров конфигурации runnable-интерфейса;

Эндпоинты работают в соответствии с LangChain Expression Language interface (LCEL) — DSL для создания цепочек.

Песочница

Страница песочницы доступна по адресу /my_runnable/playground. На ней представлен простой интерфейс, который позволяет настроить параметры runnable-интерфейса и сделать запрос к нему с потоковым выводом и демонстрацией промежуточных шагов.

Основной интерфейс

Виджеты

Песочница поддерживает виджеты и может использоваться для тестирования ваших цепочек с разными входными данными.

Обмен конфигурацией цепочки

Кроме этого, если цепочка может настраиваться, песочница предоставляет задать параметры цепочки и поделиться ссылкой на полученную конфигурацию.

Интерфейс обмена конфигурацией

Работа с классическими цепочками

GigaServe работает как с runnable-интерфейсами (написанным с помощью LangChain Expression Language), так и с классическими цепочками (посредством наследования от Chain).

При работе с классическими цепочками учитывайте, что некоторые входные схемы для таких цепочек могут вызывать ошибки, т.к. могут быть некорректными или неполными. Такие ошибки можно предотвратить, если обновить атрибут input_schema таких цепочек в GigaChain.

Развертывание

Ниже описаны способы развертывания на Google Cloud Platforms (GCP) и Azure.

Развертывание на GCP

Для развертывания на GCP Cloud Run используйте команду:

gcloud run deploy [your-service-name] --source . --port 8001 --allow-unauthenticated --region us-central1 --set-env-vars=GIGACHAT_API_KEY=your_key

Развертывание на Azure

Вы можете развернуть сервер на Azure с помощью Azure Container Apps:

az containerapp up --name [container-app-name] --source . --resource-group [resource-group-name] --environment  [environment-name] --ingress external --target-port 8001 --env-vars=OPENAI_API_KEY=your_key

Подробная информация в официальной документации.

Работа с Pydantic

GigaServe поддерживает Pydantic 2 с некоторыми ограничениями:

  • При использовании Pydantic V2 документация OpenAPI не генерируется. Это связанно с тем, что Fast API не поддерживает смешивание пространств имен pydantic v1 и v2.
  • GigaChain использует пространство имен версии v1 в Pydantic v2.

За исключением указанных ограничений, эндпоинты API, страница песочницы и другие функции должны работать корректно.

Дополнительные возможности

Добавление аутентификации

О том, как добавить аутентификацию на свой сервер GigaServe — в разделах документации FastAPI, посвященных безопасности и использованию связующего ПО.

Работа с файлами

Обработка файлов — это типичная задача для больших языковых моделей. Существуют различные архитектурные подходы для решения этой задачи:

  • Файл может быть загружен на сервер с помощью одного эндпоинта и обработан с помощью другого;
  • Файл может быть представлен как в виде бинарного значения, так и в виде ссылки, например, на содержимое файла, размещенное в хранилище s3.
  • Эндпоинт может быть блокирующим или неблокирующим.
  • Сложную обработку можно выделить в отдельный пул процессов.

Выбирайте подход в соответствии со своими задачами.

GigaServe не поддерживает тип multipart/form-data. Для загрузки бинарного значения файла в runnable-интерфейс используйте кодировку base64.

Пример загрузки файла закодированного с помощью base64.

Вы также можете загружать файлы с помощью ссылок (например, в хранилище s3) или загружать их на отдельный эндпоинт как multipart/form-data.

Настраиваемые типы входных и выходных данных

Типы входных и выходных данных определяются для всех runnable-интерфейсов. Они доступны в атрибутах input_schema и output_schema. GigaServe использует эти типы для валидации данных и генерации документации.

Вы можете переопределить наследованные типы с помощью метода with_types.

Общий пример работы с типами:

from typing import Any

from fastapi import FastAPI
from langchain.schema.runnable import RunnableLambda

app = FastAPI()


def func(x: Any) -> int:
"""Ошибочно заданная функция, которая принимает любые данные, хотя должна принимать int."""
return x + 1


runnable = RunnableLambda(func).with_types(
input_schema=int,
)

add_routes(app, runnable)

Пользовательские типы

Для десериализации данных в pydantic-модель, а не dict, унаследуйтесь от CustomUserType. При наследовании от этого типа сервер не будет преобразовывать данные в dict, а будет сохранять их как pydantic-модель.

from fastapi import FastAPI
from langchain.schema.runnable import RunnableLambda

from langserve import add_routes
from langserve.schema import CustomUserType

app = FastAPI()


class Foo(CustomUserType):
bar: int


def func(foo: Foo) -> int:
"""Пример функции, которая ожидает тип Foo, представленный в виде моде pydantic model"""
assert isinstance(foo, Foo)
return foo.bar

# Обратите внимание, что входные и выходные типы наследуются автоматически!
# Вам не нужно их указывать
# runnable = RunnableLambda(func).with_types( # <-- Не нужно в данном случае
# input_schema=Foo,
# output_schema=int,
#
add_routes(app, RunnableLambda(func), path="/foo")

Тип CustomUserType поддерживается только на стороне сервера и определяет поведение при декодировании данных.

Виджеты песочницы

На странице песочницы вы можете создавать различные виджеты, демонстрирующие работу runnable-интерфейсов вашего бекенда.

  • Виджет задается на уровне поля и поставляется как часть JSON-схемы вводного типа.
  • Виджет должен содержать ключ type, значением которого является один из известного списка виджетов.
  • Другие ключи виджета будут связаны со значениями, описывающими пути в JSON-объекте.

Общая схема:

type JsonPath = number | string | (number | string)[];
type NameSpacedPath = { title: string; path: JsonPath }; // title используется для имитации JSON-схемы, но можно использовать namespace
type OneOfPath = { oneOf: JsonPath[] };

type Widget = {
type: string // Какой-то хорошо известный тип, например, base64file, chat и др.
[key: string]: JsonPath | NameSpacedPath | OneOfPath;
};

Виджет загрузки файла

Виджет позволяет загружать файлы в интерфейсе песочницы. Работает для файлов в виде base64-строки.

Фрагмент примера:

try:
from pydantic.v1 import Field
except ImportError:
from pydantic import Field

from langserve import CustomUserType


# ВНИМАНИЕ: Наследуйтесь от CustomUserType, а не от BaseModel. В противном случае
# сервер декодирует данные в dict, а не модель pydantic.
class FileProcessingRequest(CustomUserType):
"""Request including a base64 encoded file."""

# Дополнительное поле используется, чтобы задать виджет в интерфейсе интерактивной страницы.
file: str = Field(..., extra={"widget": {"type": "base64file"}})
num_chars: int = 100

Интерфейс загрузки файлов

Виджет чата

Пример виджета в репозитории.

Чтобы задать виджет чата передайте "type": "chat":

  • Поле input — JSONPath к полю запроса, которое содержит новое входящее сообщение.
  • Поле output — JSONPath к полю ответа, которое содержит одно или несколько сообщений.

Не указывайте эти поля, если входящие и исходящие данные должны быть представлены в исходном виде. Например, если нужно представить исходящие данные в виде списка сообщений.

Пример:

class ChatHistory(CustomUserType):
chat_history: List[Tuple[str, str]] = Field(
...,
examples=[[("human input", "ai response")]],
extra={"widget": {"type": "chat", "input": "question", "output": "answer"}},
)
question: str


def _format_to_messages(input: ChatHistory) -> List[BaseMessage]:
"""Представление вводда в виде списка собщений."""
history = input.chat_history
user_input = input.question

messages = []

for human, ai in history:
messages.append(HumanMessage(content=human))
messages.append(AIMessage(content=ai))
messages.append(HumanMessage(content=user_input))
return messages


model = ChatOpenAI()
chat_model = RunnableParallel({"answer": (RunnableLambda(_format_to_messages) | model)})
add_routes(
app,
chat_model.with_types(input_type=ChatHistory),
config_keys=["configurable"],
path="/chat",
)

Включение и отключение эндпоинтов

Начиная с версии GigaServe 0.0.33, можно включать и отключать открытые эндпоинты. Используйте атрибут enabled_endpoints, если вы хотите предотвратить перезапись эндпонтов при обновлении версии библиотеки.

Пример ниже включает варианты эндпоинтов invoke, batch и config_hash.

add_routes(app, chain, enabled_endpoints=["invoke", "batch", "config_hashes"])

Пример ниже отключает страницу песочницы для цепочки.

add_routes(app, chain, disabled_endpoints=["playground"])

Безопасность

В версиях библиотеки 0.0.13—0.0.15 песочница, доступная по адресу /playground, позволяет получить доступ к произвольным файлам на сервере. Такое поведение исправлено в версии библиотеки 0.0.16 и выше.

ПАО Сбербанк использует cookie для персонализации сервисов и удобства пользователей.
Вы можете запретить сохранение cookie в настройках своего браузера.