Skip to content
openai-chatkit-skill logo

OpenAI ChatKit Skill

openai-chatkit-skill

Integrate OpenAI ChatKit for conversational AI interface

SKILL.md

Full skill instructions

OpenAI ChatKit Skill

Instructions

This skill provides guidance for integrating OpenAI's ChatKit interface for conversational AI experiences.

Project Structure

src/
├── components/
│   └── chat/
│       ├── ChatWindow.tsx      # Main chat container
│       ├── ChatMessage.tsx     # Individual message display
│       ├── ChatInput.tsx       # User input area
│       ├── ChatToolbar.tsx     # Quick actions toolbar
│       └── typing-indicator.tsx
├── hooks/
│   └── useChat.ts              # Chat state management
├── lib/
│   └── openai.ts               # OpenAI client configuration
└── types/
    └── chat.ts                 # Chat type definitions

Type Definitions

// src/​types/​chat.ts
export interface ChatMessage {
  id: string
  role: 'user' | 'assistant' | 'system'
  content: string
  timestamp: Date
  tools?: ToolCall[]
}

export interface ToolCall {
  id: string
  name: string
  arguments: Record<string, unknown>
  result?: unknown
}

export interface ChatState {
  messages: ChatMessage[]
  isLoading: boolean
  error: string | null
}

export interface UseChatReturn extends ChatState {
  sendMessage: (content: string) => Promise<void>
  clearMessages: () => void
  retry: () => Promise<void>
}

OpenAI Client Setup

// src/​lib/​openai.ts
import OpenAI from 'openai'

export const openai = new OpenAI({
  apiKey: process.env.NEXT_PUBLIC_OPENAI_API_KEY || process.env.OPENAI_API_KEY,
})

export interface ChatCompletionOptions {
  model?: string
  temperature?: number
  max_tokens?: number
  stream?: boolean
}

export async function createChatCompletion(
  messages: ChatMessage[],
  options: ChatCompletionOptions = {}
) {
  const response = await openai.chat.completions.create({
    model: options.model || 'gpt-4o',
    messages: messages.map(m => ({
      role: m.role,
      content: m.content,
    })),
    temperature: options.temperature ?? 0.7,
    max_tokens: options.max_tokens ?? 1024,
    stream: options.stream ?? false,
    tools: getToolDefinitions(),
  })

  return response
}

// Tool definitions for the AI
export function getToolDefinitions() {
  return [
    {
      type: 'function',
      function: {
        name: 'get_tasks',
        description: 'Get all tasks for the current user',
        parameters: {
          type: 'object',
          properties: {},
        },
      },
    },
    {
      type: 'function',
      function: {
        name: 'create_task',
        description: 'Create a new task',
        parameters: {
          type: 'object',
          properties: {
            title: { type: 'string', description: 'Task title' },
            description: { type: 'string', description: 'Optional description' },
            due_date: { type: 'string', description: 'Optional due date' },
          },
          required: ['title'],
        },
      },
    },
    {
      type: 'function',
      function: {
        name: 'update_task',
        description: 'Update an existing task',
        parameters: {
          type: 'object',
          properties: {
            task_id: { type: 'number', description: 'Task ID' },
            title: { type: 'string', description: 'New title' },
            completed: { type: 'boolean', description: 'Completion status' },
          },
          required: ['task_id'],
        },
      },
    },
    {
      type: 'function',
      function: {
        name: 'delete_task',
        description: 'Delete a task',
        parameters: {
          type: 'object',
          properties: {
            task_id: { type: 'number', description: 'Task ID to delete' },
          },
          required: ['task_id'],
        },
      },
    },
  ]
}

Chat Hook

// src/​hooks/​useChat.ts
'use client'

import { useCallback, useState } from 'react'
import { ChatMessage, UseChatReturn } from '@/​types/​chat'
import { createChatCompletion } from '@/​lib/​openai'
import { v4 as uuidv4 } from 'uuid'

const SYSTEM_PROMPT = `You are a helpful AI assistant for task management.
You can help users create, update, and manage their tasks through natural language.
Always be helpful, friendly, and concise in your responses.`

export function useChat(): UseChatReturn {
  const [messages, setMessages] = useState<ChatMessage[]>([
    {
      id: 'system',
      role: 'system',
      content: SYSTEM_PROMPT,
      timestamp: new Date(),
    },
  ])
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const sendMessage = useCallback(async (content: string) => {
    const userMessage: ChatMessage = {
      id: uuidv4(),
      role: 'user',
      content,
      timestamp: new Date(),
    }

    setMessages(prev => [...prev, userMessage])
    setIsLoading(true)
    setError(null)

    try {
      const response = await createChatCompletion(
        [...messages, userMessage],
        { stream: false }
      )

      const assistantMessage: ChatMessage = {
        id: uuidv4(),
        role: 'assistant',
        content: response.choices[0]?.message?.content || 'No response',
        timestamp: new Date(),
      }

      setMessages(prev => [...prev, assistantMessage])
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error')
    } finally {
      setIsLoading(false)
    }
  }, [messages])

  const clearMessages = useCallback(() => {
    setMessages([
      {
        id: 'system',
        role: 'system',
        content: SYSTEM_PROMPT,
        timestamp: new Date(),
      },
    ])
  }, [])

  const retry = useCallback(async () => {
    const lastUserMessage = [...messages].reverse().find(m => m.role === 'user')
    if (lastUserMessage) {
      await sendMessage(lastUserMessage.content)
    }
  }, [messages, sendMessage])

  return {
    messages,
    isLoading,
    error,
    sendMessage,
    clearMessages,
    retry,
  }
}

