分享一个单文件的 ChatGPT api接口实现

banro512

一个非常简单的单文件 ChatGPT api接口实现

最终效果图如下.


使用说明

  1. 基于webman,首先要安装好webman

  2. 然后安装 OpenAI 的官方php库

    composer require openai-php/client
    composer require guzzlehttp/guzzle

  3. api在国内已被墙,但官方库(< v0.4.0)不支持使用代理,所以需要手动修改官方库,添加代理支持

打开 vendor\openai-php\client\src\OpenAI.php
将在 第29行 附近的代码 $client = new GuzzleClient(); 改为

  $client = new GuzzleClient([
            "proxy"=>"你的代理 http://ip:端口"
  ]);

比如我的代理地址是 127.0.0.1:7890 则修改后是:

        $client = new GuzzleClient([
            "proxy"=>"http://127.0.0.1:7890"
        ]);

新版 >= 0.4 以后 openai库有变化,无需再改动官方库了

  1. 将该文件放在某个控制器目录下,然后访问 http://域名/chatgpt 如果有模块,则访问 http://域名/模块/chatgpt

  2. php start.php restart 重启你的应用

全部代码如下


<?php
namespace app\controller;
use support\Request;
use OpenAI\Client;
// openai 的 key
// 全局代理后,在这里注册登录 https://platform.openai.com/signup 
define('OPENAI_KEY', '填写你的key');
class ChatgptController
{   
    // 初始化获取全部已回答
    public function all(Request $request){
        $messages = $request->session()->get('messages', []);
        return json([
            "code"=>0,
            "msg"=>"ok",
            "data"=>$messages
        ]);
    }
    // 单次查询
    public function query(Request $request)
    {
        $messages = $request->session()->get('messages', []);
        $messages[] = ['role' => 'user', 'content' => $request->input('message')];
        $current=[];
        try {
            // 旧版 <0.4 OpenAI 库 使用该代码 
            $response = OpenAI::client(OPENAI_KEY)->chat()->create([
                'model' => 'gpt-3.5-turbo',
                'messages' => $messages
            ]);
            // 新版 >=0.4 OpenAI 库使用该代码 
            /*
            $response=OpenAI::factory()
                ->withApiKey(OPENAI_KEY)
                ->withHttpClient(new \GuzzleHttp\Client([
                        "proxy"=>"你的代理 http://ip:端口"
              ]))->make()->chat()->create([
                'model' => 'gpt-3.5-turbo',
                'messages' => $messages
            ])
            */
            $current = ['role' => 'assistant', 'content' => nl2br($response->choices[0]->message->content)];
        } catch (\Exception $e) {
            $current = ['role' => 'assistant', 'content' => $e->getMessage()];
        }
        $messages[]=$current;
        $request->session()->put('messages', $messages);
            return json([
                "code"=>0,
                "msg"=>"ok",
                "data"=>$current['content']
            ]);
    }
    // 清除
    public function clear(Request $request)
    {
        return redirect("/");
    }
     public function index(Request $request)
    {
        $html=<<<'EOF'
        <!DOCTYPE html>
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>ChatGPT</title>
            <link href="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/tailwindcss/2.2.19/tailwind.min.css" type="text/css" rel="stylesheet" />
        </head>
        <body class="antialiased mx-auto" style="max-width:1000px">
            <div class="flex flex-col space-y-4 py-4 overflow-auto" id="body"></div>
            <div class="text-center text-red-400" id="loading"></div>
            <div class="py-4 flex flex-col space-x-1 justify-center items-center">
                <textarea required onkeypress="submit(this)" placeholder="输入后 ctrl+回车 或点击右侧 发送 按钮" name="message" class="border   p-2 flex-1 w-full" id="message"></textarea>
                <div class="flex justify-center items-center mt-2">
                    <input type="button" onclick="submit(this,'click')" class="cursor-pointer bg-green-500 text-white p-2  mx-1" value="发送请求" />
                    <a class="bg-gray-200 text-white p-2  mx-1" href="./clear" title="出现错误时,可尝试清除提问记录">清除记录</a>
                </div>
            </div>
            <script>
            // 名称
            window.chatgptConfig = {
                "assistant": "ChatGPT",
                "user": "我"
            };
            window.onload = function() {
                fetch("./all", {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-Requested-With': 'XMLHttpRequest'
                        },
                        body: ''
                    })
                    .then(response => response.json()) 
                    .then(json => {
                        if (json.data) {
                            json.data.forEach(it => {
                                append(it['content'], window.chatgptConfig[it.role]);
                            });
                        }
                    })
                    .catch(error => {
                        console.error(error);
                    });
            };
            function submit(el, type) {
                if (type === 'click' || (event.ctrlKey && event.keyCode === 10)) {
                    if (el.getAttribute('disabled')) {
                        alert("正在等待上次提问的回复,请稍等");
                        return;
                    }
                    var msg = document.getElementById('message').value.trim();
                    if (!msg) {
                        alert("必须输入问题");
                        return;
                    }
                    el.setAttribute('disabled', 'disabled');
                    append(msg, window.chatgptConfig['user']);
                    document.getElementById('loading').innerHTML = "努力思考中,稍等哦...";
                    document.documentElement.scrollTop+=50;
                    fetch("./query", {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                                'X-Requested-With': 'XMLHttpRequest'
                            },
                            body: JSON.stringify({ message: msg})
                        })
                        .then(response => response.json())
                        .then(json => {
                            append(json.data, window.chatgptConfig["assistant"]);
                            document.getElementById('loading').innerHTML = '';
                            document.getElementById('message').value = '';
                            el.removeAttribute('disabled', 'disabled');
                        })
                        .catch(error => {
                            console.error(error);
                            el.removeAttribute('disabled', 'disabled');
                        });
                }
            }
            function append(msg, name) {
                var html = `
                    <div class="ml-4">
                        <div class="text-lg">
                            <a href="#" class="font-medium ${name==window.chatgptConfig['assistant']?'text-green-400':'text-gray-900'}">${name}:</a>
                        </div>
                        <div class="mt-1">
                            <p class="text-gray-800">
                                ${msg}
                            </p>
                        </div>
                    </div>
                `;
                const newDiv = document.createElement("div");
                newDiv.className=`overflow-auto flex p-4 ${name==window.chatgptConfig['assistant']?'bg-gray-100 border flex-reverse':""}`;
                newDiv.innerHTML = html;
                const bodyDiv = document.getElementById("body");
                bodyDiv.appendChild(newDiv);
            }
            </script>
        </body>
        </html>
