DeepSeekService.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. <?php
  2. namespace App\Services\DeepSeek;
  3. use App\Consts\ErrorConst;
  4. use App\Facade\Site;
  5. use App\Libs\Utils;
  6. use GuzzleHttp\Client;
  7. use Illuminate\Support\Facades\DB;
  8. use Illuminate\Support\Facades\Redis;
  9. use OSS\Core\OssException;
  10. use OSS\OssClient;
  11. class DeepSeekService
  12. {
  13. private $url;
  14. private $api_key;
  15. private $client;
  16. private $headers;
  17. public function __construct() {
  18. $this->url = 'https://api.deepseek.com/chat/completions';
  19. $this->api_key = env('DEEPSEEK_API_KEY');
  20. $this->client = new Client();
  21. $this->headers = [
  22. 'Authorization' => 'Bearer '.$this->api_key,
  23. 'Content-Type' => 'application/json; charset=UTF-8'
  24. ];
  25. }
  26. // 与推理模型对话
  27. public function chatWithReasoner($data) {
  28. ini_set('max_execution_time', 0);
  29. $content = getProp($data, 'content');
  30. $model = getProp($data, 'model', 'r1');
  31. $model = $model == 'r1' ? 'deepseek-reasoner' : 'deepseek-chat';
  32. $messages = [
  33. [
  34. 'role' => 'system',
  35. 'content' => '下面有一段小说文本,请帮我将文本中的每句话按从上到下的顺序拆分成角色不同的剧本文稿(不得更改上下文顺序和内容),文稿形式严格按照“角色名(男或女):台词{情感}”输出,需要注意以下几点要求:
  36. 1.角色名后不要加入任何其他词语,只能加不包括旁白的性别,在男或女中选
  37. 2.非对话部分请全部用旁白角色代替
  38. 3.情感必须在【开心、悲伤、生气、惊讶、恐惧、厌恶、激动、冷漠、中性】中选一个,不得使用其他词语'
  39. ],
  40. [
  41. 'role' => 'user',
  42. 'content' => $content
  43. ]
  44. ];
  45. $post_data = [
  46. 'model' => $model, // R1模型: deepseek-reasoner V3模型: deepseek-chat
  47. 'messages' => $messages,
  48. 'max_tokens' => 8192,
  49. 'temperature' => 1, // 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。
  50. // 'top_p' => 1, // 作为调节采样温度的替代方案(<=1),模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。
  51. 'frequency_penalty' => 0, // 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。
  52. 'presence_penalty' => 0, // 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。
  53. 'response_format' => [
  54. 'type' => 'text' // 默认值text,回答的结果输出文字(非接口返回值是text,接口返回值还是json字串),还可选:json_object,输出json格式
  55. ],
  56. 'stream' => false // 是否流式输出,如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾。
  57. ];
  58. $result = $this->client->post($this->url, ['json' => $post_data, 'headers' => $this->headers]);
  59. $response = $result->getBody()->getContents();
  60. $response_arr = json_decode($response, true);
  61. $update_data = [];
  62. $content = '';
  63. if (isset($response_arr['choices']) && count($response_arr['choices']) > 0) {
  64. $content = isset($response_arr['choices'][0]['message']['content']) ? $response_arr['choices'][0]['message']['content'] : '';
  65. $update_data = [
  66. 'role' => 'assistant',
  67. 'content' => $response_arr['choices'][0]['message']['content'],
  68. 'usage' => isset($response_arr['usage']) ? $response_arr['usage'] : []
  69. ];
  70. }
  71. // 处理获取到的剧本数据
  72. $script_content = handleScriptWords($content);
  73. $result = [
  74. 'origin_content' => $content,
  75. 'roles' => getProp($script_content, 'roles'),
  76. 'words' => getProp($script_content, 'words'),
  77. ];
  78. return $result;
  79. }
  80. // 文字合成语音(火山引擎)
  81. public function tts($data) {
  82. $url = 'https://openspeech.bytedance.com/api/v1/tts';
  83. $headers = [
  84. 'Authorization' => 'Bearer;'.env('VOLC_TOKEN'),
  85. 'Content-Type' => 'application/json; charset=UTF-8'
  86. ];
  87. $post_data = [
  88. 'app' => [
  89. 'appid' => env('VOLC_APPID'),
  90. 'token' => env('VOLC_TOKEN'),
  91. 'cluster' => 'volcano_tts'
  92. ],
  93. 'user' => [
  94. 'uid' => 'mp_audio'
  95. ],
  96. // 'audio' => [
  97. // 'voice_type' =>
  98. // ],
  99. ];
  100. }
  101. }