Chat Window Component

// src/​components/​chat/​ChatWindow.tsx
'use client'

import { useRef, useEffect } from 'react'
import { useChat } from '@/​hooks/​useChat'
import { ChatMessage } from './​ChatMessage'
import { ChatInput } from './​ChatInput'

export function ChatWindow() {
  const { messages, isLoading, error, sendMessage } = useChat()
  const messagesEndRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
  }, [messages])

  const handleSend = async (content: string) => {
    await sendMessage(content)
  }

  const assistantMessages = messages.filter(m => m.role === 'assistant')
  const userMessages = messages.filter(m => m.role === 'user')

  return (
    <div className="flex flex-col h-[600px] max-w-2xl mx-auto border rounded-xl overflow-hidden">
      {/​* Header */​}
      <div className="bg-gradient-to-r from-blue-600 to-purple-600 text-white p-4">
        <h2 className="text-lg font-semibold">AI Assistant</​h2>
        <p className="text-sm opacity-80">Ask me to help with your tasks</​p>
      </​div>

      {/​* Messages */​}
      <div className="flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50">
        {messages
          .filter(m => m.role !== 'system')
          .map((message, index) => (
            <ChatMessage
              key={message.id}
              message={message}
              isLatest={index === messages.length - 2}
            />
          ))}

        {isLoading && (
          <div className="flex items-center gap-2 text-gray-500">
            <div className="flex gap-1">
              <span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" />
              <span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-100" />
              <span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-200" />
            </​div>
            <span className="text-sm">AI is thinking...</​span>
          </​div>
        )}

        {error && (
          <div className="p-3 bg-red-50 text-red-700 rounded-lg">
            Error: {error}
          </​div>
        )}

        <div ref={messagesEndRef} />
      </​div>

      {/​* Input */​}
      <ChatInput onSend={handleSend} isLoading={isLoading} />
    </​div>
  )
}

Chat Message Component

// src/​components/​chat/​ChatMessage.tsx
import { ChatMessage as ChatMessageType } from '@/​types/​chat'

interface ChatMessageProps {
  message: ChatMessageType
  isLatest?: boolean
}

export function ChatMessage({ message, isLatest }: ChatMessageProps) {
  const isUser = message.role === 'user'

  return (
    <div className={`flex ${isUser ? 'justify-end' : 'justify-start'}`}>
      <div className={`max-w-[80%] ${isUser ? 'order-2' : 'order-1'}`}>
        {/​* Avatar */​}
        <div className={`flex items-center gap-2 mb-1 ${isUser ? 'flex-row-reverse' : ''}`}>
          <div className={`
            w-8 h-8 rounded-full flex items-center justify-center text-white text-sm
            ${isUser ? 'bg-blue-500' : 'bg-gradient-to-br from-purple-500 to-pink-500'}
          `}>
            {isUser ? 'U' : 'AI'}
          </​div>
          <span className="text-xs text-gray-500">
            {new Date(message.timestamp).toLocaleTimeString()}
          </​span>
        </​div>

        {/​* Message bubble */​}
        <div className={`
          rounded-2xl px-4 py-3
          ${isUser
            ? 'bg-blue-500 text-white rounded-tr-sm'
            : 'bg-white border shadow-sm rounded-tl-sm'
          }
        `}>
          <p className="whitespace-pre-wrap leading-relaxed">
            {message.content}
          </​p>
        </​div>

        {/​* Tool calls indicator */​}
        {message.tools && message.tools.length > 0 && (
          <div className="mt-2 flex flex-wrap gap-2">
            {message.tools.map(tool => (
              <span
                key={tool.id}
                className="inline-flex items-center gap-1 px-2 py-1 bg-purple-100 text-purple-700 rounded text-xs"
              >
                <span className="w-2 h-2 bg-purple-500 rounded-full" />
                {tool.name}
              </​span>
            ))}
          </​div>
        )}
      </​div>
    </​div>
  )
}

Chat Input Component

// src/​components/​chat/​ChatInput.tsx
'use client'

import { useState, useRef, useEffect } from 'react'

interface ChatInputProps {
  onSend: (content: string) => Promise<void>
  isLoading: boolean
}