EOF;
        return $html;
    }
}
3014 6 4
6个评论

Tinywan

感谢分享

  • 暂无评论
liudada

https 域名的怎么代理呢

截图
这里没有$client = new GuzzleClient();这行代码怎么整

  • banro512 2023-03-20

    新版的 OpenAI 库有变化,不要手动去修改官方库了
    直接将请求时的代码

    $response = OpenAI::client(OPENAI_KEY)->chat()->create([
                    'model' => 'gpt-3.5-turbo',
                    'messages' => $messages
                ]);

    改为

            $response=OpenAI::factory()
                ->withApiKey(OPENAI_KEY)
                ->withHttpClient(new \GuzzleHttp\Client([
                        "proxy"=>"你的代理 http://ip:端口"
              ]))->make()->chat()->create([
                'model' => 'gpt-3.5-turbo',
                'messages' => $messages
            ])
  • 岛屿可以找到海 2023-03-20

    {
    "code": 500,
    "msg": "OpenAI\Factory::withHttpClient(): Argument #1 ($client) must be of type Psr\Http\Client\ClientInterface, GuzzleHttp\Client given, called in D:\Desktop\chatGptApi\app\controller\ChatgptController.php on line 30",
    "traces": "TypeError: OpenAI\Factory::withHttpClient(): Argument #1 ($client) must be of type Psr\Http\Client\ClientInterface, GuzzleHttp\Client given, called in D:\Desktop\chatGptApi\app\controller\ChatgptController.php on line 30 and defined in D:\Desktop\chatGptApi\vendor\openai-php\client\src\Factory.php:73\nStack trace:\n#0 D:\Desktop\chatGptApi\app\controller\ChatgptController.php(30): OpenAI\Factory->withHttpClient(Object(GuzzleHttp\Client))\n#1 D:\Desktop\chatGptApi\vendor\workerman\webman-framework\src\App.php(319): app\controller\ChatgptController->query(Object(support\Request))\n#2 D:\Desktop\chatGptApi\vendor\workerman\webman-framework\src\App.php(141): Webman\App::Webman\{closure}(Object(support\Request))\n#3 D:\Desktop\chatGptApi\vendor\workerman\workerman\Connection\TcpConnection.php(646): Webman\App->onMessage(Object(Workerman\Connection\TcpConnection), Object(support\Request))\n#4 D:\Desktop\chatGptApi\vendor\workerman\workerman\Events\Select.php(311): Workerman\Connection\TcpConnection->baseRead(Resource id #130)\n#5 D:\Desktop\chatGptApi\vendor\workerman\workerman\Worker.php(1479): Workerman\Events\Select->loop()\n#6 D:\Desktop\chatGptApi\vendor\workerman\workerman\Worker.php(1399): Workerman\Worker::forkWorkersForWindows()\n#7 D:\Desktop\chatGptApi\vendor\workerman\workerman\Worker.php(560): Workerman\Worker::forkWorkers()\n#8 D:\Desktop\chatGptApi\vendor\workerman\webman-framework\src\support\App.php(131): Workerman\Worker::runAll()\n#9 D:\Desktop\chatGptApi\start.php(4): support\App::run()\n#10 {main}"
    }

    替换完之后报这个错了

截图

  • banro512 2023-03-20

    <?php
    use OpenAI\Client;

    $response=OpenAI::factory()
                    ->withApiKey('sk-********************')
                    ->withHttpClient(new \GuzzleHttp\Client([
                            "proxy"=>"http://127.0.0.1:8581"
                  ]))->make()->chat()->create([
                    'model' => 'gpt-3.5-turbo',
                    'messages' => [['role' => 'user', 'content' => '写一段python代码,输出hello world']]
                ]);
    print_r($response);     
    

    实测无问题,有可能你的 guzzlehttp库太久了吧。更新下composer require guzzlehttp/guzzle

  • 岛屿可以找到海 2023-03-21

    升级后可以了,感谢分享

cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://api.openai.com/v1/chat/completions
这个怎么调

  • 暂无评论
lang

请问这个代理和端口怎么配置?

  • 暂无评论

banro512

1510
积分
0
获赞数
0
粉丝数
2021-12-16 加入
🔝