'/chat/completions', 'deepseek-reasoner' => '/chat/completions', 'upload' => '/v1/upload', 'generations' => '/v1/images/generations', 'generate' => '/v1/text/generate', 'uploadFile' => '/files', ]; public function __construct() { set_time_limit(0); $this->apiUrl = ConfigService::make()->getConfigByCode('dk_base_url'); $this->apiKey = ConfigService::make()->getConfigByCode('dk_api_key'); $this->apiName = ConfigService::make()->getConfigByCode('dk_api_name'); } /** * 静态入口 * @return static|null */ public static function make() { if (!self::$instance) { self::$instance = new static(); } return self::$instance; } /** * AI 分析接口 * @param $params * @param string $model * @return array|false|mixed */ public function apiRequest($params, $model='deepseek-chat') { if(empty($this->apiUrl) || empty($this->apiKey) || empty($this->apiName)){ $this->error = 'AI接口参数未配置'; return false; } $headers = [ 'Content-Type: application/json', 'Accept: application/json', 'Authorization: Bearer ' . $this->apiKey ]; $answer = isset($params['answer'])? $params['answer'] : ''; $topic = isset($params['topic'])? $params['topic'] : ''; $score = isset($params['score'])? $params['score'] : 0; //$type = isset($params['type'])? $params['type'] : 1; // 1-文本,2-图片 $content = '你是一个答题高手,请给描述中问题的答案评分,并尝试按 JSON:{"score": 8,"topic":"this is topic content","analysis":"this is topic analysis"}返回'; $message = "请给题目【{$topic}】总分{$score}的答案【{$answer}】评分?"; $format = 'json_object'; $data = [ 'model'=> $model, 'messages'=> [ [ 'role' => 'system', 'content' => $content ], [ 'role' => 'user', 'content' => $message ], ], 'stream' => false, //false 非流 true//流返回,需要前端追加和保持长连接 'response_format'=>[ 'type'=> $format //返回格式(text,json_object) ], "max_tokens"=>2048, //最大返回token数 ]; $url = $this->apiUrl.$this->apiUrls[$model]; $this->saveLog("caches:dkApi:{$model}:request_1_".date('YmdHis'), ['url'=>$url,'data'=>$data]); $result = aiRequest($url, json_encode($data), 60, $headers); $result = $result? json_decode(trim($result),true) : []; if(empty($result)){ $this->error = '答案验证失败'; return false; } $this->saveLog("caches:dkApi:{$model}:request_2_".date('YmdHis'), ['url'=>$url,'data'=>$data,'result'=>$result]); $choices = isset($result['choices'])? $result['choices'] : []; $choiceData = isset($choices[0]['message'])? $choices[0]['message'] : []; $choiceContent = isset($choiceData['content'])? $choiceData['content'] : ''; $content = $choiceContent? str_replace('\n','', $choiceContent) : ''; $content = $content? preg_replace("/^```json/",'', $content) : ''; $content = $content? rtrim($content,'```') : ''; $content = $content? json_decode($content, true) : []; return $content; } /** * 文件上传和处理(支持图像和文档) */ public function uploadAndProcessFile($filePath, $prompt = '请处理这个文件') { // 首先上传文件获取文件ID $fileId = $this->uploadFile($filePath); if (!$fileId) { return ['error' => '文件上传失败']; } // 使用文件ID进行对话 $messages = [ [ 'role' => 'user', 'content' => $prompt, 'file_ids' => [$fileId] ] ]; return $this->chat($messages); } /** * 上传文件到DeepSeek */ public function uploadFile($imageUrl) { $uploadUrl = 'http://127.0.5.12/api/upload/image'; // $uploadUrl = $this->apiUrl.$this->apiUrls['uploadFile']; $filePath = ATTACHMENT_PATH.get_image_path($imageUrl); if (!file_exists($filePath)) { return false; } $mimeType = mime_content_type($filePath); $fileName = basename($filePath); // 准备文件数据 $fileData = [ 'file' => new \CURLFile($filePath, $mimeType, $fileName), 'purpose' => 'vision' // 或者 'assistants',根据用途 ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $uploadUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $fileData, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $this->apiKey ], CURLOPT_TIMEOUT => 30 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); dump($response); curl_close($ch); if ($httpCode === 200) { $result = json_decode($response, true); return $result['id'] ?? false; } return false; } /** * 上传图片 * @param $image * @return false */ public function analyzeImageByFile($fileIds, $prompt='请分析读取这张图片内容') { if(empty($this->apiUrl) || empty($this->apiKey) || empty($this->apiName)){ $this->error = 'AI接口参数未配置'; return false; } $headers = [ 'Content-Type: application/json', 'Accept: application/json', 'Authorization: Bearer ' . $this->apiKey ]; // $filePath = ATTACHMENT_PATH.get_image_path($imageUrl); // $imageData = base64_encode(file_get_contents($filePath)); // $mineType = mime_content_type($filePath); $data = [ 'model'=> 'deepseek-reasoner', 'messages'=> [ [ 'role' => 'user', 'content' => $prompt, 'file_ids'=> ['file-bc202f5a-0894-478c-b22c-c79c9f8b8298'] ] ], // 'stream' => false, //false 非流 true//流返回,需要前端追加和保持长连接 // 'response_format'=>[ // 'type'=> 'text' //返回格式(text,json_object) // ], "max_tokens"=>2048, //最大返回token数 ]; $model = 'deepseek-reasoner'; $url = $this->apiUrl.$this->apiUrls[$model]; $this->saveLog("caches:dkApi:{$model}:request_1_".date('YmdHis'), ['url'=>$url,'data'=>$data]); $result = aiRequest($url, json_encode($data), 60, $headers); dump($result); var_dump($result); } /** * 上传Base64图片 * @param $image * @return false */ public function analyzeImageByBase64($imageUrl, $prompt='请分析读取这张图片内容') { if(empty($this->apiUrl) || empty($this->apiKey) || empty($this->apiName)){ $this->error = 'AI接口参数未配置'; return false; } $headers = [ 'Content-Type: application/json', 'Accept: application/json', 'Authorization: Bearer ' . $this->apiKey ]; $filePath = ATTACHMENT_PATH.get_image_path($imageUrl); $imageData = base64_encode(file_get_contents($filePath)); $mineType = mime_content_type($filePath); $data = [ 'purpose'=>'vision', 'file'=> $imageData, 'filename'=> basename($filePath), 'mine_type'=> $mineType ]; $model = 'uploadFile'; $url = $this->apiUrl.$this->apiUrls['uploadFile']; $this->saveLog("caches:dkApi:{$model}:request_1_".date('YmdHis'), ['url'=>$url,'data'=>$data]); $result = aiRequest($url, json_encode($data), 60, $headers); dump($result); var_dump($result); } /** * 上传图片 * @param $image * @return false */ public function analyzeImage($imageUrl, $prompt='请分析读取这张图片内容') { if(empty($this->apiUrl) || empty($this->apiKey) || empty($this->apiName)){ $this->error = 'AI接口参数未配置'; return false; } $headers = [ 'Content-Type: application/json', 'Accept: application/json', 'Authorization: Bearer ' . $this->apiKey ]; $filePath = ATTACHMENT_PATH.get_image_path($imageUrl); $imageData = base64_encode(file_get_contents($filePath)); $mineType = mime_content_type($filePath); $data = [ 'model'=> 'deepseek-reasoner', 'messages'=> [ [ 'role' => 'user', 'content' => [ [ 'type'=>'text', 'text'=> $prompt ], [ 'type'=>'image_url', 'image_url'=> [ 'url'=>"data:{$mineType};base64,{$imageData}" ] ] ] ] ], // 'stream' => false, //false 非流 true//流返回,需要前端追加和保持长连接 // 'response_format'=>[ // 'type'=> 'text' //返回格式(text,json_object) // ], "max_tokens"=>2048, //最大返回token数 ]; $model = 'deepseek-reasoner'; $url = $this->apiUrl.$this->apiUrls[$model]; $this->saveLog("caches:dkApi:{$model}:request_1_".date('YmdHis'), ['url'=>$url,'data'=>$data]); $result = aiRequest($url, json_encode($data), 60, $headers); dump($result); var_dump($result); } /** * 生成图片 * @param $image * @return false */ public function makeImage($prompt) { if(empty($this->apiUrl) || empty($this->apiKey) || empty($this->apiName)){ $this->error = 'AI接口参数未配置'; return false; } $headers = [ 'Content-Type: application/json', 'Accept: application/json', 'Authorization: Bearer ' . $this->apiKey ]; $url = $this->apiUrl.$this->apiUrls['deepseek-chat']; $data = [ 'prompt'=> $prompt, 'model'=>'deepseek-image', 'size'=> '1024x1024', ]; $result = aiRequest($url, json_encode($data), 10, $headers); var_dump($result); } /** * 获取题目内容 * @param $imageUrl * @return false */ public function getImageTopicData($imageUrl) { if(empty($imageUrl)){ return false; } // 图片获取数据 if(preg_match("/(images|temp)/", $imageUrl)){ $path = get_image_path($imageUrl); $ocr = new TesseractOCR(ATTACHMENT_PATH.$path); $data = $ocr->psm(3) ->horc() ->lang('chi_sim','eng','chi_tra') ->run(60); return $data?str_replace("\n",'\n', $data):''; } return $imageUrl; } /** * 保存缓存日志 * @param $cacheKey * @param $message * @param $expireTime */ public function saveLog($cacheKey, $message, $expireTime=0, $open=false) { if(env('APP_DEBUG') || $open){ RedisService::set($cacheKey, $message, $expireTime||$this->expireTime); } } }