aliyun / alibabacloud-typescript-sdk

49 stars 26 forks source link

@alicloud/bailian20230601 包在next.js14的框架中,进行流式数据请求之前获取token的时候会进行报错 #22

Closed buzhou9 closed 8 months ago

buzhou9 commented 8 months ago

这是用于测试的路由文件代码

// 文件路径 app/api/test-stream/route.ts

import aliTokenHandler from "@/utils/aliTokenHandler";
import {NextRequest, NextResponse} from 'next/server';

export const runtime = 'edge';

async function handler(request: NextRequest, res: NextResponse, token: string) {

  const responseStream = new TransformStream();
  const writer = responseStream.writable.getWriter();
  const encoder = new TextEncoder();

  try {
    const responseChunks = `This is a response from a NextJS server. Thank you for your message! ${token}`.split(' ');
    sendStream(writer, encoder, responseChunks);
  } catch (err) {
    console.log(JSON.stringify(err));
    throw err;
  }

  return new Response(responseStream.readable, {
    headers: {
      'Content-Type': 'text/event-stream',
      Connection: 'keep-alive',
      'Cache-Control': 'no-cache, no-transform',
    },
  });

  function sendStream(
    writer: WritableStreamDefaultWriter<any>,
    encoder: TextEncoder,
    responseChunks: string[],
    chunkIndex = 0
  ) {
    setTimeout(() => {
      const chunk = responseChunks[chunkIndex];
      if (chunk) {
        // Sends response back to Deep Chat using the Response format:
        // https://deepchat.dev/docs/connect/#Response
        writer.write(encoder.encode(`data: ${JSON.stringify({text: `${chunk} `})}\n\n`));
        sendStream(writer, encoder, responseChunks, chunkIndex + 1);
      } else {
        writer.close();
      }
    }, 70);
  }
}

const GET = aliTokenHandler(handler)

export {
  GET
}

这是aliTokenHandler封装的方法

// 文件路径 /utils/aliTokenHandler
import {NextRequest, NextResponse} from 'next/server';
import Client from '@alicloud/bailian20230601'
import * as memoryCache from 'memory-cache'

type CallbackFunc = (req: NextRequest, res: NextResponse, token: string) => Promise<NextResponse<Response>> | Promise<Response>;

async function getToken() {

  const cacheKey = 'bailian_token'

  if (memoryCache.get(cacheKey)) {
    return memoryCache.get(cacheKey)
  }

  const accessKeyId = process.env.ALY_ACCESS_KEY_ID || '';
  const accessKeySecret = process.env.ALY_ACCESS_KEY_SECRET || '';
  const agentKey = process.env.ALY_BAILIAN_AGENT_KEY || '';
  const endpoint = 'bailian.cn-beijing.aliyuncs.com'

  // @ts-ignore
  const c = new Client({
    accessKeyId: accessKeyId,
    accessKeySecret: accessKeySecret,
    endpoint: endpoint
  })

  async function createToken() {
    const response = await c.createToken(
      // @ts-ignore
      { agentKey: agentKey }
    );

    const responseBody = response.body
    if (responseBody === null) {
      throw new Error('failed to create token')
    }

    if (responseBody.success !== true) {
      let requestId = responseBody.requestId;
      if (requestId == null) {
        requestId = response.headers["x-acs-request-id"];
      }

      let error = "failed to create token " + responseBody.message + ", requestId: " + requestId;
      throw new Error(error)
    }

    const tokenData = responseBody.data
    if (!tokenData) {
      throw new Error("failed to create token");
    }
    return tokenData
  }

  try {
    const { token = '', expiredTime = 0} = await createToken()
    memoryCache.put(cacheKey, token, expiredTime * 1000 - new Date().getTime());
    console.log('token: ' + memoryCache.get(cacheKey) + ', expiredTime: ' + expiredTime);
    return token
  } catch (err) {
    console.error('failed to create token, err: ', err);
    throw err
  }
}

export default function errorHandler(callbackFunc: CallbackFunc) {
  return async (req: NextRequest, res: NextResponse) => {
    try {
      const token = await getToken()
      return await callbackFunc(req, res, token);
    } catch (error) {
      console.error('API Error:', error);
      // Sends response back to Deep Chat using the Response format:
      // https://deepchat.dev/docs/connect/#Response
      return NextResponse.json({error}, {status: 500});
    }
  };
}

