main: 增强工具功能与消息处理

- 添加 `FileReadTool`,支持文件内容读取与安全验证
- 引入 `hasToolMessages` 逻辑,优化工具历史上下文处理
- 修改工具选项逻辑,支持禁用工具时的动态调整
- 增加消息序列化逻辑,优化 Redis 序列管理与数据同步
- 扩展测试覆盖,验证序列化与工具调用场景
- 增强 Docker Compose 脚本,支持应用重置与日志清理
- 调整工具调用超时设置,提升运行时用户体验
This commit is contained in:
2025-12-24 00:55:54 +08:00
parent 71226c255b
commit e956df9daa
24 changed files with 741 additions and 38 deletions

View File

@@ -16,6 +16,9 @@ use Illuminate\Support\Str;
class ChatService
{
public function __construct(private readonly MessageSequence $messageSequence)
{
}
/**
* 创建一个新聊天会话。
@@ -88,41 +91,55 @@ class ChatService
}
}
$newSeq = $session->last_seq + 1;
$attempts = 0;
while (true) {
$attempts++;
$newSeq = $this->messageSequence->nextForSession($session);
$message = new Message([
'message_id' => (string) Str::uuid(),
'session_id' => $session->session_id,
'role' => $dto['role'],
'type' => $dto['type'],
'content' => $dto['content'] ?? null,
'payload' => $dto['payload'] ?? null,
'reply_to' => $dto['reply_to'] ?? null,
'dedupe_key' => $dedupeKey,
'seq' => $newSeq,
'created_at' => now(),
]);
$message = new Message([
'message_id' => (string) Str::uuid(),
'session_id' => $session->session_id,
'role' => $dto['role'],
'type' => $dto['type'],
'content' => $dto['content'] ?? null,
'payload' => $dto['payload'] ?? null,
'reply_to' => $dto['reply_to'] ?? null,
'dedupe_key' => $dedupeKey,
'seq' => $newSeq,
'created_at' => now(),
]);
try {
if ($save) {
$message->save();
}
$isNew = true;
} catch (QueryException $e) {
if ($this->isUniqueConstraint($e) && $dedupeKey) {
$existing = Message::query()
->where('session_id', $session->session_id)
->where('dedupe_key', $dedupeKey)
->first();
if ($existing) {
$messageRef = $existing;
$wasDeduped = true;
return;
try {
if ($save) {
$message->save();
}
}
$isNew = true;
break;
} catch (QueryException $e) {
if ($this->isUniqueConstraint($e) && $dedupeKey) {
$existing = Message::query()
->where('session_id', $session->session_id)
->where('dedupe_key', $dedupeKey)
->first();
throw $e;
if ($existing) {
$messageRef = $existing;
$wasDeduped = true;
return;
}
}
if ($this->isUniqueConstraint($e) && $this->isSeqUniqueConstraint($e) && $attempts < 3) {
$maxPersistedSeq = (int) (Message::query()
->where('session_id', $session->session_id)
->max('seq') ?? 0);
$this->messageSequence->syncToAtLeast($session->session_id, max($session->last_seq, $maxPersistedSeq));
continue;
}
throw $e;
}
}
$session->update([
@@ -327,4 +344,11 @@ class ChatService
return $sqlState === '23505';
}
private function isSeqUniqueConstraint(QueryException $e): bool
{
$details = $e->errorInfo[2] ?? $e->getMessage();
return is_string($details) && str_contains($details, 'messages_session_id_seq_unique');
}
}