|
@@ -762,7 +762,7 @@ class DeepSeekService
|
|
|
* @param array $data 请求参数
|
|
* @param array $data 请求参数
|
|
|
* @return \Generator
|
|
* @return \Generator
|
|
|
*/
|
|
*/
|
|
|
- public function newGenerateTextStream($data) {
|
|
|
|
|
|
|
+ public function newGenerateTextStream($data, $existingTask = null) {
|
|
|
$model = getProp($data, 'model', 'deepseek-v4-pro');
|
|
$model = getProp($data, 'model', 'deepseek-v4-pro');
|
|
|
$messages = getProp($data, 'messages', []);
|
|
$messages = getProp($data, 'messages', []);
|
|
|
$systemPrompt = getProp($data, 'system_prompt', '');
|
|
$systemPrompt = getProp($data, 'system_prompt', '');
|
|
@@ -775,14 +775,15 @@ class DeepSeekService
|
|
|
$sequence = getProp($data, 'sequence', 0);
|
|
$sequence = getProp($data, 'sequence', 0);
|
|
|
|
|
|
|
|
$generateTaskId = 0;
|
|
$generateTaskId = 0;
|
|
|
- $uid = Site::getUid();
|
|
|
|
|
- if ($script_id > 0) {
|
|
|
|
|
- $existingTask = DB::table('mp_script_generate_tasks')
|
|
|
|
|
|
|
+ // 异步 worker 场景直接使用任务记录中的用户,避免命令行下无登录上下文
|
|
|
|
|
+ $uid = $existingTask ? (int)$existingTask->uid : (int)Site::getUid();
|
|
|
|
|
+ if ($script_id > 0 && !$existingTask) {
|
|
|
|
|
+ $runningTask = DB::table('mp_script_generate_tasks')
|
|
|
->where('uid', $uid)
|
|
->where('uid', $uid)
|
|
|
->where('script_id', $script_id)
|
|
->where('script_id', $script_id)
|
|
|
->orderByDesc('id')
|
|
->orderByDesc('id')
|
|
|
->first();
|
|
->first();
|
|
|
- if ($existingTask && in_array(getProp($existingTask, 'status'), ['pending', 'processing'])) {
|
|
|
|
|
|
|
+ if ($runningTask && in_array(getProp($runningTask, 'status'), ['pending', 'processing'])) {
|
|
|
Utils::throwError('20003:该剧本资产正在生成中,请稍后重试');
|
|
Utils::throwError('20003:该剧本资产正在生成中,请稍后重试');
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -958,17 +959,22 @@ class DeepSeekService
|
|
|
|
|
|
|
|
// 如果提供了script_id,创建剧本资产生成任务
|
|
// 如果提供了script_id,创建剧本资产生成任务
|
|
|
if ($script_id > 0) {
|
|
if ($script_id > 0) {
|
|
|
- $uid = Site::getUid();
|
|
|
|
|
- $generateTaskId = DB::table('mp_script_generate_tasks')->insertGetId([
|
|
|
|
|
- 'uid' => $uid,
|
|
|
|
|
- 'script_id' => $script_id,
|
|
|
|
|
- 'sequence' => $sequence,
|
|
|
|
|
- 'status' => 'processing',
|
|
|
|
|
- 'prompt' => getProp($data, 'prompt', ''),
|
|
|
|
|
- 'started_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
- 'created_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
- 'updated_at' => date('Y-m-d H:i:s')
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+ if ($existingTask) {
|
|
|
|
|
+ // 异步 worker:接管已创建的 pending 任务,不再新建
|
|
|
|
|
+ $generateTaskId = (int)$existingTask->id;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 同步调用:保持原行为,创建 processing 任务
|
|
|
|
|
+ $generateTaskId = DB::table('mp_script_generate_tasks')->insertGetId([
|
|
|
|
|
+ 'uid' => $uid,
|
|
|
|
|
+ 'script_id' => $script_id,
|
|
|
|
|
+ 'sequence' => $sequence,
|
|
|
|
|
+ 'status' => 'processing',
|
|
|
|
|
+ 'prompt' => getProp($data, 'prompt', ''),
|
|
|
|
|
+ 'started_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ 'created_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s')
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 积分余额预检(仅存在有效剧本时按 chat 类型计费,不足直接报错)
|
|
// 积分余额预检(仅存在有效剧本时按 chat 类型计费,不足直接报错)
|
|
@@ -1033,47 +1039,50 @@ class DeepSeekService
|
|
|
if (!empty($ctx['script_id'])) {
|
|
if (!empty($ctx['script_id'])) {
|
|
|
try {
|
|
try {
|
|
|
$now = now();
|
|
$now = now();
|
|
|
- DB::table('mp_script_records')->insert([
|
|
|
|
|
- [
|
|
|
|
|
- 'uid' => $ctx['uid'],
|
|
|
|
|
- 'script_id' => $ctx['script_id'],
|
|
|
|
|
- 'sequence' => $ctx['sequence'],
|
|
|
|
|
- 'role' => 'user',
|
|
|
|
|
- 'content' => $ctx['original_prompt'],
|
|
|
|
|
- 'created_at' => $now,
|
|
|
|
|
- 'updated_at' => $now,
|
|
|
|
|
- ],
|
|
|
|
|
- [
|
|
|
|
|
- 'uid' => $ctx['uid'],
|
|
|
|
|
- 'script_id' => $ctx['script_id'],
|
|
|
|
|
- 'sequence' => $ctx['sequence'],
|
|
|
|
|
- 'role' => 'assistant',
|
|
|
|
|
- 'content' => $fullContent,
|
|
|
|
|
- 'created_at' => $now,
|
|
|
|
|
- 'updated_at' => $now,
|
|
|
|
|
- ],
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- $chunk['records'] = DB::table('mp_script_records')
|
|
|
|
|
- ->where('uid', $ctx['uid'])
|
|
|
|
|
- ->where('script_id', $ctx['script_id'])
|
|
|
|
|
- ->where('sequence', $ctx['sequence'])
|
|
|
|
|
- ->orderBy('id', 'desc')
|
|
|
|
|
- ->limit(2)
|
|
|
|
|
- ->get()
|
|
|
|
|
- ->map(function ($record) {
|
|
|
|
|
- return [
|
|
|
|
|
- 'rid' => $record->id,
|
|
|
|
|
- 'script_id' => $record->script_id,
|
|
|
|
|
- 'sequence' => $record->sequence,
|
|
|
|
|
- 'role' => $record->role,
|
|
|
|
|
- 'content' => $record->content,
|
|
|
|
|
- 'created_at' => $record->created_at,
|
|
|
|
|
- ];
|
|
|
|
|
- })
|
|
|
|
|
- ->reverse()
|
|
|
|
|
- ->values()
|
|
|
|
|
- ->toArray();
|
|
|
|
|
|
|
+ // 两条对话记录(user/assistant)在同一事务内写入,保证原子性
|
|
|
|
|
+ $chunk['records'] = DB::transaction(function () use ($ctx, $fullContent, $now) {
|
|
|
|
|
+ DB::table('mp_script_records')->insert([
|
|
|
|
|
+ [
|
|
|
|
|
+ 'uid' => $ctx['uid'],
|
|
|
|
|
+ 'script_id' => $ctx['script_id'],
|
|
|
|
|
+ 'sequence' => $ctx['sequence'],
|
|
|
|
|
+ 'role' => 'user',
|
|
|
|
|
+ 'content' => $ctx['original_prompt'],
|
|
|
|
|
+ 'created_at' => $now,
|
|
|
|
|
+ 'updated_at' => $now,
|
|
|
|
|
+ ],
|
|
|
|
|
+ [
|
|
|
|
|
+ 'uid' => $ctx['uid'],
|
|
|
|
|
+ 'script_id' => $ctx['script_id'],
|
|
|
|
|
+ 'sequence' => $ctx['sequence'],
|
|
|
|
|
+ 'role' => 'assistant',
|
|
|
|
|
+ 'content' => $fullContent,
|
|
|
|
|
+ 'created_at' => $now,
|
|
|
|
|
+ 'updated_at' => $now,
|
|
|
|
|
+ ],
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ return DB::table('mp_script_records')
|
|
|
|
|
+ ->where('uid', $ctx['uid'])
|
|
|
|
|
+ ->where('script_id', $ctx['script_id'])
|
|
|
|
|
+ ->where('sequence', $ctx['sequence'])
|
|
|
|
|
+ ->orderBy('id', 'desc')
|
|
|
|
|
+ ->limit(2)
|
|
|
|
|
+ ->get()
|
|
|
|
|
+ ->map(function ($record) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'rid' => $record->id,
|
|
|
|
|
+ 'script_id' => $record->script_id,
|
|
|
|
|
+ 'sequence' => $record->sequence,
|
|
|
|
|
+ 'role' => $record->role,
|
|
|
|
|
+ 'content' => $record->content,
|
|
|
|
|
+ 'created_at' => $record->created_at,
|
|
|
|
|
+ ];
|
|
|
|
|
+ })
|
|
|
|
|
+ ->reverse()
|
|
|
|
|
+ ->values()
|
|
|
|
|
+ ->toArray();
|
|
|
|
|
+ });
|
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
|
dLog('deepseek')->error('保存剧本对话记录失败: ' . $e->getMessage());
|
|
dLog('deepseek')->error('保存剧本对话记录失败: ' . $e->getMessage());
|
|
|
$chunk['records'] = [];
|
|
$chunk['records'] = [];
|
|
@@ -1084,15 +1093,28 @@ class DeepSeekService
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if ($finished) {
|
|
if ($finished) {
|
|
|
- if (!empty($ctx['generate_task_id'])) {
|
|
|
|
|
- DB::table('mp_script_generate_tasks')->where('id', $ctx['generate_task_id'])->update([
|
|
|
|
|
- 'status' => 'success',
|
|
|
|
|
- 'result' => $fullContent,
|
|
|
|
|
- 'completed_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
- 'updated_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+ // 任务状态与扣费在同一事务内:扣费失败则任务状态一并回滚,由外层 catch 置为 failed
|
|
|
|
|
+ DB::transaction(function () use ($ctx, $fullContent, $usage) {
|
|
|
|
|
+ if (!empty($ctx['generate_task_id'])) {
|
|
|
|
|
+ DB::table('mp_script_generate_tasks')->where('id', $ctx['generate_task_id'])->update([
|
|
|
|
|
+ 'status' => 'success',
|
|
|
|
|
+ 'result' => $fullContent,
|
|
|
|
|
+ 'completed_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+ if ($ctx['has_valid_script'] && $fullContent !== '') {
|
|
|
|
|
+ $this->chargeChatSuccess($ctx['uid'], $this->pointsService->getTokensFromUsage($usage), [
|
|
|
|
|
+ 'model' => $ctx['model'],
|
|
|
|
|
+ 'script_id' => $ctx['script_id'],
|
|
|
|
|
+ 'sequence' => $ctx['sequence'],
|
|
|
|
|
+ 'source' => 'newGenerateText',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- // JSON 输出结果的安全校验/修复与观察(不改变 result 与 SSE 推送内容)
|
|
|
|
|
|
|
+ // JSON 输出结果的安全校验/修复与观察(事务外执行,不改变 result 与 SSE 推送内容)
|
|
|
|
|
+ if (!empty($ctx['generate_task_id'])) {
|
|
|
$this->guardScriptGenerateJsonResult(
|
|
$this->guardScriptGenerateJsonResult(
|
|
|
(int)$ctx['generate_task_id'],
|
|
(int)$ctx['generate_task_id'],
|
|
|
(int)($ctx['script_id'] ?? 0),
|
|
(int)($ctx['script_id'] ?? 0),
|
|
@@ -1101,14 +1123,6 @@ class DeepSeekService
|
|
|
(string)($ctx['response_format'] ?? '')
|
|
(string)($ctx['response_format'] ?? '')
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
- if ($ctx['has_valid_script'] && $fullContent !== '') {
|
|
|
|
|
- $this->chargeChatSuccess($ctx['uid'], $this->pointsService->getTokensFromUsage($usage), [
|
|
|
|
|
- 'model' => $ctx['model'],
|
|
|
|
|
- 'script_id' => $ctx['script_id'],
|
|
|
|
|
- 'sequence' => $ctx['sequence'],
|
|
|
|
|
- 'source' => 'newGenerateText',
|
|
|
|
|
- ]);
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
} catch (\Throwable $e) {
|
|
} catch (\Throwable $e) {
|
|
|
if (!empty($ctx['generate_task_id'])) {
|
|
if (!empty($ctx['generate_task_id'])) {
|
|
@@ -1230,6 +1244,175 @@ class DeepSeekService
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
|
+ * 创建剧本资产生成异步任务(stream=1 + script_id 走异步任务模式)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array $data 原请求参数
|
|
|
|
|
+ * @return int 任务ID
|
|
|
|
|
+ */
|
|
|
|
|
+ public function createScriptGenerateAsyncTask(array $data): int
|
|
|
|
|
+ {
|
|
|
|
|
+ $script_id = (int)getProp($data, 'script_id', 0);
|
|
|
|
|
+ if ($script_id <= 0) {
|
|
|
|
|
+ Utils::throwError('20003:缺少剧本ID');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $uid = (int)Site::getUid();
|
|
|
|
|
+
|
|
|
|
|
+ // 与同步逻辑一致:同一剧本存在生成中任务时直接拒绝
|
|
|
|
|
+ $runningTask = DB::table('mp_script_generate_tasks')
|
|
|
|
|
+ ->where('uid', $uid)
|
|
|
|
|
+ ->where('script_id', $script_id)
|
|
|
|
|
+ ->orderByDesc('id')
|
|
|
|
|
+ ->first();
|
|
|
|
|
+ if ($runningTask && in_array(getProp($runningTask, 'status'), ['pending', 'processing'])) {
|
|
|
|
|
+ Utils::throwError('20003:该剧本资产正在生成中,请稍后重试');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 前置校验剧本存在且内容非空,避免创建无效任务
|
|
|
|
|
+ $script = DB::table('mp_scripts')
|
|
|
|
|
+ ->where('id', $script_id)
|
|
|
|
|
+ ->where('is_deleted', 0)
|
|
|
|
|
+ ->first();
|
|
|
|
|
+ if (!$script) {
|
|
|
|
|
+ Utils::throwError('20003:剧本不存在或已删除');
|
|
|
|
|
+ }
|
|
|
|
|
+ if (empty($script->content)) {
|
|
|
|
|
+ Utils::throwError('20003:剧本内容为空');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // newGenerateText 请求不含上传文件对象,images/image_urls 以字符串形式传入,可直接序列化
|
|
|
|
|
+ $paramsJson = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
|
|
|
+ if ($paramsJson === false) {
|
|
|
|
|
+ Utils::throwError('20003:请求参数序列化失败');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $now = date('Y-m-d H:i:s');
|
|
|
|
|
+ return (int)DB::table('mp_script_generate_tasks')->insertGetId([
|
|
|
|
|
+ 'uid' => $uid,
|
|
|
|
|
+ 'script_id' => $script_id,
|
|
|
|
|
+ 'sequence' => (int)getProp($data, 'sequence', 0),
|
|
|
|
|
+ 'status' => 'pending',
|
|
|
|
|
+ 'prompt' => (string)getProp($data, 'prompt', ''),
|
|
|
|
|
+ 'request_params' => $paramsJson,
|
|
|
|
|
+ 'started_at' => $now,
|
|
|
|
|
+ 'created_at' => $now,
|
|
|
|
|
+ 'updated_at' => $now,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 供 SSE 轮询:查询任务状态与增量 chunks
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param int $taskId
|
|
|
|
|
+ * @param int $uid 当前登录用户,防越权
|
|
|
|
|
+ * @param int $afterSeq 已推送的最大 seq
|
|
|
|
|
+ * @return array{found:bool, task:?object, chunks:array, status:?string, error_message:?string}
|
|
|
|
|
+ */
|
|
|
|
|
+ public function pollScriptGenerateTask(int $taskId, int $uid, int $afterSeq = -1): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $task = DB::table('mp_script_generate_tasks')
|
|
|
|
|
+ ->where('id', $taskId)
|
|
|
|
|
+ ->where('uid', $uid)
|
|
|
|
|
+ ->first();
|
|
|
|
|
+ if (!$task) {
|
|
|
|
|
+ return ['found' => false, 'task' => null, 'chunks' => [], 'status' => null, 'error_message' => null];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $rows = DB::table('mp_script_generate_task_chunks')
|
|
|
|
|
+ ->where('task_id', $taskId)
|
|
|
|
|
+ ->where('seq', '>', $afterSeq)
|
|
|
|
|
+ ->orderBy('seq')
|
|
|
|
|
+ ->get();
|
|
|
|
|
+
|
|
|
|
|
+ $chunks = [];
|
|
|
|
|
+ foreach ($rows as $row) {
|
|
|
|
|
+ $payload = json_decode((string)$row->payload, true);
|
|
|
|
|
+ if (is_array($payload)) {
|
|
|
|
|
+ $chunks[] = $payload;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'found' => true,
|
|
|
|
|
+ 'task' => $task,
|
|
|
|
|
+ 'chunks' => $chunks,
|
|
|
|
|
+ 'status' => (string)$task->status,
|
|
|
|
|
+ 'error_message' => (string)($task->error_message ?? ''),
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 异步 worker 执行入口:抢占 pending 任务并流式请求大模型
|
|
|
|
|
+ *
|
|
|
|
|
+ * 由 text:generate:run 命令调用;worker 内完成 chunks 落库,模型结束后
|
|
|
|
|
+ * 由流式包装(wrapNewGenerateTextStream)完成 records/扣费/任务状态/json 修复收尾。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param int $taskId
|
|
|
|
|
+ * @return array{ok:bool, task_id:int, status:?string, seq:int, error:?string}
|
|
|
|
|
+ */
|
|
|
|
|
+ public function runScriptGenerateTaskAsync(int $taskId): array
|
|
|
|
|
+ {
|
|
|
|
|
+ // 原子抢占:只允许 pending -> processing,避免重复执行
|
|
|
|
|
+ $claimed = DB::table('mp_script_generate_tasks')
|
|
|
|
|
+ ->where('id', $taskId)
|
|
|
|
|
+ ->where('status', 'pending')
|
|
|
|
|
+ ->update([
|
|
|
|
|
+ 'status' => 'processing',
|
|
|
|
|
+ 'started_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ if (!$claimed) {
|
|
|
|
|
+ return ['ok' => false, 'task_id' => $taskId, 'status' => null, 'seq' => 0, 'error' => '任务已被处理或状态异常'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $task = DB::table('mp_script_generate_tasks')->where('id', $taskId)->first();
|
|
|
|
|
+ if (!$task) {
|
|
|
|
|
+ return ['ok' => false, 'task_id' => $taskId, 'status' => null, 'seq' => 0, 'error' => '任务不存在'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $params = json_decode((string)getProp($task, 'request_params', ''), true);
|
|
|
|
|
+ if (!is_array($params)) {
|
|
|
|
|
+ DB::table('mp_script_generate_tasks')->where('id', $taskId)->update([
|
|
|
|
|
+ 'status' => 'failed',
|
|
|
|
|
+ 'error_message' => '任务请求参数缺失',
|
|
|
|
|
+ 'completed_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ return ['ok' => false, 'task_id' => $taskId, 'status' => 'failed', 'seq' => 0, 'error' => '任务请求参数缺失'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 兼容旧参数未带 stream 的情况
|
|
|
|
|
+ $params['stream'] = 1;
|
|
|
|
|
+
|
|
|
|
|
+ $seq = 0;
|
|
|
|
|
+ try {
|
|
|
|
|
+ $generator = $this->newGenerateTextStream($params, $task);
|
|
|
|
|
+ foreach ($generator as $chunk) {
|
|
|
|
|
+ DB::table('mp_script_generate_task_chunks')->insert([
|
|
|
|
|
+ 'task_id' => $taskId,
|
|
|
|
|
+ 'seq' => $seq,
|
|
|
|
|
+ 'chunk_type' => (string)getProp($chunk, 'type', 'content'),
|
|
|
|
|
+ 'payload' => json_encode($chunk, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE),
|
|
|
|
|
+ 'created_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ $seq++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $finalStatus = (string)DB::table('mp_script_generate_tasks')->where('id', $taskId)->value('status');
|
|
|
|
|
+ return ['ok' => $finalStatus === 'success', 'task_id' => $taskId, 'status' => $finalStatus, 'seq' => $seq, 'error' => null];
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ // 兜底:正常流程中 wrap 会自行把任务置为 failed,这里防止未知异常导致任务卡死
|
|
|
|
|
+ DB::table('mp_script_generate_tasks')->where('id', $taskId)->update([
|
|
|
|
|
+ 'status' => 'failed',
|
|
|
|
|
+ 'error_message' => $e->getMessage(),
|
|
|
|
|
+ 'completed_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ return ['ok' => false, 'task_id' => $taskId, 'status' => 'failed', 'seq' => $seq, 'error' => $e->getMessage()];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
* 通用文生文方法(非流式版本)- 支持多模型、图片输入、JSON输出和模板提示词
|
|
* 通用文生文方法(非流式版本)- 支持多模型、图片输入、JSON输出和模板提示词
|
|
|
*
|
|
*
|
|
|
* @param array $data 请求参数
|
|
* @param array $data 请求参数
|