chore(copilot): remove the training controls feature (#6257)

This commit is contained in:
Waleed
2026-08-04 12:10:03 -07:00
committed by GitHub
parent 63faeb555f
commit fb7fd5b621
10 changed files with 1 additions and 356 deletions
@@ -1,82 +0,0 @@
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { copilotTrainingExampleContract } from '@/lib/api/contracts/copilot'
import { parseRequest, validationErrorResponse } from '@/lib/api/server'
import { checkInternalApiKey, createUnauthorizedResponse } from '@/lib/copilot/request/http'
import { env } from '@/lib/core/config/env'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
const logger = createLogger('CopilotTrainingExamplesAPI')
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
export const POST = withRouteHandler(async (request: NextRequest) => {
const auth = checkInternalApiKey(request)
if (!auth.success) {
return createUnauthorizedResponse()
}
const baseUrl = env.AGENT_INDEXER_URL
if (!baseUrl) {
logger.error('Missing AGENT_INDEXER_URL environment variable')
return NextResponse.json({ error: 'Missing AGENT_INDEXER_URL env' }, { status: 500 })
}
const apiKey = env.AGENT_INDEXER_API_KEY
if (!apiKey) {
logger.error('Missing AGENT_INDEXER_API_KEY environment variable')
return NextResponse.json({ error: 'Missing AGENT_INDEXER_API_KEY env' }, { status: 500 })
}
try {
const parsed = await parseRequest(
copilotTrainingExampleContract,
request,
{},
{
validationErrorResponse: (error) => {
logger.warn('Invalid training example format', { errors: error.issues })
return validationErrorResponse(error, 'Invalid training example format')
},
}
)
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info('Sending workflow example to agent indexer', {
hasJsonField: typeof validatedData.json === 'string',
title: validatedData.title,
})
const upstream = await fetch(`${baseUrl}/examples/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
},
body: JSON.stringify(validatedData),
})
if (!upstream.ok) {
const errorText = await upstream.text()
logger.error('Agent indexer rejected the example', {
status: upstream.status,
error: errorText,
})
return NextResponse.json({ error: errorText }, { status: upstream.status })
}
const data = await upstream.json()
logger.info('Successfully sent workflow example to agent indexer')
return NextResponse.json(data, {
headers: { 'content-type': 'application/json' },
})
} catch (err) {
const errorMessage = getErrorMessage(err, 'Failed to add example')
logger.error('Failed to send workflow example', { error: err })
return NextResponse.json({ error: errorMessage }, { status: 502 })
}
})
@@ -1,94 +0,0 @@
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { copilotTrainingDataContract } from '@/lib/api/contracts/copilot'
import { parseRequest, validationErrorResponse } from '@/lib/api/server'
import { checkInternalApiKey, createUnauthorizedResponse } from '@/lib/copilot/request/http'
import { env } from '@/lib/core/config/env'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
const logger = createLogger('CopilotTrainingAPI')
export const POST = withRouteHandler(async (request: NextRequest) => {
const auth = checkInternalApiKey(request)
if (!auth.success) {
return createUnauthorizedResponse()
}
try {
const baseUrl = env.AGENT_INDEXER_URL
if (!baseUrl) {
logger.error('Missing AGENT_INDEXER_URL environment variable')
return NextResponse.json({ error: 'Agent indexer not configured' }, { status: 500 })
}
const apiKey = env.AGENT_INDEXER_API_KEY
if (!apiKey) {
logger.error('Missing AGENT_INDEXER_API_KEY environment variable')
return NextResponse.json(
{ error: 'Agent indexer authentication not configured' },
{ status: 500 }
)
}
const parsed = await parseRequest(
copilotTrainingDataContract,
request,
{},
{
validationErrorResponse: (error) => {
logger.warn('Invalid training data format', { errors: error.issues })
return validationErrorResponse(error, 'Invalid training data format')
},
}
)
if (!parsed.success) return parsed.response
const { title, prompt, input, output, operations } = parsed.data.body
logger.info('Sending training data to agent indexer', {
title,
operationsCount: operations.length,
})
const upstreamUrl = `${baseUrl}/operations/add`
const upstreamResponse = await fetch(upstreamUrl, {
method: 'POST',
headers: {
'x-api-key': apiKey,
'content-type': 'application/json',
},
body: JSON.stringify({
title,
prompt,
input,
output,
operations: { operations },
}),
})
const responseData = await upstreamResponse.json()
if (!upstreamResponse.ok) {
logger.error('Agent indexer rejected the data', {
status: upstreamResponse.status,
response: responseData,
})
return NextResponse.json(responseData, { status: upstreamResponse.status })
}
logger.info('Successfully sent training data to agent indexer', {
title,
response: responseData,
})
return NextResponse.json(responseData)
} catch (error) {
logger.error('Failed to send training data to agent indexer', { error })
return NextResponse.json(
{
error: getErrorMessage(error, 'Failed to send training data'),
},
{ status: 502 }
)
}
})
@@ -23,7 +23,6 @@ import { requestJson } from '@/lib/api/client/request'
import { telemetryContract } from '@/lib/api/contracts/telemetry'
import { signOut, useSession } from '@/lib/auth/auth-client'
import { ANONYMOUS_USER_ID } from '@/lib/auth/constants'
import { getEnv, isTruthy } from '@/lib/core/config/env'
import { isHosted } from '@/lib/core/config/env-flags'
import { getBrowserTimezone, getTimezoneOptions } from '@/lib/core/utils/timezone'
import { getBaseUrl } from '@/lib/core/utils/urls'
@@ -79,7 +78,6 @@ export function General() {
const isLoading = isProfileLoading || isSettingsLoading
const isTrainingEnabled = isTruthy(getEnv('NEXT_PUBLIC_COPILOT_TRAINING_ENABLED'))
const isAuthDisabled = session?.user?.id === ANONYMOUS_USER_ID
const [name, setName] = useState(profile?.name || '')
@@ -232,12 +230,6 @@ export function General() {
}
}
const handleTrainingControlsChange = async (checked: boolean) => {
if (checked !== settings?.showTrainingControls && !updateSetting.isPending) {
await updateSetting.mutateAsync({ key: 'showTrainingControls', value: checked })
}
}
const handleErrorNotificationsChange = async (checked: boolean) => {
if (checked !== settings?.errorNotificationsEnabled && !updateSetting.isPending) {
await updateSetting.mutateAsync({ key: 'errorNotificationsEnabled', value: checked })
@@ -528,17 +520,6 @@ export function General() {
onCheckedChange={handleShowActionBarChange}
/>
</div>
{isTrainingEnabled && (
<div className='flex items-center justify-between'>
<Label htmlFor='training-controls'>Training controls</Label>
<Switch
id='training-controls'
checked={settings?.showTrainingControls ?? false}
onCheckedChange={handleTrainingControlsChange}
/>
</div>
)}
</div>
</SettingsSection>
@@ -16,11 +16,9 @@ import {
ArrowUp,
Check,
Clipboard,
Database,
Download,
MoreHorizontal,
Palette,
Pause,
Search,
Trash,
X,
@@ -94,9 +92,6 @@ export interface OutputPanelProps {
setShowInput: (show: boolean) => void
hasInputData: boolean
isPlaygroundEnabled: boolean
shouldShowTrainingButton: boolean
isTraining: boolean
handleTrainingClick: (e: React.MouseEvent) => void
showCopySuccess: boolean
handleCopy: () => void
hasEntries: boolean
@@ -121,9 +116,6 @@ export const OutputPanel = React.memo(function OutputPanel({
setShowInput,
hasInputData,
isPlaygroundEnabled,
shouldShowTrainingButton,
isTraining,
handleTrainingClick,
showCopySuccess,
handleCopy,
hasEntries,
@@ -392,31 +384,6 @@ export const OutputPanel = React.memo(function OutputPanel({
</Tooltip.Root>
)}
{shouldShowTrainingButton && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button
variant='ghost'
onClick={handleTrainingClick}
aria-label={isTraining ? 'Stop training' : 'Train Sim'}
className={clsx(
'!p-1.5 -m-1.5',
isTraining && 'text-orange-600 dark:text-orange-400'
)}
>
{isTraining ? (
<Pause className='h-3.5 w-3.5' />
) : (
<Database className='h-3.5 w-3.5' />
)}
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
<span>{isTraining ? 'Stop Training' : 'Train Sim'}</span>
</Tooltip.Content>
</Tooltip.Root>
)}
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button
@@ -12,16 +12,7 @@ import {
PopoverTrigger,
Tooltip,
} from '@sim/emcn'
import {
ArrowDown,
ArrowUp,
Database,
Download,
MoreHorizontal,
Palette,
Pause,
Trash,
} from '@sim/emcn/icons'
import { ArrowDown, ArrowUp, Download, MoreHorizontal, Palette, Trash } from '@sim/emcn/icons'
import { formatDuration } from '@sim/utils/formatting'
import { useVirtualizer } from '@tanstack/react-virtual'
import clsx from 'clsx'
@@ -58,7 +49,6 @@ import {
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils'
import { useContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks'
import { getTileIconColorClass } from '@/blocks/icon-color'
import { useShowTrainingControls } from '@/hooks/queries/general-settings'
import { OUTPUT_PANEL_WIDTH, TERMINAL_HEIGHT } from '@/stores/constants'
import type { ConsoleEntry } from '@/stores/terminal'
import {
@@ -718,14 +708,6 @@ export const Terminal = memo(function Terminal() {
const [autoSelectEnabled, setAutoSelectEnabled] = useState(true)
const [mainOptionsOpen, setMainOptionsOpen] = useState(false)
const [isTrainingEnvEnabled] = useState(() =>
isTruthy(getEnv('NEXT_PUBLIC_COPILOT_TRAINING_ENABLED'))
)
const showTrainingControls = useShowTrainingControls()
const isTraining = false
const toggleTrainingModal = useCallback(() => {}, [])
const stopTraining = useCallback(() => {}, [])
const [isPlaygroundEnabled] = useState(() => isTruthy(getEnv('NEXT_PUBLIC_ENABLE_PLAYGROUND')))
const { handlePointerDown } = useTerminalResize()
@@ -1044,20 +1026,6 @@ export const Terminal = memo(function Terminal() {
[closeLogRowMenu]
)
const handleTrainingClick = useCallback(
(e: React.MouseEvent) => {
e.stopPropagation()
if (isTraining) {
stopTraining()
} else {
toggleTrainingModal()
}
},
[isTraining, stopTraining, toggleTrainingModal]
)
const shouldShowTrainingButton = isTrainingEnvEnabled && showTrainingControls
useRegisterGlobalCommands(() =>
createCommands([
{
@@ -1392,31 +1360,6 @@ export const Terminal = memo(function Terminal() {
</Tooltip.Root>
)}
{shouldShowTrainingButton && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button
variant='ghost'
onClick={handleTrainingClick}
aria-label={isTraining ? 'Stop training' : 'Train Sim'}
className={clsx(
'!p-1.5 -m-1.5',
isTraining && 'text-orange-600 dark:text-orange-400'
)}
>
{isTraining ? (
<Pause className='h-3.5 w-3.5' />
) : (
<Database className='h-3.5 w-3.5' />
)}
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
<span>{isTraining ? 'Stop Training' : 'Train Sim'}</span>
</Tooltip.Content>
</Tooltip.Root>
)}
{filteredEntries.length > 0 && (
<>
<Tooltip.Root>
@@ -1528,9 +1471,6 @@ export const Terminal = memo(function Terminal() {
setShowInput={setShowInput}
hasInputData={hasInputData}
isPlaygroundEnabled={isPlaygroundEnabled}
shouldShowTrainingButton={shouldShowTrainingButton}
isTraining={isTraining}
handleTrainingClick={handleTrainingClick}
showCopySuccess={showCopySuccess}
handleCopy={handleCopy}
hasEntries={filteredEntries.length > 0}
@@ -28,7 +28,6 @@ export const GENERAL_SETTINGS_STALE_TIME = 60 * 60 * 1000
*/
export interface GeneralSettings {
autoConnect: boolean
showTrainingControls: boolean
superUserModeEnabled: boolean
mothershipEnvironment: MothershipEnvironment
theme: 'light' | 'dark' | 'system'
@@ -50,7 +49,6 @@ export interface GeneralSettings {
export function mapGeneralSettingsResponse(data: UserSettingsApi): GeneralSettings {
return {
autoConnect: data.autoConnect,
showTrainingControls: data.showTrainingControls,
superUserModeEnabled: data.superUserModeEnabled,
mothershipEnvironment: data.mothershipEnvironment,
theme: data.theme,
@@ -114,11 +112,6 @@ export function useAutoConnect(): boolean {
return data?.autoConnect ?? true
}
export function useShowTrainingControls(): boolean {
const { data } = useGeneralSettings()
return data?.showTrainingControls ?? false
}
export function useSnapToGridSize(): number {
const { data } = useGeneralSettings()
return data?.snapToGridSize ?? 0
-53
View File
@@ -90,29 +90,6 @@ export const createWorkflowCopilotChatBodySchema = z.object({
})
export type CreateWorkflowCopilotChatBody = z.input<typeof createWorkflowCopilotChatBodySchema>
export const copilotTrainingExampleBodySchema = z.object({
json: z.string().min(1, 'JSON string is required'),
title: z.string().min(1, 'Title is required'),
tags: z.array(z.string()).optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
})
export type CopilotTrainingExampleBody = z.input<typeof copilotTrainingExampleBodySchema>
const copilotTrainingOperationSchema = z.object({
operation_type: z.string(),
block_id: z.string(),
params: z.record(z.string(), z.unknown()).optional(),
})
export const copilotTrainingDataBodySchema = z.object({
title: z.string().min(1, 'Title is required'),
prompt: z.string().min(1, 'Prompt is required'),
input: z.record(z.string(), z.unknown()),
output: z.record(z.string(), z.unknown()),
operations: z.array(copilotTrainingOperationSchema),
})
export type CopilotTrainingDataBody = z.input<typeof copilotTrainingDataBodySchema>
export const renameCopilotChatBodySchema = z.object({
chatId: z.string().min(1),
title: z.string().min(1).max(200),
@@ -711,36 +688,6 @@ export const removeCopilotChatResourceContract = defineRouteContract({
},
})
/**
* Forwards the agent indexer's free-form JSON response.
* Shape varies by upstream version.
*/
export const copilotTrainingDataContract = defineRouteContract({
method: 'POST',
path: '/api/copilot/training',
body: copilotTrainingDataBodySchema,
response: {
mode: 'json',
// untyped-response: forwards external agent indexer /operations/add response unchanged; shape varies by upstream version
schema: z.unknown(),
},
})
/**
* Forwards the agent indexer's free-form JSON response.
* Shape varies by upstream version.
*/
export const copilotTrainingExampleContract = defineRouteContract({
method: 'POST',
path: '/api/copilot/training/examples',
body: copilotTrainingExampleBodySchema,
response: {
mode: 'json',
// untyped-response: forwards external agent indexer /examples/add response unchanged; shape varies by upstream version
schema: z.unknown(),
},
})
export const renameCopilotChatContract = defineRouteContract({
method: 'PATCH',
path: '/api/copilot/chat/rename',
-2
View File
@@ -84,7 +84,6 @@ export const userSettingsSchema = z.object({
telemetryEnabled: z.boolean().default(true),
emailPreferences: userSettingsEmailPreferencesSchema.optional().default({}),
billingUsageNotificationsEnabled: z.boolean().default(true),
showTrainingControls: z.boolean().default(false),
superUserModeEnabled: z.boolean().default(false),
mothershipEnvironment: mothershipEnvironmentSchema.default('default'),
errorNotificationsEnabled: z.boolean().default(true),
@@ -105,7 +104,6 @@ export const updateUserSettingsBodySchema = z.object({
telemetryEnabled: z.boolean().optional(),
emailPreferences: userSettingsEmailPreferencesSchema.optional(),
billingUsageNotificationsEnabled: z.boolean().optional(),
showTrainingControls: z.boolean().optional(),
superUserModeEnabled: z.boolean().optional(),
mothershipEnvironment: mothershipEnvironmentSchema.optional(),
errorNotificationsEnabled: z.boolean().optional(),
-2
View File
@@ -577,7 +577,6 @@ export const env = createEnv({
NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS: z.string().optional(), // Hide Bedrock credential fields when deployment uses AWS default credential chain (IAM roles, instance profiles, ECS task roles, IRSA)
NEXT_PUBLIC_AZURE_CONFIGURED: z.string().optional(), // Hide Azure credential fields when endpoint/key/version are pre-configured server-side
NEXT_PUBLIC_COHERE_CONFIGURED: z.string().optional(), // Hide Cohere API key field on Knowledge block when COHERE_API_KEY is pre-configured server-side
NEXT_PUBLIC_COPILOT_TRAINING_ENABLED: z.string().optional(),
NEXT_PUBLIC_ENABLE_PLAYGROUND: z.string().optional(), // Enable component playground at /playground
NEXT_PUBLIC_DOCUMENTATION_URL: z.string().url().optional(), // Custom documentation URL
NEXT_PUBLIC_TERMS_URL: z.string().url().optional(), // Custom terms of service URL
@@ -661,7 +660,6 @@ export const env = createEnv({
NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS: process.env.NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS,
NEXT_PUBLIC_AZURE_CONFIGURED: process.env.NEXT_PUBLIC_AZURE_CONFIGURED,
NEXT_PUBLIC_COHERE_CONFIGURED: process.env.NEXT_PUBLIC_COHERE_CONFIGURED,
NEXT_PUBLIC_COPILOT_TRAINING_ENABLED: process.env.NEXT_PUBLIC_COPILOT_TRAINING_ENABLED,
NEXT_PUBLIC_ENABLE_PLAYGROUND: process.env.NEXT_PUBLIC_ENABLE_PLAYGROUND,
NEXT_PUBLIC_POSTHOG_ENABLED: process.env.NEXT_PUBLIC_POSTHOG_ENABLED,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
-3
View File
@@ -17,7 +17,6 @@ export const defaultUserSettings: UserSettingsApi = {
telemetryEnabled: true,
emailPreferences: {},
billingUsageNotificationsEnabled: true,
showTrainingControls: false,
superUserModeEnabled: false,
mothershipEnvironment: 'default',
errorNotificationsEnabled: true,
@@ -54,7 +53,6 @@ export async function getUserSettings(userId: string | null): Promise<UserSettin
telemetryEnabled: settings.telemetryEnabled,
emailPreferences: settings.emailPreferences,
billingUsageNotificationsEnabled: settings.billingUsageNotificationsEnabled,
showTrainingControls: settings.showTrainingControls,
superUserModeEnabled: settings.superUserModeEnabled,
mothershipEnvironment: settings.mothershipEnvironment,
errorNotificationsEnabled: settings.errorNotificationsEnabled,
@@ -80,7 +78,6 @@ export async function getUserSettings(userId: string | null): Promise<UserSettin
telemetryEnabled: userSettings.telemetryEnabled,
emailPreferences: userSettings.emailPreferences ?? {},
billingUsageNotificationsEnabled: userSettings.billingUsageNotificationsEnabled ?? true,
showTrainingControls: userSettings.showTrainingControls ?? false,
superUserModeEnabled: userSettings.superUserModeEnabled ?? false,
mothershipEnvironment:
(userSettings.mothershipEnvironment as UserSettingsApi['mothershipEnvironment']) ?? 'default',