| name | ai-client |
| description | Conventions for the opentrons-ai-client React/TypeScript frontend — project structure, API integration, state management (Jotai), feature flags, types, and testing. Use when working with files in opentrons-ai-client/ or discussing the AI client application. |
AI Client Instructions
Overview
opentrons-ai-client is a React/TypeScript single-page application that generates and updates Opentrons protocols from natural language descriptions. It lives in the monorepo and is built with Vite.
Deployed environments: staging (staging.opentrons.ai) and prod (ai.opentrons.com).
Project Structure
opentrons-ai-client/
├── src/
│ ├── OpentronsAI.tsx # Root component, Auth0 bootstrap, feature flag sync
│ ├── main.tsx # Auth0 provider, router, app entry
│ ├── analytics/ # Mixpanel event tracking
│ ├── assets/localization/en/ # i18next translation strings
│ ├── components/ # atoms/, molecules/, organisms/
│ ├── pages/ # Chat, CreateProtocol, UpdateProtocol, Landing, Settings
│ ├── resources/
│ │ ├── atoms.ts # Jotai atoms (app-wide state)
│ │ ├── constants.ts # API base URLs and Auth0 config per environment
│ │ ├── types.ts # Shared TypeScript types (ChatData, CreatePrompt, etc.)
│ │ ├── hooks/
│ │ │ ├── useInputPromptController.ts # Main submit logic
│ │ │ ├── useGetAccessToken.ts # Auth0 token fetching
│ │ │ ├── useApiCall.ts # Axios wrapper (callApi, data, isLoading, error)
│ │ │ └── useAttachFiles.ts # File attachment handling
│ │ └── utils/
│ │ ├── buildRequestConfig.ts # Builds Axios config per endpoint type
│ │ ├── buildChatHistory.ts # Formats chat history for the API
│ │ ├── createUserInput.ts # Creates ChatData for user turns
│ │ ├── protocolUtils.ts # Prompt builders for create/update
│ │ └── resolveErrorMessage.ts # Maps API error_type to user-facing strings
│ └── feature-flags/ # Feature flag types and DEPRECATED_FLAGS cleanup
└── vite.config.mts
Types — Use camelCase Throughout
All types in src/resources/types.ts use camelCase field names. The API request/response bodies also use camelCase. Do not use snake_case for any new TypeScript types or API payload fields.
Key types:
| Type | Purpose |
|---|
ChatData | A single turn in the chat display (user or assistant) |
Chat | A history entry sent to the server (role, content, protocolContent) |
CreatePrompt | Atom shape for the create-protocol form state |
UpdatePrompt | Atom shape for the update-protocol form state |
State Management (Jotai)
Key atoms in src/resources/atoms.ts:
| Atom | Purpose |
|---|
chatDataAtom | All turns displayed in the chat UI |
chatHistoryAtom | History sent to the server in API requests |
createProtocolChatAtom | Create-protocol form values |
updateProtocolChatAtom | Update-protocol form values |
featureFlagsAtom | Feature flags (persisted to localStorage) |
feedbackModalAtom | Whether the feedback modal is open |
tokenAtom has been removed. Always use useGetAccessToken() to fetch a fresh token before making API calls.
API Integration
The client communicates with the server via a standard JSON HTTP API. Endpoint selection is handled in buildRequestConfig.ts.
| Request type | Endpoint |
|---|
| Update protocol (no files) | POST /api/chat/update-protocol |
| Create protocol (no files) | POST /api/chat/create-protocol |
| Chat completion (no files) | POST /api/chat/completion |
| Chat completion (with files) | POST /api/chat/completion-multipart |
The useInputPromptController hook is the single entry point for all submissions. It:
- Calls
getAccessToken() to get a fresh token before each request
- Calls
buildRequestConfig() to assemble the Axios config
- Calls
callApi(config) from useApiCall
- On completion, updates
chatDataAtom and chatHistoryAtom
Authentication
- Use
useGetAccessToken() (not the removed tokenAtom) whenever a token is needed
getAccessToken() is async — always await it and wrap in try/catch
FeedbackModal and other components that POST independently also use useGetAccessToken()
Environment-Driven URLs
constants.ts exports endpoint constants for three environments (LOCAL_*, STAGING_*, PROD_*). buildRequestConfig.ts uses _NODE_ENV_ (a Vite define) via pickEndpoint() to select the right URL.
Feature Flags
Feature flags live in featureFlagsAtom and are persisted to localStorage. Flags listed in DEPRECATED_FLAGS (src/feature-flags/types.ts) are automatically cleaned up on startup.
User-facing flags are toggled on the Settings page. The enablePrereleaseMode flag is toggled via window.enablePrereleaseMode() in the browser console.
useEffect Exhaustive-Deps Convention
New code wraps useEffect callbacks in the multi-line form and adds a FIXME comment for any suppressed exhaustive-deps lint rule:
useEffect(
() => {
},
[dep1, dep2]
)
Dev Commands
Run from the monorepo root:
make -C opentrons-ai-client dev
make test-js-opentrons-ai-client
make format-js
make lint-js
make lint-css
make check-js