lh 2 месяцев назад
Родитель
Сommit
9a0d88ab57
2 измененных файлов с 329 добавлено и 23 удалено
  1. 1 0
      app/Consts/BaseConst.php
  2. 328 23
      app/Services/DeepSeek/DeepSeekService.php

+ 1 - 0
app/Consts/BaseConst.php

@@ -85,6 +85,7 @@ class BaseConst
     const VOLC_PIC_MODELS = ['doubao-seedream-5-0-260128', 'doubao-seedream-5-0-lite-260128', 'doubao-seedream-4-5-251128'];
     const GPT_IMAGE2_MODELS = ['gpt-image-2'];
     const NANO_BANANA_MODELS = ['NanoBanana2', 'NanoBananaPro', 'GptImage2'];
+    const GPT_TEXT_MODELS = ['gpt-5.4', 'gpt-5.6-terra'];
     // 火山写作奖励计划接入点(使用该接入点可在第二天根据前一天使用量获取免费额度)
     const MODELS_MAP = [
         'doubao-seed-2-0-pro-260215'        => 'ep-20260422093628-pv5vj',

+ 328 - 23
app/Services/DeepSeek/DeepSeekService.php

@@ -3,6 +3,7 @@
 namespace App\Services\DeepSeek;
 
 use App\Consts\ErrorConst;
+use App\Consts\BaseConst;
 use App\Facade\Site;
 use App\Libs\Utils;
 use GuzzleHttp\Client;
@@ -26,6 +27,7 @@ class DeepSeekService
     private $sys_message;
     private $headers;
     private $valid_text_models;
+    private $gpt_text_models;
     protected $aiImageGenerationService;
 
     public function __construct(AIImageGenerationService $aiImageGenerationService) {
@@ -38,6 +40,8 @@ class DeepSeekService
         ];
         // 火山引擎模型列表
         $this->valid_text_models = DB::table('mp_text_models')->where('is_enabled', 1)->pluck('model')->toArray();
+        // GPT文本模型列表
+        $this->gpt_text_models = BaseConst::GPT_TEXT_MODELS;
         $this->sys_message = [
                 'role'    => 'system',
                 'content' => "你是一个专业的文档分析助手及资深编剧,请根据用户提供的文档内容完成示例格式的内容输出(需通过以下几个板块进行回复: <故事梗概><剧本亮点><人物关系><核心矛盾><主体列表><美术风格><场景列表><分集剧本>,每个板块之间必须使用###分隔,#不能多也不能少;同时板块之间需满足以下要求:\n
@@ -247,8 +251,8 @@ class DeepSeekService
         if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
             // DeepSeek 官方模型使用 DeepSeek API
             return $this->deepSeekStreamResponse($post_data);
-        } else if ($model === 'gpt-5.4') {
-            // GPT-5.4 模型使用 TokenRouter API
+        } else if (in_array($model, $this->gpt_text_models)) {
+            // GPT 模型使用 TokenRouter API
             return $this->gpt54StreamResponse($post_data);
         } else if (in_array($model, $this->valid_text_models)) {
             // 火山引擎支持的模型使用火山引擎 API
@@ -525,9 +529,10 @@ class DeepSeekService
             if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
                 // DeepSeek模型
                 $post_data['response_format'] = ['type' => 'json_object'];
-            } else if ($model === 'gpt-5.4') {
-                // GPT-5.4模型
+            } else if (in_array($model, $this->gpt_text_models)) {
+                // GPT模型
                 $post_data['response_format'] = ['type' => 'json_object'];
+                dd($post_data);
             } else if (in_array($model, $this->valid_text_models)) {
                 // 火山引擎模型
                 $post_data['response_format'] = ['type' => 'json_object'];
@@ -550,11 +555,11 @@ class DeepSeekService
                 'model' => $model,
                 'finish_reason' => 'stop'
             ];
-        } else if ($model === 'gpt-5.4') {
-            // GPT-5.4 模型使用现有的 gpt54ChatOnly 方法
+        } else if (in_array($model, $this->gpt_text_models)) {
+            // GPT 模型使用现有的 gpt54ChatOnly 方法
             $result = $this->gpt54ChatOnly($post_data);
             
-            dLog('deepseek')->info('newGenerateText使用GPT-5.4模型', ['requested_model' => $model]);
+            dLog('deepseek')->info('newGenerateText使用GPT模型', ['requested_model' => $model]);
             
             $aiResult = [
                 'content' => $result['fullContent'],
@@ -1290,16 +1295,16 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
     }
 
     /**
-     * GPT-5.4 流式输出处理方法
+     * GPT 流式输出处理方法
      * 
-     * @param array $post_data GPT-5.4 API请求参数
+     * @param array $post_data GPT API请求参数
      * @return \Generator 返回生成器,用于流式输出
      */
     private function gpt54StreamResponse($post_data) {
         $apiKey = env('GPT_54_API_KEY');
         
         if (empty($apiKey)) {
-            Utils::throwError('20003:GPT-5.4 API Key未配置');
+            Utils::throwError('20003:GPT API Key未配置');
         }
 
         $client = new Client(['timeout' => 1200, 'verify' => false]);
@@ -1328,7 +1333,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         $usage = [];
         $buffer = '';
 
-        dLog('deepseek')->info('开始读取 GPT-5.4 流式响应');
+        dLog('deepseek')->info('开始读取 GPT 流式响应');
 
         // 逐行读取流式响应
         while (!$body->eof()) {
@@ -1483,7 +1488,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
             // DeepSeek 官方模型使用 DeepSeek API
             $chatResult = $this->chatOnly($post_data);
-        } else if ($model === 'gpt-5.4') {
+        } else if (in_array($model, $this->gpt_text_models)) {
             // GPT-5.4 模型使用 TokenRouter API
             $chatResult = $this->gpt54ChatOnly($post_data);
         } else {
@@ -2484,7 +2489,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
             // DeepSeek 官方模型使用 DeepSeek API
             $streamGenerator = $this->deepSeekStreamResponse($post_data);
-        } else if ($model === 'gpt-5.4') {
+        } else if (in_array($model, $this->gpt_text_models)) {
             // GPT-5.4 模型使用 TokenRouter API
             $streamGenerator = $this->gpt54StreamResponse($post_data);
         } else {
@@ -2768,7 +2773,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
             // DeepSeek 官方模型使用 DeepSeek API
             $streamGenerator = $this->deepSeekStreamResponse($post_data);
-        } else if ($model === 'gpt-5.4') {
+        } else if (in_array($model, $this->gpt_text_models)) {
             // GPT-5.4 模型使用 TokenRouter API
             $streamGenerator = $this->gpt54StreamResponse($post_data);
         } else {
@@ -2985,7 +2990,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
             // DeepSeek 官方模型使用 DeepSeek API
             $chatResult = $this->chatOnly($post_data);
-        } else if ($model === 'gpt-5.4') {
+        } else if (in_array($model, $this->gpt_text_models)) {
             // GPT-5.4 模型使用 TokenRouter API
             $chatResult = $this->gpt54ChatOnly($post_data);
         } else {
@@ -3895,7 +3900,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
                 // DeepSeek 官方模型使用 DeepSeek API
                 $streamGenerator = $this->deepSeekStreamResponse($post_data);
-            } else if ($model === 'gpt-5.4') {
+            } else if (in_array($model, $this->gpt_text_models)) {
                 // GPT-5.4 模型使用 TokenRouter API
                 $streamGenerator = $this->gpt54StreamResponse($post_data);
             } else {
@@ -4741,7 +4746,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         try {
             if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
                 $streamGenerator = $this->deepSeekStreamResponse($post_data);
-            } else if ($model === 'gpt-5.4') {
+            } else if (in_array($model, $this->gpt_text_models)) {
                 // GPT-5.4 模型使用 TokenRouter API
                 $streamGenerator = $this->gpt54StreamResponse($post_data);
             } else {
@@ -5603,7 +5608,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
                 // DeepSeek 官方模型使用 DeepSeek API
                 $streamGenerator = $this->deepSeekStreamResponse($post_data);
-            } else if ($model === 'gpt-5.4') {
+            } else if (in_array($model, $this->gpt_text_models)) {
                 // GPT-5.4 模型使用 TokenRouter API
                 $streamGenerator = $this->gpt54StreamResponse($post_data);
             } else {
@@ -6221,7 +6226,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
                 // DeepSeek 官方模型使用 DeepSeek API
                 $streamGenerator = $this->deepSeekStreamResponse($post_data);
-            } else if ($model === 'gpt-5.4') {
+            } else if (in_array($model, $this->gpt_text_models)) {
                 // GPT-5.4 模型使用 TokenRouter API
                 $streamGenerator = $this->gpt54StreamResponse($post_data);
             } else {
@@ -7124,7 +7129,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         try {
             if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
                 $streamGenerator = $this->deepSeekStreamResponse($post_data);
-            } else if ($model === 'gpt-5.4') {
+            } else if (in_array($model, $this->gpt_text_models)) {
                 // GPT-5.4 模型使用 TokenRouter API
                 $streamGenerator = $this->gpt54StreamResponse($post_data);
             } else {
@@ -8147,7 +8152,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
             if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
                 // DeepSeek 官方模型使用 DeepSeek API
                 $streamGenerator = $this->deepSeekStreamResponse($post_data);
-            } else if ($model === 'gpt-5.4') {
+            } else if (in_array($model, $this->gpt_text_models)) {
                 // GPT-5.4 模型使用 TokenRouter API
                 $streamGenerator = $this->gpt54StreamResponse($post_data);
             } else {
@@ -8377,6 +8382,306 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         ];
     }
 
+    /**
+     * 重新生成分段剧本
+     * @param array $data 包含以下字段:
+     *   - prompt: string|null 用户修改要求(可选)
+     *   - template_id: int|null 提示词模板ID(可选)
+     *   - act_id: int|null 单个片段ID(与episode_id二选一)
+     *   - episode_id: int|null 剧集ID,用于重新生成整个剧集的所有片段(与act_id二选一)
+     * @return array 返回生成结果
+     */
+    public function regenerateSegmentScript($data) {
+        $uid = Site::getUid();
+        $prompt = trim((string)getProp($data, 'prompt', ''));
+        $template_id = getProp($data, 'template_id', 0);
+        $act_id = getProp($data, 'act_id', 0);
+        $episode_id = getProp($data, 'episode_id', 0);
+        
+        // 验证:prompt和template_id至少提供一个
+        if (empty($prompt) && empty($template_id)) {
+            Utils::throwError('20003:请提供修改要求或选择提示词模板');
+        }
+        
+        // 验证:act_id和episode_id二选一
+        if (empty($act_id) && empty($episode_id)) {
+            Utils::throwError('20003:请提供片段ID或剧集ID');
+        }
+        
+        if (!empty($act_id) && !empty($episode_id)) {
+            Utils::throwError('20003:片段ID和剧集ID只能选择一个');
+        }
+        
+        // 如果提供了template_id,获取模板提示词
+        $template_prompt = '';
+        if ($template_id > 0) {
+            $template = DB::table('mp_prompt_templates')
+                ->where('id', $template_id)
+                ->where('is_deleted', 0)
+                ->first();
+            
+            if (!$template) {
+                Utils::throwError('20003:提示词模板不存在或已删除');
+            }
+            
+            $template_prompt = $template->template_prompt ?? '';
+        }
+        
+        // 合并用户提示词和模板提示词
+        $final_prompt = '';
+        if (!empty($template_prompt)) {
+            $final_prompt = $template_prompt;
+            if (!empty($prompt)) {
+                $final_prompt .= "\n\n补充要求:" . $prompt;
+            }
+        } else {
+            $final_prompt = $prompt;
+        }
+        
+        // 获取需要重新生成的内容参考
+        $reference_content = '';
+        $target_episode_id = 0;
+        $target_anime_id = 0;
+        $target_episode_number = 0;
+        
+        if ($act_id > 0) {
+            // 单个片段模式:获取该片段的内容
+            $segment = DB::table('mp_episode_segments')
+                ->where('id', $act_id)
+                ->first();
+            
+            if (!$segment) {
+                Utils::throwError('20003:片段不存在');
+            }
+            
+            $target_episode_id = $segment->episode_id;
+            $target_anime_id = $segment->anime_id;
+            $target_episode_number = $segment->episode_number;
+            $reference_content = $segment->act_content ?? '';
+            
+        } else {
+            // 整个剧集模式:获取所有片段的内容
+            $segments = DB::table('mp_episode_segments')
+                ->where('episode_id', $episode_id)
+                ->orderBy('act_number')
+                ->get();
+            
+            if ($segments->isEmpty()) {
+                Utils::throwError('20003:该剧集没有片段数据');
+            }
+            
+            $target_episode_id = $episode_id;
+            $target_anime_id = $segments[0]->anime_id;
+            $target_episode_number = $segments[0]->episode_number;
+            
+            // 拼接所有片段内容,保留格式用于AI理解
+            $act_contents = [];
+            foreach ($segments as $seg) {
+                $act_number = $seg->act_number;
+                $act_content = $seg->act_content ?? '';
+                if (!empty($act_content)) {
+                    $act_contents[] = "##片段{$act_number}\n" . $act_content;
+                }
+            }
+            $reference_content = implode("\n\n", $act_contents);
+        }
+        
+        if (empty($reference_content)) {
+            Utils::throwError('20003:没有可参考的片段内容');
+        }
+        
+        // 获取剧集的基本信息(intro、art_style、roles、scenes)
+        $episode = DB::table('mp_anime_episodes')
+            ->where('id', $target_episode_id)
+            ->first();
+        
+        if (!$episode) {
+            Utils::throwError('20003:剧集不存在');
+        }
+        
+        $intro = $episode->intro ?? '';
+        $art_style = $episode->art_style ?? '';
+        $roles = json_decode($episode->roles ?? '[]', true);
+        $scenes = json_decode($episode->scenes ?? '[]', true);
+        
+        // 构建提示词中的主体列表和场景列表参考
+        $roles_ref = '';
+        if (!empty($roles) && is_array($roles)) {
+            $roles_list = [];
+            foreach ($roles as $role) {
+                $role_name = getProp($role, 'role', '');
+                $role_desc = getProp($role, 'description', '');
+                $pic_prompt = getProp($role, 'pic_prompt', '');
+                $voice_prompt = getProp($role, 'voice_prompt', '');
+                
+                if (!empty($role_name)) {
+                    $role_line = $role_name;
+                    if (!empty($role_desc)) $role_line .= ":" . $role_desc;
+                    if (!empty($pic_prompt)) $role_line .= "{" . $pic_prompt . "}";
+                    if (!empty($voice_prompt)) $role_line .= "{{" . $voice_prompt . "}}";
+                    $roles_list[] = $role_line;
+                }
+            }
+            if (!empty($roles_list)) {
+                $roles_ref = "\n\n【主体列表参考】\n" . implode("\n", $roles_list);
+            }
+        }
+        
+        $scenes_ref = '';
+        if (!empty($scenes) && is_array($scenes)) {
+            $scenes_list = [];
+            foreach ($scenes as $scene) {
+                $scene_name = getProp($scene, 'scene', '');
+                $scene_desc = getProp($scene, 'description', '');
+                $pic_prompt = getProp($scene, 'pic_prompt', '');
+                
+                if (!empty($scene_name)) {
+                    $scene_line = $scene_name;
+                    if (!empty($scene_desc)) $scene_line .= ":" . $scene_desc;
+                    if (!empty($pic_prompt)) $scene_line .= "{" . $pic_prompt . "}";
+                    $scenes_list[] = $scene_line;
+                }
+            }
+            if (!empty($scenes_list)) {
+                $scenes_ref = "\n\n【场景列表参考】\n" . implode("\n", $scenes_list);
+            }
+        }
+        
+        // 构建美术风格参考
+        $art_style_ref = '';
+        if (!empty($art_style)) {
+            $art_style_ref = "\n\n【美术风格参考】\n" . $art_style;
+        }
+        
+        // 构建内容简介参考
+        $intro_ref = '';
+        if (!empty($intro)) {
+            $intro_ref = "\n\n【内容简介参考】\n" . $intro;
+        }
+        
+        // 构建完整的AI提示词
+        $system_prompt = "你是一个专业的剧本编剧和影视导演,擅长根据已有的分段剧本进行优化和调整。你需要严格按照用户的要求重新生成分段剧本内容,保持原有的格式和结构。";
+        
+        $user_prompt = "请根据以下要求重新生成分段剧本:\n\n【用户要求】\n{$final_prompt}";
+        $user_prompt .= "\n\n【原分段剧本内容】\n{$reference_content}";
+        $user_prompt .= $intro_ref;
+        $user_prompt .= $art_style_ref;
+        $user_prompt .= $roles_ref;
+        $user_prompt .= $scenes_ref;
+        
+        $user_prompt .= "\n\n【格式要求】\n";
+        $user_prompt .= "1. 必须保持原有的分段格式(##片段X格式)\n";
+        $user_prompt .= "2. 每个片段包含:时长、多个分镜\n";
+        $user_prompt .= "3. 每个分镜包含:分镜序号、场景、画面、运镜、配音台词、背景音效\n";
+        $user_prompt .= "4. 场景必须从【场景列表参考】中选择(如果提供了场景列表)\n";
+        $user_prompt .= "5. 出场角色必须从【主体列表参考】中选择(如果提供了主体列表)\n";
+        $user_prompt .= "6. 保持美术风格的一致性(如果提供了美术风格参考)\n";
+        $user_prompt .= "7. 只需输出分段剧本部分,不要输出其他内容(不要包含###分段剧本标题)\n";
+        
+        // 调用AI生成新的分段剧本
+        try {
+            $ai_result = $this->newGenerateText([
+                'model' => 'deepseek-v4-pro',
+                'system_prompt' => $system_prompt,
+                'prompt' => $user_prompt,
+                'temperature' => 0.7,
+            ]);
+            
+            $generated_content = $ai_result['content'] ?? '';
+            
+            if (empty($generated_content)) {
+                Utils::throwError('20003:AI生成内容为空,请重试');
+            }
+            
+            // 解析生成的内容
+            $parsed_segments = $this->parseSegmentContent($generated_content);
+            
+            if (empty($parsed_segments)) {
+                Utils::throwError('20003:无法解析生成的分段剧本,请检查格式');
+            }
+            
+            // 保存到数据库
+            $now = date('Y-m-d H:i:s');
+            
+            if ($act_id > 0) {
+                // 单个片段模式:更新单条记录
+                if (count($parsed_segments) > 0) {
+                    $new_segment = $parsed_segments[0];
+                    
+                    $update_data = [
+                        'act_content' => $generated_content,
+                        'act_title' => getProp($new_segment, 'act_title', ''),
+                        'act_duration' => getProp($new_segment, 'act_duration', 0),
+                        'updated_at' => $now,
+                    ];
+                    
+                    DB::table('mp_episode_segments')
+                        ->where('id', $act_id)
+                        ->update($update_data);
+                    
+                    dLog('deepseek')->info('单个片段重新生成成功', ['act_id' => $act_id]);
+                }
+                
+            } else {
+                // 整个剧集模式:删除原有记录并批量保存新记录
+                DB::beginTransaction();
+                
+                try {
+                    // 删除原有的所有片段记录
+                    DB::table('mp_episode_segments')
+                        ->where('episode_id', $target_episode_id)
+                        ->delete();
+                    
+                    // 批量插入新的片段记录
+                    $segments_to_insert = [];
+                    foreach ($parsed_segments as $index => $segment) {
+                        $act_number = $index + 1;
+                        
+                        $segments_to_insert[] = [
+                            'anime_id' => $target_anime_id,
+                            'episode_id' => $target_episode_id,
+                            'episode_number' => $target_episode_number,
+                            'act_number' => $act_number,
+                            'act_title' => getProp($segment, 'act_title', ''),
+                            'act_duration' => getProp($segment, 'act_duration', 0),
+                            'act_content' => getProp($segment, 'act_content', ''),
+                            'created_at' => $now,
+                            'updated_at' => $now,
+                        ];
+                    }
+                    
+                    if (!empty($segments_to_insert)) {
+                        DB::table('mp_episode_segments')->insert($segments_to_insert);
+                    }
+                    
+                    DB::commit();
+                    
+                    dLog('deepseek')->info('整个剧集片段重新生成成功', [
+                        'episode_id' => $target_episode_id,
+                        'segments_count' => count($segments_to_insert)
+                    ]);
+                    
+                } catch (\Exception $e) {
+                    DB::rollBack();
+                    dLog('deepseek')->error('保存重新生成的片段失败: ' . $e->getMessage());
+                    Utils::throwError('20003:保存失败,请重试');
+                }
+            }
+            
+            return [
+                'success' => true,
+                'message' => $act_id > 0 ? '单个片段重新生成成功' : '整个剧集片段重新生成成功',
+                'generated_content' => $generated_content,
+                'parsed_segments' => $parsed_segments,
+                'usage' => $ai_result['usage'] ?? [],
+            ];
+            
+        } catch (\Exception $e) {
+            dLog('deepseek')->error('重新生成分段剧本失败: ' . $e->getMessage());
+            throw $e;
+        }
+    }
+
     private function buildSingleEpisodeFromScript(array $script_arr, string $fullContent, string $animeName = ''): array
     {
         $episode_arr = [
@@ -9872,7 +10177,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
             // DeepSeek 官方模型使用 DeepSeek API
             $chatResult = $this->chatOnly($post_data);
-        } else if ($model === 'gpt-5.4') {
+        } else if (in_array($model, $this->gpt_text_models)) {
             // GPT-5.4 模型使用 TokenRouter API
             $chatResult = $this->gpt54ChatOnly($post_data);
         } else {
@@ -10368,7 +10673,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         if (in_array($model, ['deepseek-reasoner', 'deepseek-chat', 'deepseek-v4-flash', 'deepseek-v4-pro'])) {
             // DeepSeek 官方模型使用 DeepSeek API
             $chatResult = $this->chatOnly($post_data);
-        } else if ($model === 'gpt-5.4') {
+        } else if (in_array($model, $this->gpt_text_models)) {
             // GPT-5.4 模型使用 TokenRouter API
             $chatResult = $this->gpt54ChatOnly($post_data);
         } else {