V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
x77
V2EX  ›  程序员

邀请懂 TypeScript 的程序员帮忙改代码

  •  
  •   x77 · 307 天前 · 1440 次点击
    这是一个创建于 307 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一个 ChatGPT 的 Web UI 项目,搞得还不错支持语音,我小试了一下 Azure 的语音合成,效果真不错。不过这项目设计是在客户端(浏览器)向 OpenAI 发起请求,有没有人帮忙改成由服务端发起请求,类似这个项目。具体的要修改的代码可能在这里: https://github.com/hahahumble/speechgpt/blob/main/src/apis/openai.ts

    我知道代码里有个代理设置,在云端开个 Function 服务器就可以转发,但还是有些麻烦,另外还得传输 APIKey 不是很安全。

    我不懂 Typescript ,虽然语言都是相通的,能看懂能小改 Typescript ,但是要用不懂的语言来表达还是有些吃力,也容易搞得低效、不合理。也有人给这项目提类似需求的 Issue (希望在服务端设置 API ),但是开发方更新有点慢。

    那就自个搞一下把,希望有兴趣的开发者帮忙改下,感谢。

    15 条回复    2023-05-27 19:24:32 +08:00
    ByteCat
        1
    ByteCat  
       307 天前
    Node.js 18 开始也自带 fetch 支持了呀
    a632079
        2
    a632079  
       307 天前
    https://nodejs.org/dist/latest-v18.x/docs/api/globals.html#fetch

    亦或者添加 node-fetch 模块。

    P.S 这和 TS 无关,纯粹模块问题……
    leokun
        3
    leokun  
       307 天前
    这个如果改成 next 的还是有一点工作量的
    最简单最快的方法就是 njs 写一个转发,打成容器就好了
    x77
        4
    x77  
    OP
       307 天前
    @ByteCat
    @a632079
    我 nodejs 开发环境都没折腾啊
    Aloento
        5
    Aloento  
       307 天前
    @x77 #4 他们应该听不懂你在说什么
    CLMan
        6
    CLMan  
       307 天前
    这软件架构就是这样,服务端提供的只是一个 UI ,由本地向 OpenAI API 地址发起请求的。

    你要改成服务端请求,最简单的办法是搭建一个 api 代理,将其密钥放在代理的服务端,UI 层只需要根据你的需求进行小改。
    Puteulanus
        7
    Puteulanus  
       307 天前   ❤️ 2
    你这能算自个搞一下吗哥 😂
    leokun
        8
    leokun  
       307 天前
    a632079
        9
    a632079  
       307 天前
    @Aloento 他说服务端,也没提什么框架,什么 Runtime 。只提了一嘴小改 TS ,那不就默认 node 了嘛😨
    zbinlin
        10
    zbinlin  
       307 天前
    这个项目不就是一个纯前端项目吗,如果你要改成从服务端发起请求,不就需要添加服务端 API 了(或者用 nginx 的 njs 来添加一个转发接口?)
    AS4694lAS4808
        11
    AS4694lAS4808  
       307 天前 via Android
    代码只需要加一行,把 base_url 改成你机器的。

    然后在对应的机器上开一个 nginx ,后端代理到 api.openai.com ,location 里添加密钥头,完事。
    ruoxie
        12
    ruoxie  
       307 天前
    代码直接给你了
    import * as https from 'https';
    import { TextDecoder } from 'util';

    export const createChatCompletion = (options: {
    host: string;
    apiKey: string;
    model: string;
    text: string;
    context?: string;
    maxTokens: number;
    handleChunk?: (data: { text?: string; hasMore: boolean }) => void;
    }) =>
    new Promise<string>((resolve, reject) => {
    let combinedResult = '';
    const request = https.request(
    {
    hostname: options.host,
    port: 443,
    path: '/v1/chat/completions',
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${options.apiKey}`,
    },
    },
    (res) => {
    res.on('data', async (chunk) => {
    const text = new TextDecoder('utf-8').decode(chunk);
    const data = text.split('\n\n').filter((s) => s);
    for (let i = 0; i < data.length; i++) {
    try {
    let element = data[i];
    if (element.includes('data: ')) {
    if (element.trim() === 'data:') {
    // 处理只返回了 data: 的情况
    return;
    }
    } else if (element.includes('delta')) {
    // 处理没有 data 开头
    element = `data: ${element}`;
    }
    if (element.includes('data: ')) {
    if (element.includes('[DONE]')) {
    options.handleChunk &&
    options.handleChunk({ hasMore: false, text: '' });
    return;
    }
    // remove 'data: '
    const data = JSON.parse(element.replace('data: ', ''));
    if (data.finish_reason === 'stop') {
    options.handleChunk &&
    options.handleChunk({ hasMore: false, text: '' });
    return;
    }
    const openaiRes = data.choices[0].delta.content;
    if (openaiRes) {
    options.handleChunk &&
    options.handleChunk({
    text: openaiRes.replaceAll('\\n', '\n'),
    hasMore: true,
    });
    combinedResult += openaiRes;
    }
    } else {
    options.handleChunk &&
    options.handleChunk({ hasMore: false, text: element });
    return;
    }
    } catch (e) {
    console.error({
    e,
    element: data[i],
    });
    }
    }
    });
    res.on('error', (e) => {
    options.handleChunk &&
    options.handleChunk({ hasMore: false, text: e.toString() });
    reject(e);
    });
    res.on('end', () => {
    resolve(combinedResult);
    });
    },
    );
    const body = {
    model: options.model,
    messages: [
    {
    role: 'system',
    content: options.context || '',
    },
    {
    role: 'user',
    content: options.text,
    },
    ],
    stream: true,
    max_tokens: options.maxTokens,
    };
    request.write(JSON.stringify(body));
    request.end();
    });
    ruoxie
        13
    ruoxie  
       307 天前
    这个项目没有用流模式,体验太差了
    x77
        14
    x77  
    OP
       306 天前
    @Puteulanus 咱们自个儿
    x77
        15
    x77  
    OP
       306 天前
    @ruoxie 感谢。
    我这边只能编译 docker 镜像,把您的代码替换掉 openai.ts 文件就可以了吗?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1589 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 16:58 · PVG 00:58 · LAX 09:58 · JFK 12:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.