feat: AIChat 全链路调试日志 + 错误详情展示

load/createSession/loadSession/send 都加了 print 日志:
- scope 参数
- 请求成功/失败状态
- 具体错误信息

sessionError 现在显示具体错误原因而非通用"创建对话失败"

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-06 18:19:30 +08:00
parent 9d0f426118
commit 99dc2807be
2 changed files with 32 additions and 6 deletions

View File

@ -353,7 +353,15 @@ class RagChatService {
title: nil, title: nil,
forceCreate: forceCreate ? true : nil forceCreate: forceCreate ? true : nil
) )
return try await client.request("/rag-chat/sessions", method: "POST", body: body) print("[RagChat] POST /rag-chat/sessions — scopeType=\(body.scopeType), scopeId=\(body.scopeId ?? "nil"), parentKB=\(body.parentKnowledgeBaseId ?? "nil"), forceCreate=\(forceCreate)")
do {
let session = try await client.request("/rag-chat/sessions", method: "POST", body: body) as ChatSession
print("[RagChat] createSession OK — id=\(session.id), scopeType=\(session.scopeType ?? "nil")")
return session
} catch {
print("[RagChat] createSession FAILED: \(error)")
throw error
}
} }
func listSessions(scopeType: String? = nil, scopeId: String? = nil, parentKnowledgeBaseId: String? = nil) async throws -> [ChatSession] { func listSessions(scopeType: String? = nil, scopeId: String? = nil, parentKnowledgeBaseId: String? = nil) async throws -> [ChatSession] {

View File

@ -74,12 +74,18 @@ final class AIChatViewModel: ObservableObject {
createdFrom: "global_ai_entry" createdFrom: "global_ai_entry"
) )
print("[AIChat] load() entry — scopeType=\(ctx.scopeType.rawValue), scopeId=\(ctx.scopeId ?? "nil"), parentKB=\(ctx.parentKnowledgeBaseId ?? "nil")")
do { do {
// open-or-create: backend handles find-or-create logic
let session = try await RagChatService.shared.createSession(ctx: ctx) let session = try await RagChatService.shared.createSession(ctx: ctx)
print("[AIChat] open-or-create OK — sessionId=\(session.id), isNew=\(session.createdAt == session.updatedAt)")
await loadSession(session.id) await loadSession(session.id)
} catch { } catch {
sessionError = "创建对话失败" print("[AIChat] open-or-create FAILED: \(error)")
if let apiErr = error as? APIError {
print("[AIChat] APIError detail: \(apiErr)")
}
sessionError = "创建对话失败: \(error.localizedDescription)"
isCreatingSession = false isCreatingSession = false
} }
} }
@ -89,7 +95,6 @@ final class AIChatViewModel: ObservableObject {
messages = [] messages = []
isCreatingSession = true isCreatingSession = true
// Use entry context if available, otherwise default to global
let ctx = entryContext ?? ChatEntryContext( let ctx = entryContext ?? ChatEntryContext(
scopeType: .global, scopeType: .global,
scopeId: nil, scopeId: nil,
@ -98,14 +103,18 @@ final class AIChatViewModel: ObservableObject {
createdFrom: "global_ai_entry" createdFrom: "global_ai_entry"
) )
print("[AIChat] createNewSession() — scopeType=\(ctx.scopeType.rawValue), scopeId=\(ctx.scopeId ?? "nil")")
do { do {
let session = try await RagChatService.shared.createSession(ctx: ctx, forceCreate: true) let session = try await RagChatService.shared.createSession(ctx: ctx, forceCreate: true)
print("[AIChat] forceCreate OK — sessionId=\(session.id)")
sessionId = session.id sessionId = session.id
let scopeLabel = ctx.scopeType != .global ? "\(ctx.scopeName)」的" : "" let scopeLabel = ctx.scopeType != .global ? "\(ctx.scopeName)」的" : ""
messages = [AIMessage(role: .ai, content: "你好!我是你的 AI 学习助手,基于\(scopeLabel)内容回答问题。")] messages = [AIMessage(role: .ai, content: "你好!我是你的 AI 学习助手,基于\(scopeLabel)内容回答问题。")]
isCreatingSession = false isCreatingSession = false
} catch { } catch {
sessionError = "创建对话失败" print("[AIChat] forceCreate FAILED: \(error)")
sessionError = "创建对话失败: \(error.localizedDescription)"
isCreatingSession = false isCreatingSession = false
} }
} }
@ -113,6 +122,7 @@ final class AIChatViewModel: ObservableObject {
func send() { func send() {
guard canSend, let sid = sessionId else { return } guard canSend, let sid = sessionId else { return }
let text = inputText.trimmingCharacters(in: .whitespacesAndNewlines) let text = inputText.trimmingCharacters(in: .whitespacesAndNewlines)
print("[AIChat] send — sessionId=\(sid), text=\(text.prefix(50))")
messages.append(AIMessage(role: .user, content: text)) messages.append(AIMessage(role: .user, content: text))
inputText = "" inputText = ""
isSending = true isSending = true
@ -139,10 +149,12 @@ final class AIChatViewModel: ObservableObject {
messages[idx] = AIMessage(role: .ai, content: reply, thinkingContent: thinking, isStreaming: true, id: placeholderId) messages[idx] = AIMessage(role: .ai, content: reply, thinkingContent: thinking, isStreaming: true, id: placeholderId)
} }
case "error": case "error":
print("[AIChat] stream error: \(chunk.content ?? "unknown")")
if let idx = messages.firstIndex(where: { $0.id == placeholderId }) { if let idx = messages.firstIndex(where: { $0.id == placeholderId }) {
messages[idx] = AIMessage(role: .ai, content: "请求失败: \(chunk.content ?? "未知错误")") messages[idx] = AIMessage(role: .ai, content: "请求失败: \(chunk.content ?? "未知错误")")
} }
case "done": case "done":
print("[AIChat] stream done — reply=\(reply.count)chars, thinking=\(thinking.count)chars")
if let idx = messages.firstIndex(where: { $0.id == placeholderId }) { if let idx = messages.firstIndex(where: { $0.id == placeholderId }) {
messages[idx] = AIMessage(role: .ai, content: reply, thinkingContent: thinking.isEmpty ? nil : thinking) messages[idx] = AIMessage(role: .ai, content: reply, thinkingContent: thinking.isEmpty ? nil : thinking)
} }
@ -150,6 +162,7 @@ final class AIChatViewModel: ObservableObject {
} }
} }
} catch { } catch {
print("[AIChat] stream FAILED: \(error)")
if let idx = messages.firstIndex(where: { $0.id == placeholderId }) { if let idx = messages.firstIndex(where: { $0.id == placeholderId }) {
messages[idx] = AIMessage(role: .ai, content: "发送失败: \(error.localizedDescription)") messages[idx] = AIMessage(role: .ai, content: "发送失败: \(error.localizedDescription)")
} }
@ -164,7 +177,9 @@ final class AIChatViewModel: ObservableObject {
sessionId = id sessionId = id
isCreatingSession = true isCreatingSession = true
do { do {
print("[AIChat] loadSession — id=\(id)")
let msgs: [ChatMessage] = try await RagChatService.shared.getMessages(sessionId: id) let msgs: [ChatMessage] = try await RagChatService.shared.getMessages(sessionId: id)
print("[AIChat] loadSession OK — \(msgs.count) messages")
// Update entryContext from the first message's scope snapshot (if available) // Update entryContext from the first message's scope snapshot (if available)
if let snapshot = msgs.first?.scopeSnapshot, if let snapshot = msgs.first?.scopeSnapshot,
@ -191,7 +206,10 @@ final class AIChatViewModel: ObservableObject {
AIMessage(role: m.role == "user" ? .user : .ai, content: m.content, citations: m.citations) AIMessage(role: m.role == "user" ? .user : .ai, content: m.content, citations: m.citations)
} }
} }
} catch { sessionError = "加载对话失败" } } catch {
print("[AIChat] loadSession FAILED: \(error)")
sessionError = "加载对话失败: \(error.localizedDescription)"
}
isCreatingSession = false isCreatingSession = false
} }
} }