saas-starter-kit icon indicating copy to clipboard operation
saas-starter-kit copied to clipboard

OpenAI API integration

Open rambalachandran opened this issue 5 months ago • 1 comments

OpenAI API integration will be useful to get them deployed into the SaaS Solution built on top of this starter kit

rambalachandran avatar Jan 31 '24 00:01 rambalachandran

@rambalachandran What do you mean by integration for doing what ?

You can create a chat OpenAI integration with those steps

  • [ ] understand the structure of boxyhq/saas-starter-kit
  • [ ] create file pages\teams\[slug]\openai.tsx
  • [ ] create file pages\api\openai.ts
  • [ ] add to next.config.js , env: { OPENAI_API_SECRET_KEY: process.env.OPENAI_API_SECRET_KEY,},
  • [ ] add OPENAI_API_SECRET_KEY to .env
  • [ ] add new MenuItem to components\shared\shell\TeamNavigation.tsx
  • [ ] adapt the solution to your app by enhancing source code and adding your stuff

File content

pages\teams[slug]\openai.tsx

import axios from 'axios';
import { useTranslation } from 'next-i18next';
import React, { useCallback, useEffect, useRef, useState } from 'react';

type HistoryItem = {
  role: 'user' | 'assistant';
  content: string;
};

const handleMessageSubmit = async (
  message: string,
  setLoading: React.Dispatch<React.SetStateAction<boolean>>,
  setHistory: React.Dispatch<React.SetStateAction<HistoryItem[]>>,
  setMessage: React.Dispatch<React.SetStateAction<string>>
) => {
  setLoading(true);
  try {
    const { data } = await axios.post('/api/openai', { prompt: message });
    setHistory((oldHistory) => [
      ...oldHistory,
      { role: 'user', content: message },
      { role: 'assistant', content: data.message },
    ]);
    setMessage('');
  } catch (error) {
    alert('An error occurred');
  } finally {
    setLoading(false);
  }
};

const OpenAIChat: React.FC = () => {
  const { t } = useTranslation('common');
  const [message, setMessage] = useState('');
  const [history, setHistory] = useState<HistoryItem[]>([]);
  const [loading, setLoading] = useState(false);
  const callbackRef = useRef<() => void>();

  useEffect(() => {
    callbackRef.current = () => {
      handleMessageSubmit(message, setLoading, setHistory, setMessage);
    };
  });

  const handleInputChange = useCallback(
    (event) => setMessage(event.target.value),
    []
  );

  const handleFormSubmit = useCallback((event) => {
    event.preventDefault();
    callbackRef.current && callbackRef.current();
  }, []);

  return (
    <div className="container mx-auto py-4 px-4">
      <ul>
        {history.map((item, index) => (
          <li
            key={`history-item-${index}`}
            className="my-2 p-2 rounded bg-gray-200 dark:bg-gray-700"
          >
            <strong>{item.role}:</strong> {item.content}
          </li>
        ))}
      </ul>
      <form
        onSubmit={handleFormSubmit}
        className="mt-4 flex items-center justify-between"
      >
        <input
          type="text"
          value={message}
          onChange={handleInputChange}
          disabled={loading}
          className="flex-1 border p-2 rounded mr-2"
        />
        <button
          type="submit"
          className="btn bg-blue-500 text-white px-4 py-2 rounded"
        >
          {t('Send')}
        </button>
      </form>
    </div>
  );
};

export default OpenAIChat;

pages\api\openai.ts

import { NextApiRequest, NextApiResponse } from 'next';
import { OpenAI } from 'openai';

const apiKey = process.env.OPENAI_API_SECRET_KEY;

if (!apiKey) {
  throw new Error(
    'OPENAI_API_SECRET_KEY must be declared in your environment variables'
  );
}

const openai = new OpenAI({
  apiKey: apiKey,
});

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === 'POST') {
    const { prompt } = req.body;

    try {
      const response = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [
          {
            role: 'system',
            content: 'You are chatting with Assistant.',
          },
          {
            role: 'user',
            content: prompt,
          },
        ],
      });

      const extractedResponseMessage = response.choices[0].message.content;
      return res.status(200).json({ message: extractedResponseMessage });
    } catch (error) {
      console.error('Error:', error);
      return res
        .status(500)
        .json({ error: 'Failed to fetch response from OpenAI.' });
    }
  } else {
    return res
      .status(405)
      .json({ error: 'Method not allowed. Please use POST.' });
  }
}

Results

image

KazeroG avatar Feb 17 '24 07:02 KazeroG

Thank you for the detailed response. This appears sufficient for now. I will close the ticket

rambalachandran avatar Apr 09 '24 00:04 rambalachandran