Llm.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. <?php
  2. namespace app\admin\controller\ps;
  3. use app\common\controller\Backend;
  4. use app\common\library\ps\LlmClient;
  5. use think\Db;
  6. /**
  7. * 大模型设置:线上拆版 + 本地简单任务
  8. *
  9. * @icon fa fa-cloud
  10. */
  11. class Llm extends Backend
  12. {
  13. protected $noNeedRight = [];
  14. /**
  15. * @var array
  16. */
  17. protected $keys = [
  18. 'llm_online_enable',
  19. 'llm_online_provider',
  20. 'llm_online_base_url',
  21. 'llm_online_api_key',
  22. 'llm_online_model',
  23. 'llm_online_vision_model',
  24. 'llm_online_timeout',
  25. 'llm_base_url',
  26. 'llm_api_key',
  27. 'llm_timeout',
  28. 'llm_text_model',
  29. 'llm_vision_model',
  30. 'llm_vision_complex',
  31. ];
  32. public function index()
  33. {
  34. if ($this->request->isPost()) {
  35. $row = $this->request->post('row/a');
  36. if (!is_array($row)) {
  37. $this->error('参数错误');
  38. }
  39. $oldKey = (string)Db::name('config')->where('name', 'llm_online_api_key')->value('value');
  40. $postedKey = isset($row['llm_online_api_key']) ? trim((string)$row['llm_online_api_key']) : '';
  41. if ($postedKey === '') {
  42. $row['llm_online_api_key'] = $oldKey;
  43. }
  44. $row['llm_online_enable'] = !empty($row['llm_online_enable']) && $row['llm_online_enable'] !== '0' ? '1' : '0';
  45. foreach ($this->keys as $name) {
  46. if (!array_key_exists($name, $row)) {
  47. continue;
  48. }
  49. $exists = Db::name('config')->where('name', $name)->find();
  50. if ($exists) {
  51. Db::name('config')->where('name', $name)->update(['value' => (string)$row[$name]]);
  52. }
  53. }
  54. $this->writeSiteFile();
  55. $this->success('已保存。拆版将优先使用线上视觉模型,本地 Ollama 仍用于简单任务。');
  56. }
  57. $site = [];
  58. foreach ($this->keys as $name) {
  59. $site[$name] = (string)Db::name('config')->where('name', $name)->value('value');
  60. }
  61. $this->view->assign('site', $site);
  62. $this->view->assign('keySaved', $site['llm_online_api_key'] !== '');
  63. $this->view->assign('providers', [
  64. 'dashscope' => '阿里云通义(推荐,qwen-vl-max)',
  65. 'openai' => 'OpenAI',
  66. 'moonshot' => '月之暗面 Kimi',
  67. 'zhipu' => '智谱 GLM',
  68. 'siliconflow' => '硅基流动',
  69. 'volcengine' => '火山方舟',
  70. 'deepseek' => 'DeepSeek(仅文本,不能拆版)',
  71. 'custom' => '自定义 OpenAI 兼容接口',
  72. ]);
  73. return $this->view->fetch();
  74. }
  75. /**
  76. * 测试本地 Ollama
  77. */
  78. public function testLocal()
  79. {
  80. $row = $this->request->post('row/a', []);
  81. try {
  82. $client = new LlmClient([
  83. 'profile' => 'local',
  84. 'base_url' => $row['llm_base_url'] ?? '',
  85. 'timeout' => (int)($row['llm_timeout'] ?? 30),
  86. ]);
  87. $models = $client->listModels();
  88. $this->success('本地 Ollama 已连通,共 ' . count($models) . ' 个模型', [
  89. 'models' => array_slice($models, 0, 30),
  90. ]);
  91. } catch (\Exception $e) {
  92. $this->error($e->getMessage());
  93. }
  94. }
  95. /**
  96. * 测试线上接口(用表单当前值,可先测再保存)
  97. */
  98. public function testOnline()
  99. {
  100. $row = $this->request->post('row/a', []);
  101. $apiKey = trim((string)($row['llm_online_api_key'] ?? ''));
  102. if ($apiKey === '') {
  103. $apiKey = (string)Db::name('config')->where('name', 'llm_online_api_key')->value('value');
  104. }
  105. if ($apiKey === '') {
  106. $this->error('请填写线上 API Key');
  107. }
  108. $model = trim((string)($row['llm_online_model'] ?? ''));
  109. if ($model === '') {
  110. $model = 'qwen-plus';
  111. }
  112. try {
  113. $client = new LlmClient([
  114. 'profile' => 'online',
  115. 'force' => true,
  116. 'online_enable'=> '1',
  117. 'base_url' => $row['llm_online_base_url'] ?? '',
  118. 'api_key' => $apiKey,
  119. 'text_model' => $model,
  120. 'vision_model' => $row['llm_online_vision_model'] ?? '',
  121. 'timeout' => (int)($row['llm_online_timeout'] ?? 60),
  122. ]);
  123. $reply = $client->chat('只回复两个字:成功', $model, false);
  124. $this->success('线上接口可用', [
  125. 'model' => $model,
  126. 'reply' => mb_substr(trim((string)$reply), 0, 80),
  127. ]);
  128. } catch (\Exception $e) {
  129. $this->error($e->getMessage());
  130. }
  131. }
  132. /**
  133. * 与系统配置共用 fa_config,直接重写 extra/site.php,避免无 general/config/edit 权限时写不进去。
  134. */
  135. protected function writeSiteFile()
  136. {
  137. $config = [];
  138. $rows = Db::name('config')->select();
  139. foreach ($rows as $value) {
  140. if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'], true)) {
  141. $value['value'] = explode(',', (string)$value['value']);
  142. }
  143. if ($value['type'] === 'array') {
  144. $value['value'] = (array)json_decode((string)$value['value'], true);
  145. }
  146. $config[$value['name']] = $value['value'];
  147. }
  148. file_put_contents(
  149. CONF_PATH . 'extra' . DS . 'site.php',
  150. "<?php\n\nreturn " . var_export($config, true) . ";\n"
  151. );
  152. }
  153. }