Remove context usage code

This commit is contained in:
Siddharth Ganesan
2026-01-06 19:18:25 -08:00
committed by Emir Karabeg
parent 4fd5656c01
commit d86e43945f
8 changed files with 1 additions and 334 deletions

View File

@@ -1,134 +0,0 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { getCopilotModel } from '@/lib/copilot/config'
import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/copilot/constants'
import type { CopilotProviderConfig } from '@/lib/copilot/types'
import { env } from '@/lib/core/config/env'
const logger = createLogger('ContextUsageAPI')
const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT
const ContextUsageRequestSchema = z.object({
chatId: z.string(),
model: z.string(),
workflowId: z.string(),
provider: z.any().optional(),
})
/**
* POST /api/copilot/context-usage
* Fetch context usage from sim-agent API
*/
export async function POST(req: NextRequest) {
try {
logger.info('[Context Usage API] Request received')
const session = await getSession()
if (!session?.user?.id) {
logger.warn('[Context Usage API] No session/user ID')
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await req.json()
logger.info('[Context Usage API] Request body', body)
const parsed = ContextUsageRequestSchema.safeParse(body)
if (!parsed.success) {
logger.warn('[Context Usage API] Invalid request body', parsed.error.errors)
return NextResponse.json(
{ error: 'Invalid request body', details: parsed.error.errors },
{ status: 400 }
)
}
const { chatId, model, workflowId, provider } = parsed.data
const userId = session.user.id // Get userId from session, not from request
logger.info('[Context Usage API] Request validated', { chatId, model, userId, workflowId })
// Build provider config similar to chat route
let providerConfig: CopilotProviderConfig | undefined = provider
if (!providerConfig) {
const defaults = getCopilotModel('chat')
const modelToUse = env.COPILOT_MODEL || defaults.model
const providerEnv = env.COPILOT_PROVIDER as any
if (providerEnv) {
if (providerEnv === 'azure-openai') {
providerConfig = {
provider: 'azure-openai',
model: modelToUse,
apiKey: env.AZURE_OPENAI_API_KEY,
apiVersion: env.AZURE_OPENAI_API_VERSION,
endpoint: env.AZURE_OPENAI_ENDPOINT,
}
} else if (providerEnv === 'vertex') {
providerConfig = {
provider: 'vertex',
model: modelToUse,
apiKey: env.COPILOT_API_KEY,
vertexProject: env.VERTEX_PROJECT,
vertexLocation: env.VERTEX_LOCATION,
}
} else {
providerConfig = {
provider: providerEnv,
model: modelToUse,
apiKey: env.COPILOT_API_KEY,
}
}
}
}
// Call sim-agent API
const requestPayload = {
chatId,
model,
userId,
workflowId,
...(providerConfig ? { provider: providerConfig } : {}),
}
logger.info('[Context Usage API] Calling sim-agent', {
url: `${SIM_AGENT_API_URL}/api/get-context-usage`,
payload: requestPayload,
})
const simAgentResponse = await fetch(`${SIM_AGENT_API_URL}/api/get-context-usage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(env.COPILOT_API_KEY ? { 'x-api-key': env.COPILOT_API_KEY } : {}),
},
body: JSON.stringify(requestPayload),
})
logger.info('[Context Usage API] Sim-agent response', {
status: simAgentResponse.status,
ok: simAgentResponse.ok,
})
if (!simAgentResponse.ok) {
const errorText = await simAgentResponse.text().catch(() => '')
logger.warn('[Context Usage API] Sim agent request failed', {
status: simAgentResponse.status,
error: errorText,
})
return NextResponse.json(
{ error: 'Failed to fetch context usage from sim-agent' },
{ status: simAgentResponse.status }
)
}
const data = await simAgentResponse.json()
logger.info('[Context Usage API] Sim-agent data received', data)
return NextResponse.json(data)
} catch (error) {
logger.error('Error fetching context usage:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}

View File

@@ -1,76 +0,0 @@
'use client'
import { useMemo } from 'react'
import { Tooltip } from '@/components/emcn'
interface ContextUsageIndicatorProps {
/** Usage percentage (0-100) */
percentage: number
/** Size of the indicator in pixels */
size?: number
/** Stroke width in pixels */
strokeWidth?: number
}
/**
* Circular context usage indicator showing percentage of context window used.
* Displays a progress ring that changes color based on usage level.
*
* @param props - Component props
* @returns Rendered context usage indicator
*/
export function ContextUsageIndicator({
percentage,
size = 20,
strokeWidth = 2,
}: ContextUsageIndicatorProps) {
const radius = (size - strokeWidth) / 2
const circumference = radius * 2 * Math.PI
const offset = circumference - (percentage / 100) * circumference
const color = useMemo(() => {
if (percentage >= 90) return 'var(--text-error)'
if (percentage >= 75) return 'var(--warning)'
return 'var(--text-muted)'
}, [percentage])
const displayPercentage = useMemo(() => {
return Math.round(percentage)
}, [percentage])
return (
<Tooltip.Root delayDuration={100}>
<Tooltip.Trigger asChild>
<div
className='flex cursor-pointer items-center justify-center transition-opacity hover:opacity-80'
style={{ width: size, height: size }}
>
<svg width={size} height={size} className='rotate-[-90deg]'>
<circle
cx={size / 2}
cy={size / 2}
r={radius}
stroke='currentColor'
strokeWidth={strokeWidth}
fill='none'
className='text-muted-foreground/20'
/>
<circle
cx={size / 2}
cy={size / 2}
r={radius}
stroke={color}
strokeWidth={strokeWidth}
fill='none'
strokeDasharray={circumference}
strokeDashoffset={offset}
className='transition-all duration-300 ease-in-out'
strokeLinecap='round'
/>
</svg>
</div>
</Tooltip.Trigger>
<Tooltip.Content side='top'>{displayPercentage}% context used</Tooltip.Content>
</Tooltip.Root>
)
}

View File

@@ -1,6 +1,5 @@
export { AttachedFilesDisplay } from './attached-files-display/attached-files-display'
export { ContextPills } from './context-pills/context-pills'
export { ContextUsageIndicator } from './context-usage-indicator/context-usage-indicator'
export { MentionMenu } from './mention-menu/mention-menu'
export { ModeSelector } from './mode-selector/mode-selector'
export { ModelSelector } from './model-selector/model-selector'

View File

@@ -117,7 +117,6 @@ const UserInput = forwardRef<UserInputRef, UserInputProps>(
const selectedModel =
selectedModelOverride !== undefined ? selectedModelOverride : copilotStore.selectedModel
const setSelectedModel = onModelChangeOverride || copilotStore.setSelectedModel
const contextUsage = copilotStore.contextUsage
// Internal state
const [internalMessage, setInternalMessage] = useState('')

View File

@@ -100,7 +100,6 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(({ panelWidth }, ref
loadChats,
messageCheckpoints,
currentChat,
fetchContextUsage,
selectChat,
deleteChat,
areChatsFresh,
@@ -119,7 +118,6 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(({ panelWidth }, ref
chatsLoadedForWorkflow,
setCopilotWorkflowId,
loadChats,
fetchContextUsage,
loadAutoAllowedTools,
currentChat,
isSendingMessage,

View File

@@ -11,7 +11,6 @@ interface UseCopilotInitializationProps {
chatsLoadedForWorkflow: string | null
setCopilotWorkflowId: (workflowId: string | null) => Promise<void>
loadChats: (forceRefresh?: boolean) => Promise<void>
fetchContextUsage: () => Promise<void>
loadAutoAllowedTools: () => Promise<void>
currentChat: any
isSendingMessage: boolean
@@ -30,7 +29,6 @@ export function useCopilotInitialization(props: UseCopilotInitializationProps) {
chatsLoadedForWorkflow,
setCopilotWorkflowId,
loadChats,
fetchContextUsage,
loadAutoAllowedTools,
currentChat,
isSendingMessage,
@@ -102,18 +100,6 @@ export function useCopilotInitialization(props: UseCopilotInitializationProps) {
isSendingMessage,
])
/**
* Fetch context usage when component is initialized and has a current chat
*/
useEffect(() => {
if (isInitialized && currentChat?.id && activeWorkflowId) {
logger.info('[Copilot] Component initialized, fetching context usage')
fetchContextUsage().catch((err) => {
logger.warn('[Copilot] Failed to fetch context usage on mount', err)
})
}
}, [isInitialized, currentChat?.id, activeWorkflowId, fetchContextUsage])
/**
* Load auto-allowed tools once on mount
*/

View File

@@ -1969,7 +1969,6 @@ const initialState = {
streamingPlanContent: '',
toolCallsById: {} as Record<string, CopilotToolCall>,
suppressAutoSelect: false,
contextUsage: null,
autoAllowedTools: [] as string[],
messageQueue: [] as import('./types').QueuedMessage[],
}
@@ -1982,7 +1981,7 @@ export const useCopilotStore = create<CopilotStore>()(
setMode: (mode) => set({ mode }),
// Clear messages (don't clear streamingPlanContent - let it persist)
clearMessages: () => set({ messages: [], contextUsage: null }),
clearMessages: () => set({ messages: [] }),
// Workflow selection
setWorkflowId: async (workflowId: string | null) => {
@@ -2060,7 +2059,6 @@ export const useCopilotStore = create<CopilotStore>()(
mode: chatMode,
selectedModel: chatModel as CopilotStore['selectedModel'],
suppressAutoSelect: false,
contextUsage: null,
})
// Background-save the previous chat's latest messages, plan artifact, and config before switching (optimistic)
@@ -2112,15 +2110,11 @@ export const useCopilotStore = create<CopilotStore>()(
chats: (get().chats || []).map((c: CopilotChat) =>
c.id === chat.id ? latestChat : c
),
contextUsage: null,
toolCallsById,
})
try {
await get().loadMessageCheckpoints(latestChat.id)
} catch {}
// Fetch context usage for the selected chat
logger.info('[Context Usage] Chat selected, fetching usage')
await get().fetchContextUsage()
}
}
} catch {}
@@ -2158,7 +2152,6 @@ export const useCopilotStore = create<CopilotStore>()(
}
} catch {}
logger.info('[Context Usage] New chat created, clearing context usage')
set({
currentChat: null,
messages: [],
@@ -2167,7 +2160,6 @@ export const useCopilotStore = create<CopilotStore>()(
showPlanTodos: false,
streamingPlanContent: '',
suppressAutoSelect: true,
contextUsage: null,
})
},
@@ -2560,13 +2552,6 @@ export const useCopilotStore = create<CopilotStore>()(
} catch {}
}
// Fetch context usage after abort
logger.info('[Context Usage] Message aborted, fetching usage')
get()
.fetchContextUsage()
.catch((err) => {
logger.warn('[Context Usage] Failed to fetch after abort', err)
})
} catch {
set({ isSendingMessage: false, isAborting: false, abortController: null })
}
@@ -3132,10 +3117,6 @@ export const useCopilotStore = create<CopilotStore>()(
// Removed: stats sending now occurs only on accept/reject with minimal payload
} catch {}
// Fetch context usage after response completes
logger.info('[Context Usage] Stream completed, fetching usage')
await get().fetchContextUsage()
// Invalidate subscription queries to update usage
setTimeout(() => {
const queryClient = getQueryClient()
@@ -3313,86 +3294,11 @@ export const useCopilotStore = create<CopilotStore>()(
},
setSelectedModel: async (model) => {
logger.info('[Context Usage] Model changed', { from: get().selectedModel, to: model })
set({ selectedModel: model })
// Fetch context usage after model switch
await get().fetchContextUsage()
},
setAgentPrefetch: (prefetch) => set({ agentPrefetch: prefetch }),
setEnabledModels: (models) => set({ enabledModels: models }),
// Fetch context usage from sim-agent API
fetchContextUsage: async () => {
try {
const { currentChat, selectedModel, workflowId } = get()
logger.info('[Context Usage] Starting fetch', {
hasChatId: !!currentChat?.id,
hasWorkflowId: !!workflowId,
chatId: currentChat?.id,
workflowId,
model: selectedModel,
})
if (!currentChat?.id || !workflowId) {
logger.info('[Context Usage] Skipping: missing chat or workflow', {
hasChatId: !!currentChat?.id,
hasWorkflowId: !!workflowId,
})
return
}
const requestPayload = {
chatId: currentChat.id,
model: selectedModel,
workflowId,
}
logger.info('[Context Usage] Calling API', requestPayload)
// Call the backend API route which proxies to sim-agent
const response = await fetch('/api/copilot/context-usage', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestPayload),
})
logger.info('[Context Usage] API response', { status: response.status, ok: response.ok })
if (response.ok) {
const data = await response.json()
logger.info('[Context Usage] Received data', data)
// Check for either tokensUsed or usage field
if (
data.tokensUsed !== undefined ||
data.usage !== undefined ||
data.percentage !== undefined
) {
const contextUsage = {
usage: data.tokensUsed || data.usage || 0,
percentage: data.percentage || 0,
model: data.model || selectedModel,
contextWindow: data.contextWindow || data.context_window || 0,
when: data.when || 'end',
estimatedTokens: data.tokensUsed || data.estimated_tokens || data.estimatedTokens,
}
set({ contextUsage })
logger.info('[Context Usage] Updated store', contextUsage)
} else {
logger.warn('[Context Usage] No usage data in response', data)
}
} else {
const errorText = await response.text().catch(() => 'Unable to read error')
logger.warn('[Context Usage] API call failed', {
status: response.status,
error: errorText,
})
}
} catch (err) {
logger.error('[Context Usage] Error fetching:', err)
}
},
executeIntegrationTool: async (toolCallId: string) => {
const { toolCallsById, workflowId } = get()
const toolCall = toolCallsById[toolCallId]

View File

@@ -161,16 +161,6 @@ export interface CopilotState {
// Per-message metadata captured at send-time for reliable stats
// Context usage tracking for percentage pill
contextUsage: {
usage: number
percentage: number
model: string
contextWindow: number
when: 'start' | 'end'
estimatedTokens?: number
} | null
// Auto-allowed integration tools (tools that can run without confirmation)
autoAllowedTools: string[]
@@ -183,7 +173,6 @@ export interface CopilotActions {
setSelectedModel: (model: CopilotStore['selectedModel']) => Promise<void>
setAgentPrefetch: (prefetch: boolean) => void
setEnabledModels: (models: string[] | null) => void
fetchContextUsage: () => Promise<void>
setWorkflowId: (workflowId: string | null) => Promise<void>
validateCurrentChat: () => boolean