I am facing an issue where the conversations entered by the user and those generated by the AI are not being stored in my Postgres database within my next.js application

Whenever a question is posed to the AI and a response is provided, the issue arises where the information is not getting saved in the database. Despite including console.log statements in the route.ts file indicating that messages from both the AI and the user are being stored in the database, upon refreshing the application, the messages do not appear to be saved.

"use client";
import React, { useEffect } from "react";
import { Input } from "./ui/input";
import { useChat } from "ai/react";
import { Button } from "./ui/button";
import { Send } from "lucide-react";
import MessageList from "./MessageList";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { Message } from "ai";
 
type Props = {chatId: number};

const ChatComponent = ({chatId}: Props) => {

  const { data } = useQuery({
    queryKey: ["chat", chatId],
    queryFn: async () => {
      const response = await axios.post<Message[]>("/api/get-messages", chatId)
      return response.data
    }
  })

  

  const { input, handleInputChange, handleSubmit, messages } = useChat({
    api: '/api/chat',
    body: {
      chatId
    },
    initialMessages: Array.isArray(data) ? data : []
  });

  useEffect(() => {
    const messageContainer = document.getElementById('message-container')
    
    if (messageContainer) {
      messageContainer.scrollTo({
        top: messageContainer.scrollHeight,
        behavior: 'smooth'
      })
    }
  }, [messages]) 

  return (
    <div className="relative max-h-screen overflow-scroll" id='message-container'>
      <div className="sticky top-0 inset-x-0 p-2 bg-white h-fit">
        <h3 className="text-xl font-bold">Chat</h3>
      </div>

      <MessageList messages={messages}/>

      <form
        onSubmit={handleSubmit}
        className="sticky bottom-0 inset-x-0 px-2 py-4 bg-white"
      >
        <div className="flex">
        <Input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask any Question..."
          className="w-full"
        />
        <Button className="bg-blue-600 ml-2 relative ">
          <Send className="w-4 h-4" />
        </Button>
        </div>

      </form>
    </div>
  );
};

export default ChatComponent;
import {Configuration, OpenAIApi} from 'openai-edge'
import {OpenAIStream, StreamingTextResponse, Message} from 'ai'
import { getContext } from '@/lib/context'
import { db } from '@/lib/db'
import { chats, messages as _messages } from '@/lib/db/schema'
import { eq } from 'drizzle-orm'
import { NextResponse } from 'next/server'

//makes it faster when deployed to vercel
export const runtime = 'edge'

const config = new Configuration({
    apiKey: process.env.OPENAI_API_KEY
})

const openai = new OpenAIApi(config)

export async function POST(req: Request) {
    try {
        const { messages, chatId } = await req.json()
        const _chats = await db.select().from(chats).where(eq(chats.id, chatId))
        if (_chats.length !=1) {
            return NextResponse.json({ 'error': 'chat not found' }, { status: 404 } )
        }
        const fileKey = _chats[0].fileKey
        const lastmessage = messages[messages.length - 1]
        const context = await getContext(lastmessage.content, fileKey)

        const prompt = {
            role: "system",
            content: `AI assistant is a brand new, powerful, human-like artificial intelligence.
            The traits of AI include expert knowledge, helpfulness, cleverness, and articulateness.
            AI is a well-behaved and well-mannered individual.
            AI is always friendly, kind, and inspiring, and he is eager to provide vivid and thoughtful responses to the user.
            AI has the sum of all knowledge in their brain, and is able to accurately answer nearly any question about any topic in conversation.
            AI assistant is a big fan of Pinecone and Vercel.
            START CONTEXT BLOCK
            ${context}
            END OF CONTEXT BLOCK
            AI assistant will take into account any CONTEXT BLOCK that is provided in a conversation.
            If the context does not provide the answer to question, the AI assistant will say, "I'm sorry, but I don't know the answer to that question".
            AI assistant will not apologize for previous responses, but instead will indicated new information was gained.
            AI assistant will not invent anything that is not drawn directly from the context.
            `,
          };

        const response = await openai.createChatCompletion({
            model: 'gpt-3.5-turbo',
            messages: [
                prompt,
                ...messages.filter((message: Message) => message.role === 'user')
            ],
            stream: true
        })

        const stream = OpenAIStream(response, {
            onStart: async () => {
                //save user message into db
                await db.insert(_messages).values({
                    chatId,
                    messageContent: lastmessage.content,
                    role: "user"
                }
            )
            console.log("saved user message to db")


            },
            onCompletion: async (completion) => {
                //save ai message into db
                await db.insert(_messages).values({
                    chatId,
                    messageContent: completion,
                    role: "system"
                })

                console.log("saved ai message to db")
            }
        })

        return new StreamingTextResponse(stream)
    } catch (error) {
        console.log('error in /api/chat', error)
    }
}
import {integer, pgEnum, pgTable, serial, text, timestamp, varchar} from 'drizzle-orm/pg-core'

