(targetChatId: string, message: string)
| 81 | useEffect(() => { |
| 82 | setCurrentChatId(chatId); |
| 83 | }, [chatId]); |
| 84 | |
| 85 | const [saveMessage] = useMutation(SAVE_MESSAGE); |
| 86 | |
| 87 | const [createChat] = useMutation(CREATE_CHAT, { |
| 88 | onCompleted: async (data) => { |
| 89 | const newChatId = data.createChat.id; |
| 90 | setCurrentChatId(newChatId); |
| 91 | await handleChatResponse(newChatId, input, pendingImagesRef.current); |
| 92 | window.history.pushState({}, '', `/chat?id=${newChatId}`); |
| 93 | logger.info(`new chat: ${newChatId}`); |
| 94 | }, |
| 95 | onError: () => { |
| 96 | toast.error('Failed to create chat'); |
| 97 | setLoadingSubmit(false); |
| 98 | }, |
| 99 | }); |
| 100 | |
| 101 | const handleChatResponse = async ( |
| 102 | targetChatId: string, |
| 103 | message: string, |
| 104 | images?: string[], |
| 105 | // The first turn of a project chat arrives with its user message already |
| 106 | // stored server-side (createProject saves it); re-saving would double it. |
| 107 | saveUser = true, |
| 108 | // The chat's own stored model. State-held selectedModel lags one render |
| 109 | // behind, so the auto-started first turn passes this directly. |
| 110 | model?: string |
| 111 | ) => { |
| 112 | const replyId = `${targetChatId}-${Date.now()}`; |
| 113 | // Outside the try so the catch can tell "failed before anything streamed" |
| 114 | // (safe to retry) from "died mid-turn" (retrying would rerun real work). |
| 115 | const steps: TurnStep[] = []; |
| 116 | try { |
| 117 | setInput(''); |
| 118 | const userInput: ChatInputType = { |
| 119 | chatId: targetChatId, |
| 120 | message, |
| 121 | model: model ?? selectedModel, |
no test coverage detected