执行步骤

在浏览器访问 http://localhost:3000/api/test-stream

预期结果

流式输出文本并且返回token

实际结果

报错,以下是控制台错误日志

 ○ Compiling /api/test-stream ...
 ⨯ ./node_modules/@alicloud/credentials/dist/src/oidc_role_arn_credential.js:9:29
Module not found: Can't resolve 'fs'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/@alicloud/credentials/dist/src/client.js
./node_modules/@alicloud/openapi-client/dist/client.js
./node_modules/@alicloud/bailian20230601/dist/client.js
./src/utils/aliTokenHandler.ts
./src/app/api/test-stream/route.ts
./node_modules/next/dist/build/webpack/loaders/next-edge-app-route-loader/index.js?absolutePagePath=%2FUsers%2Fhuangxiaotong%2Fwork%2Fpenglai%2Fsrc%2Fapp%2Fapi%2Ftest-stream%2Froute.ts&page=%2Fapi%2Ftest-stream%2Froute&appDirLoader=bmV4dC1hcHAtbG9hZGVyP25hbWU9YXBwJTJGYXBpJTJGdGVzdC1zdHJlYW0lMkZyb3V0ZSZwYWdlPSUyRmFwaSUyRnRlc3Qtc3RyZWFtJTJGcm91dGUmYXBwUGF0aHM9JnBhZ2VQYXRoPXByaXZhdGUtbmV4dC1hcHAtZGlyJTJGYXBpJTJGdGVzdC1zdHJlYW0lMkZyb3V0ZS50cyZhcHBEaXI9JTJGVXNlcnMlMkZodWFuZ3hpYW90b25nJTJGd29yayUyRnBlbmdsYWklMkZzcmMlMkZhcHAmcGFnZUV4dGVuc2lvbnM9dHN4JnBhZ2VFeHRlbnNpb25zPXRzJnBhZ2VFeHRlbnNpb25zPWpzeCZwYWdlRXh0ZW5zaW9ucz1qcyZyb290RGlyPSUyRlVzZXJzJTJGaHVhbmd4aWFvdG9uZyUyRndvcmslMkZwZW5nbGFpJmlzRGV2PXRydWUmdHNjb25maWdQYXRoPXRzY29uZmlnLmpzb24mYmFzZVBhdGg9JmFzc2V0UHJlZml4PSZuZXh0Q29uZmlnT3V0cHV0PSZwcmVmZXJyZWRSZWdpb249Jm1pZGRsZXdhcmVDb25maWc9ZTMwJTNEIQ%3D%3D&nextConfigOutput=&preferredRegion=&middlewareConfig=e30%3D!
 ⚠ ./node_modules/sax/lib/sax.js
Module not found: Can't resolve 'stream' in '/Users/huangxiaotong/work/penglai/node_modules/sax/lib'

Import trace for requested module:
./node_modules/sax/lib/sax.js
./node_modules/xml2js/lib/parser.js
./node_modules/xml2js/lib/xml2js.js
./node_modules/@alicloud/tea-xml/dist/client.js
./node_modules/@alicloud/openapi-client/dist/client.js
./node_modules/@alicloud/bailian20230601/dist/client.js
./src/utils/aliTokenHandler.ts
./src/app/api/test-stream/route.ts
./node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fapi%2Ftest-stream%2Froute&page=%2Fapi%2Ftest-stream%2Froute&appPaths=&pagePath=private-next-app-dir%2Fapi%2Ftest-stream%2Froute.ts&appDir=%2FUsers%2Fhuangxiaotong%2Fwork%2Fpenglai%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Fhuangxiaotong%2Fwork%2Fpenglai&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=&preferredRegion=&middlewareConfig=e30%3D!./src/app/api/test-stream/route.ts?__next_edge_ssr_entry__
 ⚠ Fast Refresh had to perform a full reload due to a runtime error.

当我不使用流式返回的时候这个API工作是正常的

return NextResponse.json({text: `This is a respone from a NextJS edge server. Thankyou for your message! ${token}`});

我现在需要接入通义千问大模型的流式传输的接口,无法以这种形式去创建token

buzhou9 commented 8 months ago
export const runtime = 'edge'; 

改为

export const config = {
  runtime: 'edge',
};

就成功了,不知道为什么