export const userSystemEnum = pgEnum('user_system_enum', ['system', 'user'])

export const chats = pgTable('chats', {
    id: serial('id').primaryKey(),
    pdfName: text('pdf_name').notNull(),
    pdfUrl: text('pdf_url').notNull(),
    createdAt: timestamp('created_at').notNull().defaultNow(),
    userId: varchar('user_id', {length:256}).notNull(),
    fileKey: text('file_key').notNull(),
})

export type DrizzleChat = typeof chats.$inferSelect

export const messages = pgTable('messages', {
    id: serial('id').primaryKey(),
    chatId: integer('chat_id').references(() => chats.id).notNull(),
    messageContent: text('content').notNull(),
    createdAt: timestamp('created_at').notNull().defaultNow(),
    role: userSystemEnum('role').notNull()
})


Answer №1

If you want to handle the completion of the model's response and tool executions, you can use the onFinish callback in the streamText function.

Make sure to check out the example provided by Vercel for reference:

import { openai } from '@ai-sdk/openai';
import { streamText, convertToCoreMessages } from 'ai';

// Set a maximum duration for streaming responses (up to 30 seconds)
export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = await streamText({
    model: openai('gpt-4-turbo'),
    messages: convertToCoreMessages(messages),
    async onFinish({ text, toolCalls, toolResults, usage, finishReason }) {
      // You can define your own storage logic here
      await saveChat({ text, toolCalls, toolResults });
    },
  });

  return result.toDataStreamResponse();
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Declaring module public type definitions for NPM in Typescript: A comprehensive guide

I have recently developed a npm package called observe-object-path, which can be found on GitHub at https://github.com/d6u/observe-object-path. This package is written in Typescript and has a build step that compiles it down to ES5 for compatibility with ...

DiscoverField Component with Button Functionality and Checkbox Dilemma

I'm working with Vue 3, Vuetify, and TypeScript for my searchField component. Inside, I have two buttons: FreeItemMaster and PatternMaster. Clicking on these buttons should change their background color and display respective content below them. Howev ...

Transform a standard array of strings into a union string literal in TypeScript

I'm currently developing a module where users can specify a list of "allowable types" to be utilized in other functions. However, I'm encountering challenges implementing this feature effectively with TypeScript: function initializeModule<T ex ...

Displaying a horizontal scroll bar for legends in ECharts can be achieved by limiting the legend to three lines. If the legend items exceed this limit, they will scroll horizontally to accommodate all items

I am currently utilizing ECharts to display trend data in a line chart format. With 50 different series to showcase, each series comes with its own legend. My objective is to arrange the legends at the top of the chart, while limiting them to a maximum of ...

Having trouble viewing the files that have been uploaded to web3.storage

