'/chat/completions', 'deepseek-reasoner' => '/chat/completions', 'upload' => '/v1/upload', 'generations' => '/v1/images/generations', 'generate' => '/v1/text/generate', 'upload_file' => '/v0/file/upload_file', ]; 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'; if($type == 2){ $content = '你是一个识图专家,请读取图中中英文数字或符号内容,并尝试按 JSON:{"content":"this is photo content"}'; // $format = 'text'; // $content = '你是一个答题高手,请给描述中问题的答案评分,并尝试按 JSON:{"score": 8,"topic":"this is topic content","answer":"this is answer content","analysis":"this is topic analysis"}返回'; preg_match("/\.(jpg|jpeg|png)/", $answer, $extData); $ext = isset($extData[0])? $extData[0] : 'jpg'; $ext = $ext=='png'? 'png' : 'jpeg'; $filePath = ATTACHMENT_PATH.get_image_path($answer); $image = file_get_contents($filePath); $base64 = base64_encode($image); $base64 = "data:image/{$ext};base64,{$base64}"; $message = "请读取图片【{$base64}】内容?"; // $message = "请给总分为{$score}的题目【{$topic}】针对图片内【{$base64}】答案评分?"; } $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); dump($result); $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; } /** * 上传图片 * @param $image * @return false */ public function upload($image) { 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($image); dump($filePath); // $url = $this->apiUrl.$this->apiUrls['generate']; $url = $this->chaturl.$this->apiUrls['upload_file']; var_dump(basename($image)); // $file = fopen($url, 'rb'); $filePath = ATTACHMENT_PATH.get_image_path($filePath); $image = file_get_contents($filePath); $base64 = base64_encode($image); $base64 = "data:image/jpeg;base64,{$base64}"; // $file = new \CURLFile(realpath($filePath), 'application/octet-stream', basename($image)); $data = ['image' => $base64]; // $data = [ // "prompt"=> "解释量子计算的基本原理", // "max_tokens"=> 200, // "temperature"=>0.7 // ]; $data = [ 'files'=> $data ]; var_dump($url); var_dump($data); $result = aiRequest($url, '', 10, $headers); 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); // foreach((new TesseractOCR())->availableLanguages() as $lang) echo $lang; $ocr = new TesseractOCR(ATTACHMENT_PATH.$path); $data = $ocr->lang('chi_sim','eng','chi_tra') ->allowlist(range('a', 'z'), range('A', 'Z'), range(0, 9), '-_@') ->run(30); var_dump($data); return $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); } } }