如何使用 Next.js 13 使用 LangChain 的路由处理程序实现流 API 端点?

Leo*_*Zyl 6 javascript next.js

我正在尝试使用 Nextjs 13 的新路由处理程序解决方案创建一个 API 端点。该 API 使用 LangChain,并将响应流式传输回前端。当调用 OpenAI 包装器类时,我传入 Streaming 属性,并提供回调函数。然后,该回调函数将流作为块(即令牌)提供。我想将这些令牌流式传输到前端,以在生成人工智能时输出其响应。

我能够使用“旧”API 路由解决方案和以下代码来完成此工作:

import { OpenAI } from "langchain/llms/openai";

export default async function handler(req, res) {
  const chat = new OpenAI({
    modelName: "gpt-3.5-turbo",
    streaming: true,
    callbacks: [
      {
        handleLLMNewToken(token) {
          res.write(token);
        },
      },
    ],
  });

  await chat.call("Write me a song about sparkling water.");

  res.end();
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试将此代码转换为新的路由处理程序解决方案,但我无法使其正常工作。

我尝试了很多不同的方法,但没有成功。

例如:

import { NextResponse } from "next/server";

import { OpenAI } from "langchain/llms/openai";

export const dynamic = "force-dynamic";
export const revalidate = true;

export async function GET(req, res) {
  const chat = new OpenAI({
    modelName: "gpt-3.5-turbo",
    streaming: true,
    callbacks: [
      {
        handleLLMNewToken(token) {
          // res.write(token);
          return new NextResponse.json(token);
        },
      },
    ],
  });

  await chat.call("Write me a song about sparkling water.");
}

Run Code Online (Sandbox Code Playgroud)

当令牌流式传输到路由处理程序的响应时,似乎无法将令牌“写入”响应。

任何帮助将不胜感激。

Leo*_*Zyl 5

我想我可能有一个解决方案。

在路由处理程序中,我使用 TransformStream 类创建一个新的流对象。然后,我在生成令牌时将其写入该流对象。由于流需要向其传输字节,因此我使用 TextEncoder 将令牌编码为 Uint8Array 值。

最后,我在 API 响应中返回流的可读属性。这似乎可以解决问题,尽管比旧 API 路由方法的解决方案稍微复杂一些。

import { OpenAI } from "langchain/llms/openai";

export const dynamic = "force-dynamic";
export const revalidate = true;

async function runLLMChain() {
  // Create encoding to convert token (string) to Uint8Array
  const encoder = new TextEncoder();

  // Create a TransformStream for writing the response as the tokens as generated
  const stream = new TransformStream();
  const writer = stream.writable.getWriter();

  const chat = new OpenAI({
    modelName: "gpt-3.5-turbo",
    streaming: true,
    callbacks: [
      {
        async handleLLMNewToken(token) {
          await writer.ready;
          await writer.write(encoder.encode(`${token}`));
        },
        async handleLLMEnd() {
          await writer.ready;
          await writer.close();
        },
      },
    ],
  });
  chat.call("Write me a song about sparkling water.");

  // Return the readable stream
  return stream.readable;
}

export async function GET(req) {
  const stream = runLLMChain();
  return new Response(await stream);
}
Run Code Online (Sandbox Code Playgroud)