I am currently in the process of constructing a website using Next js that enables users to upload files to IPFS through web3.storage. After carefully following their documentation at (https://web3.storage/docs/w3up-client/), I have successfully created ac ...

Angular Command Line Interface version 12.2.0 displays the message: "No overload matches this call."

I've been working on developing a shopping cart feature for an Angular project. I'm trying to define a product as a Product type, but I keep encountering an error message stating: "(product: Product) => { ..." The error message rea ...

Leveraging the power of the app folder in conjunction with the pages

Being new to Next.JS, I recently learned that we have the option of organizing our pages in either the app/ or pages/ folder. I am a bit confused because there is a suggestion to utilize a new src/app/ folder instead of the pages/ folder. However, it seem ...

Is there a way to refresh the page in NextJs whenever the parameters are modified without the need for reloading?

Currently, I am navigating on http://localhost:3000/?groupId=chicago&dayOfWeek=tuesday. Despite pressing a button on a component, the desired outcome of transitioning to another day, like Thursday, is not occurring. import { useRouter, usePathname, use ...

What category does a Fresh of Deno event fall under?

I'm currently working with Deno and fresh. When it comes to events in islands, what is the type for an event like the one below? export function Sample() { return ( <input type="file" onChange={(e) => ...} // What typ ...

Retrieving Data from Angular Component within a Directive

Currently, I am in the process of creating an "autocomplete" directive for a project. The aim is to have the directive query the API and present a list of results for selection. A component with a modal containing a simple input box has been set up. The ob ...

What advantages does using a predicate as a return type offer over a simple boolean?

Recently, I stumbled upon the concept of user-defined typeguards while perusing through this enlightening article: https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards One intriguing example presented in the aforementi ...

Avoid circular dependencies in Angular 6 to ensure proper association between modules

When working with TypeScript, how should I handle the scenario where Cat has an owner: Person Person owns a pet: Cat Cat import {Person} from './person' export class Cat { owner: Person constructor(){ this.owner = new Pers ...

Waiting for the response to come by subscribing in Angular

I am encountering an issue while trying to subscribe to an Observable and assign data from the response. The problem is that my code does not wait for the response before executing the console.log(this.newIds) line, resulting in an empty value being logg ...

What is the best way to analyze the current pathname and route in next.js?

Currently in the process of migrating a project developed with creat-react-app to a next.js setup. Encountered an issue while trying to migrate the matchPath function from react-router-dom to achieving a similar functionality in next.js, where we compare ...

What could be causing issues with my application when using server-side rendered styled-components with Next.js?

I am in need of assistance with an error I've encountered. Every time I try to access the homepage of my Next.js app, it breaks and displays a cannot read data map of undefined error. The browser consistently directs me to the _document.js file, but I ...

Tips for Using Typescript Instance Fields to Prevent Undefined Values

After creating a few Typescript classes, I encountered an issue where I would get an undefined error when trying to use them after instantiating. I experimented with initializing my fields in the constructor, which resolved the problem, but I don't t ...

The current enablement status does not support the experimental syntax 'flow' (7:8):

Utilizing a Mono repo to share react native components with a react app has presented some challenges. When attempting to use a react native component from react, an error keeps popping up that I can't seem to resolve. I've attempted to follow t ...

The partial template is not functioning as anticipated

Introducing an interface designed to accept two templates, with the resulting function being a partial of one of them (similar to React-Redux): export type IState<TState, TOwnProps> = { connect: (mapStateToProps: MapStateToProps<TState, Parti ...

Incorporating Bootstrap Modal into a TypeScript-based minimalist web application

I want to incorporate the Bootstrap Modal component into my TypeScript application. Therefore, I added the necessary types: npm i @types/bootstrap After that, in my TypeScript code: import { Modal } from "bootstrap"; const myModal = new Modal(&a ...

Is there a way I can keep my ChartJS constantly updated in real time? Currently, it only seems to update when I zoom in or out

I'm looking to create a dynamic graph that updates every 5 seconds or in real-time. I've managed to get it working when zooming in and out of the page, but not when doing nothing. I know it's possible to achieve this, but I can't seem t ...