Techniques
Actions
A key capability of Copilot.js is to intelligently trigger actions in your app, based on the user's prompt.
Exposing actions
Consider the example of a drawing app, which has functions for manipulating shapes such as createShape
, updateShape
, and so on:
Together, these 6 functions form an API that Copilot will invoke, to accomplish the user's prompt. This API is typically implemented inside a "store", using Redux or Zustand.
To expose this API to Copilot, simply set the context.actions
property:
// App.jsx
import store from './store' // Your application's store.
import { CopilotProvider, CopilotChat } from '@copilotjs/react'
import '@copilotjs/styles/default.css'
export default function App() {
return (
<CopilotProvider
context={{
// A map between each action's name and its function.
actions: {
createShape: store.createShape,
updateShape: store.updateShape,
deleteShape: store.deleteShape,
getShape: store.getShape,
listShapes: store.listShapes,
deleteAllShapes: store.deleteAllShapes,
},
actionTypes: '...',
}}
// ...
>
<CopilotChat />
</CopilotProvider>
)
}
Action types
Exposing actions
to the Copilot is not sufficient. We must also provide type definitions for all actions, so the Copilot knows exactly how to invoke them.
For our drawing app example, let's create a separate actionTypes.ts
file:
// actionTypes.ts
// -------------------- Actions --------------------
type createShape = (shape: Shape) => ShapeWithId
type updateShape = (shapeId: string, updatedShape: Partial<Shape>) => void
type deleteShape = (shapeId: string) => void
type getShape = (shapeId: string) => ShapeWithId
type listShapes = (filters: Partial<ShapeWithId>) => ShapeWithId[]
type deleteAllShapes = () => void
// -------------------- Shapes --------------------
type Shape = Square | Circle
type Circle = { type: 'circle'; x: number; y: number; radius: number }
type Square = { type: 'square'; x: number; y: number; length: number }
// Extend shapes with IDs
type ShapeWithId = SquareWithId | CircleWithId
type CircleWithId = Circle & { id: string }
type SquareWithId = Square & { id: string }
Importing the action types
Once we define the actionTypes.ts
file, we must pass its contents as a string to the Copilot's context.actionTypes
property. There are different ways to achieve this, depending on your bundler.
With Vite
With Vite, you can keep a separate actionTypes.ts
file (with the benefit of syntax highlighting), and use the ?raw
suffix to conveniently import the file contents as a string:
// App.jsx
import actionTypes from './actionTypes.ts?raw' // Import the whole file as raw string.
...
<CopilotProvider
context={{ actionTypes: actionTypes, actions: {} }}
// ...
>
<CopilotChat />
</CopilotProvider>
// actionTypes.ts
type createShape = ...
With any bundler
A universal method that works with any bundler is to store the type definitions in a plain string, then import it as a regular variable. However, you lose the syntax highlighting for the Typescript definitions.
// App.jsx
import { actionTypes } from './actionTypes' // Import the string variable.
...
<CopilotProvider
context={{ actionTypes: actionTypes, actions: {} }}
// ...
>
<CopilotChat />
</CopilotProvider>
// actionTypes.ts
export const actionTypes = `
type createShape = ...
`