export function ChatInput({ onSend, isLoading }: ChatInputProps) {
  const [input, setInput] = useState('')
  const textareaRef = useRef<HTMLTextAreaElement>(null)

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    if (!input.trim() || isLoading) return

    const content = input.trim()
    setInput('')
    await onSend(content)
  }

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault()
      handleSubmit(e)
    }
  }

  // Auto-resize textarea
  useEffect(() => {
    if (textareaRef.current) {
      textareaRef.current.style.height = 'auto'
      textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`
    }
  }, [input])

  return (
    <form onSubmit={handleSubmit} className="p-4 bg-white border-t">
      <div className="flex items-end gap-2">
        <textarea
          ref={textareaRef}
          value={input}
          onChange={e => setInput(e.target.value)}
          onKeyDown={handleKeyDown}
          placeholder="Ask me to help with your tasks..."
          className="flex-1 resize-none border rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent max-h-32"
          rows={1}
          disabled={isLoading}
        />
        <button
          type="submit"
          disabled={!input.trim() || isLoading}
          className={`
            px-4 py-2 rounded-lg font-medium transition-colors
            ${input.trim() && !isLoading
              ? 'bg-blue-600 text-white hover:bg-blue-700'
              : 'bg-gray-200 text-gray-400 cursor-not-allowed'
            }
          `}
        >
          {isLoading ? 'Sending...' : 'Send'}
        </​button>
      </​div>
      <p className="text-xs text-gray-500 mt-2">
        Press Enter to send, Shift+Enter for new line
      </​p>
    </​form>
  )
}

Streaming Response Example

// Streaming chat implementation
export async function* streamChat(messages: ChatMessage[]) {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: messages.map(m => ({ role: m.role, content: m.content })),
    stream: true,
  })

  for await (const chunk of response) {
    const content = chunk.choices[0]?.delta?.content
    if (content) {
      yield content
    }
  }
}

// Usage in component
async function handleStreamingSend(content: string) {
  const encoder = new TextEncoder()
  const stream = await streamChat([...messages, userMessage])

  // Display streaming content
  for await (const chunk of stream) {
    setAssistantContent(prev => prev + chunk)
  }
}

More Productivity & Planning skills

brainstorming logo
Productivity & Planning

brainstorming

Structured design dialogue that validates ideas before implementation begins.

295.4K 385.7K
View
ui-ux-pro-max logo
Productivity & Planning

ui-ux-pro-max

Comprehensive design intelligence for web and mobile UI/UX across 10 technology stacks.

133.1K 383.9K
View
writing-plans logo
Productivity & Planning

writing-plans

Comprehensive implementation plans for multi-step tasks, breaking down specs into bite-sized, testable steps.

295.4K 268K
View
using-superpowers logo
Productivity & Planning

using-superpowers

Introduction to the obra skills system with mandatory skill invocation rules and best practices.

295.4K 259.8K
View
executing-plans logo
Productivity & Planning

executing-plans

Execute a written implementation plan with critical review and task checkpoints.

295.4K 229.1K
View
dispatching-parallel-agents logo
Productivity & Planning

dispatching-parallel-agents

Delegate independent tasks to specialized agents working concurrently with isolated context.

295.4K 206.3K
View
using-git-worktrees logo
Productivity & Planning

using-git-worktrees

Isolated git worktrees with smart directory selection and safety verification.

295.4K 205.2K
View
webapp-testing logo
Productivity & Planning

webapp-testing

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

179.7K 170.6K
View
content-strategy logo
Productivity & Planning

content-strategy

Plan searchable and shareable content that drives traffic, builds authority, and generates leads.

53.3K 150.9K
View
repo-intake-and-plan logo
Productivity & Planning

repo-intake-and-plan

README-first repository scanner that extracts commands and classifies reproduction candidates without executing them.

497 139.6K
View
marketing-ideas logo
Productivity & Planning

marketing-ideas

Brainstorm and prioritize marketing strategies tailored to your SaaS stage, budget, and goals.

53.3K 137.2K
View
site-architecture logo
Productivity & Planning

site-architecture

Plan and optimize your website's page hierarchy, navigation, URL structure, and internal linking.

53.3K 112.8K
View

Productivity AI tools

Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View
SaveDay logo
Productivity

SaveDay

Capture, organize, and utilize your knowledge effortlessly.

Free
View
Any Summary logo
Productivity

Any Summary

Instant Summaries of Audio & Video Interviews with AnySummary

Freemium
View
M
Productivity

Map This

Transform PDFs into engaging mind maps.

Freemium
View
ChatPDF logo
Productivity

ChatPDF

Chat with any PDF instantly

Free
View
I
Productivity

intellisay

Create an optimal daily plan using your voice

Paid
View
A
Productivity

Aurora AI

A productivity platform to centralize organizational knowledge and workflows with contextual AI assistance.

Paid
View
AskYourPDF logo
Productivity

AskYourPDF

AskYourPDF Pricing Plans: Tailored to Your Needs

Free
View