Getting started

Let’s build an AI shopping assistant that can analyze real purchases history and make personalized recommendations.

Create an API key for both Subtotal and OpenAI if you haven’t already.

Create a new Next.js project

Create a new Next.js project named with TypeScript and the App Router:

npx create-next-app@latest ai-shopping-assistant --typescript --app

Install dependencies

Install the Subtotal AI Toolkit and OpenAI AI SDK packages:

cd ai-shopping-assistant
npm install @subtotal-inc/ai-toolkit @ai-sdk/openai react-markdown

Configure environment variables

Create a .env.local file and add your API keys:

SUBTOTAL_API_KEY=your_subtotal_api_key
OPENAI_API_KEY=your_openai_api_key

Create the chat interface

Replace the contents of src/app/page.tsx with the following code:

'use client';

import { useChat } from '@ai-sdk/react';
import ReactMarkdown from 'react-markdown';

const Chat = () => {
  const { messages, input, handleInputChange, handleSubmit } = useChat();

  return (
    <div className='flex flex-col w-full h-screen p-8 pt-24 items-center justify-center bg-black'>
      <div className="flex flex-col w-full max-w-[1000px] h-full justify-between items-center space-y-8">
        <ul className='flex flex-col w-full flex-1 overflow-clip p-4 space-y-4'>
          {messages.map(m => (
            <li key={m.id} className='flex flex-col space-y-2 hover:bg-white/5'>
              <span className='text-sm text-white font-semibold'>{m.role === 'user' ? 'You' : 'Shopping Assistant'}</span>
              <span className='text-white/80 text-sm font-light'><ChatMessage message={m.content} /></span>
            </li>
          ))}
        </ul>
          
        <form className='w-full bg-white/5 border border-white/30 rounded-md shadow' onSubmit={handleSubmit}>
          <input
            className="text-white w-full p-4 placeholder:text-white/80 focus:outline-none"
            value={input}
            placeholder={`${messages.length === 0 ? "Say hi to your new shopping assistant..." : "Ask anything..."}`}
            onChange={handleInputChange}
          />
        </form>
      </div>
    </div>
  );
}

export default Chat;

const ChatMessage = ({ message }: { message: string }) => {
  return (
    <ReactMarkdown
      components={{
        a: ({ node, ...props }) => (
          <a {...props} className="text-blue-500 underline" target="_blank" rel="noopener noreferrer">
            {props.children}
          </a>
        ),
      }}
    >
      {message}
    </ReactMarkdown>
  );
};

Integrate with Subtotal and OpenAI

Create a new file src/app/api/chat/route.ts and add the following code:

import {SubtotalAIToolkit, Tools} from '@subtotal-inc/ai-toolkit/ai-sdk';
import {openai} from '@ai-sdk/openai';
import {streamText} from 'ai';

const subtotalAIToolkit = new SubtotalAIToolkit({
  secretKey: process.env.SUBTOTAL_API_KEY!,
  configuration: {
    tools: [
      Tools.createConnection,
      Tools.createMerchantLinkUrl,
      Tools.getPurchases,
      Tools.getPurchaseDetails,
      Tools.getMerchants,
    ]
  },
});

const model = openai('gpt-4o');

const prompt = {
  role: 'system',
  content: 'Greet the user by responding to their message. ' +
  'Then create a new connection and generate a url for the user to link their Target account.' +
  'Indicate that user should click the url and link their account to get started. ' +
  'Wait for a response from the user indicating they have linked their account. '
}

export const maxDuration = 30;

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

  const result = await streamText({
    model: model,
    tools: {...subtotalAIToolkit.getTools()},
    maxSteps: 4,
    messages: [
      prompt,
      ...messages,
    ],
  });

  return result.toDataStreamResponse();
}

Run the project

npm run dev