Skip to content

Commit 0ebb45b

Browse files
Sg312icecrasher321waleedlatif1
authored
feat(copilot): show inline prompt to increase usage limit or upgrade plan (#2465)
* Add limit v1 * fix ui for copilot upgrade limit inline * open settings modal * Upgrade plan button * Remove comments * Ishosted check * Fix hardcoded bumps --------- Co-authored-by: Vikhyath Mondreti <[email protected]> Co-authored-by: Waleed <[email protected]> Co-authored-by: Vikhyath Mondreti <[email protected]>
1 parent 6247f42 commit 0ebb45b

File tree

5 files changed

+128
-3
lines changed

5 files changed

+128
-3
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './file-display'
22
export { default as CopilotMarkdownRenderer } from './markdown-renderer'
33
export * from './smooth-streaming'
44
export * from './thinking-block'
5+
export * from './usage-limit-actions'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { Loader2 } from 'lucide-react'
5+
import { Button } from '@/components/emcn'
6+
import { canEditUsageLimit } from '@/lib/billing/subscriptions/utils'
7+
import { isHosted } from '@/lib/core/config/feature-flags'
8+
import { useSubscriptionData, useUpdateUsageLimit } from '@/hooks/queries/subscription'
9+
import { useCopilotStore } from '@/stores/panel/copilot/store'
10+
11+
const LIMIT_INCREMENTS = [0, 50, 100] as const
12+
13+
function roundUpToNearest50(value: number): number {
14+
return Math.ceil(value / 50) * 50
15+
}
16+
17+
export function UsageLimitActions() {
18+
const { data: subscriptionData } = useSubscriptionData()
19+
const updateUsageLimitMutation = useUpdateUsageLimit()
20+
21+
const subscription = subscriptionData?.data
22+
const canEdit = subscription ? canEditUsageLimit(subscription) : false
23+
24+
const [selectedAmount, setSelectedAmount] = useState<number | null>(null)
25+
const [isHidden, setIsHidden] = useState(false)
26+
27+
const currentLimit = subscription?.usage_limit ?? 0
28+
const baseLimit = roundUpToNearest50(currentLimit) || 50
29+
const limitOptions = LIMIT_INCREMENTS.map((increment) => baseLimit + increment)
30+
31+
const handleUpdateLimit = async (newLimit: number) => {
32+
setSelectedAmount(newLimit)
33+
try {
34+
await updateUsageLimitMutation.mutateAsync({ limit: newLimit })
35+
36+
setIsHidden(true)
37+
38+
const { messages, sendMessage } = useCopilotStore.getState()
39+
const lastUserMessage = [...messages].reverse().find((m) => m.role === 'user')
40+
41+
if (lastUserMessage) {
42+
const filteredMessages = messages.filter(
43+
(m) => !(m.role === 'assistant' && m.errorType === 'usage_limit')
44+
)
45+
useCopilotStore.setState({ messages: filteredMessages })
46+
47+
await sendMessage(lastUserMessage.content, {
48+
fileAttachments: lastUserMessage.fileAttachments,
49+
contexts: lastUserMessage.contexts,
50+
messageId: lastUserMessage.id,
51+
})
52+
}
53+
} catch {
54+
setIsHidden(false)
55+
} finally {
56+
setSelectedAmount(null)
57+
}
58+
}
59+
60+
const handleNavigateToUpgrade = () => {
61+
if (isHosted) {
62+
window.dispatchEvent(new CustomEvent('open-settings', { detail: { tab: 'subscription' } }))
63+
} else {
64+
window.open('https://www.sim.ai', '_blank')
65+
}
66+
}
67+
68+
if (isHidden) {
69+
return null
70+
}
71+
72+
if (!isHosted || !canEdit) {
73+
return (
74+
<Button onClick={handleNavigateToUpgrade} variant='default'>
75+
Upgrade
76+
</Button>
77+
)
78+
}
79+
80+
return (
81+
<>
82+
{limitOptions.map((limit) => {
83+
const isLoading = updateUsageLimitMutation.isPending && selectedAmount === limit
84+
const isDisabled = updateUsageLimitMutation.isPending
85+
86+
return (
87+
<Button
88+
key={limit}
89+
onClick={() => handleUpdateLimit(limit)}
90+
disabled={isDisabled}
91+
variant='default'
92+
>
93+
{isLoading ? <Loader2 className='mr-1 h-3 w-3 animate-spin' /> : null}${limit}
94+
</Button>
95+
)
96+
})}
97+
</>
98+
)
99+
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/copilot-message.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
SmoothStreamingText,
1010
StreamingIndicator,
1111
ThinkingBlock,
12+
UsageLimitActions,
1213
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components'
1314
import CopilotMarkdownRenderer from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components/markdown-renderer'
1415
import {
@@ -458,6 +459,12 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
458459
<StreamingIndicator />
459460
)}
460461

462+
{message.errorType === 'usage_limit' && (
463+
<div className='mt-3 flex gap-1.5'>
464+
<UsageLimitActions />
465+
</div>
466+
)}
467+
461468
{/* Action buttons for completed messages */}
462469
{!isStreaming && cleanTextContent && (
463470
<div className='flex items-center gap-[8px] pt-[8px]'>

apps/sim/stores/panel/copilot/store.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,11 @@ function createStreamingMessage(): CopilotMessage {
533533
}
534534
}
535535

536-
function createErrorMessage(messageId: string, content: string): CopilotMessage {
536+
function createErrorMessage(
537+
messageId: string,
538+
content: string,
539+
errorType?: 'usage_limit' | 'unauthorized' | 'forbidden' | 'rate_limit' | 'upgrade_required'
540+
): CopilotMessage {
537541
return {
538542
id: messageId,
539543
role: 'assistant',
@@ -546,6 +550,7 @@ function createErrorMessage(messageId: string, content: string): CopilotMessage
546550
timestamp: Date.now(),
547551
},
548552
],
553+
errorType,
549554
}
550555
}
551556

@@ -2066,23 +2071,35 @@ export const useCopilotStore = create<CopilotStore>()(
20662071

20672072
// Check for specific status codes and provide custom messages
20682073
let errorContent = result.error || 'Failed to send message'
2074+
let errorType:
2075+
| 'usage_limit'
2076+
| 'unauthorized'
2077+
| 'forbidden'
2078+
| 'rate_limit'
2079+
| 'upgrade_required'
2080+
| undefined
20692081
if (result.status === 401) {
20702082
errorContent =
20712083
'_Unauthorized request. You need a valid API key to use the copilot. You can get one by going to [sim.ai](https://sim.ai) settings and generating one there._'
2084+
errorType = 'unauthorized'
20722085
} else if (result.status === 402) {
20732086
errorContent =
2074-
'_Usage limit exceeded. To continue using this service, upgrade your plan or top up on credits._'
2087+
'_Usage limit exceeded. To continue using this service, upgrade your plan or increase your usage limit to:_'
2088+
errorType = 'usage_limit'
20752089
} else if (result.status === 403) {
20762090
errorContent =
20772091
'_Provider config not allowed for non-enterprise users. Please remove the provider config and try again_'
2092+
errorType = 'forbidden'
20782093
} else if (result.status === 426) {
20792094
errorContent =
20802095
'_Please upgrade to the latest version of the Sim platform to continue using the copilot._'
2096+
errorType = 'upgrade_required'
20812097
} else if (result.status === 429) {
20822098
errorContent = '_Provider rate limit exceeded. Please try again later._'
2099+
errorType = 'rate_limit'
20832100
}
20842101

2085-
const errorMessage = createErrorMessage(streamingMessage.id, errorContent)
2102+
const errorMessage = createErrorMessage(streamingMessage.id, errorContent, errorType)
20862103
set((state) => ({
20872104
messages: state.messages.map((m) => (m.id === streamingMessage.id ? errorMessage : m)),
20882105
error: errorContent,

apps/sim/stores/panel/copilot/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface CopilotMessage {
3939
>
4040
fileAttachments?: MessageFileAttachment[]
4141
contexts?: ChatContext[]
42+
errorType?: 'usage_limit' | 'unauthorized' | 'forbidden' | 'rate_limit' | 'upgrade_required'
4243
}
4344

4445
// Contexts attached to a user message

0 commit comments

Comments
 (0)