xref: /plugin/ragasker/OpenAIHttpClient.php (revision 5f0c5114d87f8140fd00a9b6f05655e54e173dde)
1<?php
2class OpenAIHttpClient {
3    private $apiKey;
4    private $baseUrl = '';
5
6    public function __construct($serverUrl, $apiKey) {
7        $this->baseUrl = rtrim($serverUrl, '/');
8        $this->apiKey = $apiKey;
9    }
10
11    /**
12     * 发送聊天请求
13     */
14    public function chatCompletion($params) {
15        return $this->request('/chat/completions', $params);
16    }
17
18    /**
19     * 发送补全请求
20     */
21    public function completion($params) {
22        return $this->request('/completions', $params);
23    }
24
25    /**
26     * 通用 HTTP 请求方法
27     */
28    private function request($endpoint, $data) {
29        $url = $this->baseUrl . $endpoint;
30
31        $headers = [
32            'Content-Type: application/json',
33            'Authorization: Bearer ' . $this->apiKey
34        ];
35
36        $ch = curl_init($url);
37
38        curl_setopt_array($ch, [
39            CURLOPT_RETURNTRANSFER => true,
40            CURLOPT_POST => true,
41            CURLOPT_HTTPHEADER => $headers,
42            CURLOPT_POSTFIELDS => json_encode($data),
43            CURLOPT_TIMEOUT => 300,
44            CURLOPT_SSL_VERIFYPEER => true,
45            CURLOPT_FAILONERROR => false
46        ]);
47
48        $response = curl_exec($ch);
49        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
50        $error = curl_error($ch);
51        curl_close($ch);
52
53        if ($error) {
54            throw new Exception("cURL Error: " . $error);
55        }
56
57        $decoded = json_decode($response, true);
58
59        if ($httpCode !== 200) {
60            $errorMsg = $decoded['error']['message'] ?? "HTTP {$httpCode}";
61            throw new Exception("API Error: " . $errorMsg);
62        }
63
64        return $decoded;
65    }
66
67    /**
68     * 流式响应(适合长文本)
69     */
70    public function streamChatCompletion($params, $callback) {
71        $params['stream'] = true;
72        $url = $this->baseUrl . '/chat/completions';
73
74        $headers = [
75            'Content-Type: application/json',
76            'Authorization: Bearer ' . $this->apiKey,
77            'Accept: text/event-stream',
78            'Cache-Control: no-cache'
79        ];
80
81        $ch = curl_init($url);
82
83        curl_setopt_array($ch, [
84            CURLOPT_RETURNTRANSFER => true,
85            CURLOPT_POST => true,
86            CURLOPT_HTTPHEADER => $headers,
87            CURLOPT_POSTFIELDS => json_encode($params),
88            CURLOPT_WRITEFUNCTION => function($ch, $data) use ($callback) {
89                $callback($data);
90                return strlen($data);
91            },
92            CURLOPT_TIMEOUT => 120
93        ]);
94
95        curl_exec($ch);
96        curl_close($ch);
97    }
98
99    /**
100     * 获取可用模型列表
101     */
102    public function listModels() {
103        $url = $this->baseUrl . '/models';
104
105        $headers = [
106            'Authorization: Bearer ' . $this->apiKey
107        ];
108
109        $ch = curl_init($url);
110        curl_setopt_array($ch, [
111            CURLOPT_RETURNTRANSFER => true,
112            CURLOPT_HTTPHEADER => $headers,
113            CURLOPT_TIMEOUT => 10
114        ]);
115
116        $response = curl_exec($ch);
117        curl_close($ch);
118
119        return json_decode($response, true);
120    }
121}
122