Skip to content

Commit afc1632

Browse files
authored
Merge pull request #1245 from simstudioai/fix/copilot-billing
improvement(copilot): billing multiplier adjustments
2 parents 1de5966 + 56eee2c commit afc1632

File tree

5 files changed

+48
-15
lines changed

5 files changed

+48
-15
lines changed

apps/docs/content/docs/copilot/index.mdx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,31 @@ Copilot is your in-editor assistant that helps you build, understand, and improv
9191
>
9292
<div className="m-0 text-sm">Maximum reasoning for deep planning, debugging, and complex architectural changes.</div>
9393
</Card>
94-
</Cards>
94+
</Cards>
95+
96+
## Billing and Cost Calculation
97+
98+
### How Costs Are Calculated
99+
100+
Copilot usage is billed per token from the underlying LLM:
101+
102+
- **Input tokens**: billed at the provider's base rate (**at-cost**)
103+
- **Output tokens**: billed at **1.5×** the provider's base output rate
104+
105+
```javascript
106+
copilotCost = (inputTokens × inputPrice + outputTokens × (outputPrice × 1.5)) / 1,000,000
107+
```
108+
109+
| Component | Rate Applied |
110+
|----------|----------------------|
111+
| Input | inputPrice |
112+
| Output | outputPrice × 1.5 |
113+
114+
<Callout type="warning">
115+
Pricing shown reflects rates as of September 4, 2025. Check provider documentation for current pricing.
116+
</Callout>
117+
118+
<Callout type="info">
119+
Model prices are per million tokens. The calculation divides by 1,000,000 to get the actual cost. See <a href="/execution/advanced#cost-calculation">Logging and Cost Calculation</a> for background and examples.
120+
</Callout>
121+

apps/sim/app/api/billing/update-cost/route.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ const UpdateCostSchema = z.object({
1616
input: z.number().min(0, 'Input tokens must be a non-negative number'),
1717
output: z.number().min(0, 'Output tokens must be a non-negative number'),
1818
model: z.string().min(1, 'Model is required'),
19-
multiplier: z.number().min(0),
19+
inputMultiplier: z.number().min(0),
20+
outputMultiplier: z.number().min(0),
2021
})
2122

2223
/**
@@ -75,14 +76,15 @@ export async function POST(req: NextRequest) {
7576
)
7677
}
7778

78-
const { userId, input, output, model, multiplier } = validation.data
79+
const { userId, input, output, model, inputMultiplier, outputMultiplier } = validation.data
7980

8081
logger.info(`[${requestId}] Processing cost update`, {
8182
userId,
8283
input,
8384
output,
8485
model,
85-
multiplier,
86+
inputMultiplier,
87+
outputMultiplier,
8688
})
8789

8890
const finalPromptTokens = input
@@ -95,7 +97,8 @@ export async function POST(req: NextRequest) {
9597
finalPromptTokens,
9698
finalCompletionTokens,
9799
false,
98-
multiplier
100+
inputMultiplier,
101+
outputMultiplier
99102
)
100103

101104
logger.info(`[${requestId}] Cost calculation result`, {
@@ -104,7 +107,8 @@ export async function POST(req: NextRequest) {
104107
promptTokens: finalPromptTokens,
105108
completionTokens: finalCompletionTokens,
106109
totalTokens: totalTokens,
107-
multiplier,
110+
inputMultiplier,
111+
outputMultiplier,
108112
costResult,
109113
})
110114

apps/sim/app/api/copilot/chat/route.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ describe('Copilot Chat API Route', () => {
226226
mode: 'agent',
227227
messageId: 'mock-uuid-1234-5678',
228228
depth: 0,
229+
chatId: 'chat-123',
229230
}),
230231
})
231232
)
@@ -289,6 +290,7 @@ describe('Copilot Chat API Route', () => {
289290
mode: 'agent',
290291
messageId: 'mock-uuid-1234-5678',
291292
depth: 0,
293+
chatId: 'chat-123',
292294
}),
293295
})
294296
)
@@ -341,6 +343,7 @@ describe('Copilot Chat API Route', () => {
341343
mode: 'agent',
342344
messageId: 'mock-uuid-1234-5678',
343345
depth: 0,
346+
chatId: 'chat-123',
344347
}),
345348
})
346349
)
@@ -430,6 +433,7 @@ describe('Copilot Chat API Route', () => {
430433
mode: 'ask',
431434
messageId: 'mock-uuid-1234-5678',
432435
depth: 0,
436+
chatId: 'chat-123',
433437
}),
434438
})
435439
)

apps/sim/app/api/copilot/chat/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ export async function POST(req: NextRequest) {
378378
...(typeof effectivePrefetch === 'boolean' ? { prefetch: effectivePrefetch } : {}),
379379
...(session?.user?.name && { userName: session.user.name }),
380380
...(agentContexts.length > 0 && { context: agentContexts }),
381+
...(actualChatId ? { chatId: actualChatId } : {}),
381382
}
382383

383384
try {

apps/sim/providers/utils.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getCostMultiplier, isHosted } from '@/lib/environment'
1+
import { isHosted } from '@/lib/environment'
22
import { createLogger } from '@/lib/logs/console/logger'
33
import { anthropicProvider } from '@/providers/anthropic'
44
import { azureOpenAIProvider } from '@/providers/azure-openai'
@@ -444,7 +444,8 @@ export function calculateCost(
444444
promptTokens = 0,
445445
completionTokens = 0,
446446
useCachedInput = false,
447-
customMultiplier?: number
447+
inputMultiplier?: number,
448+
outputMultiplier?: number
448449
) {
449450
// First check if it's an embedding model
450451
let pricing = getEmbeddingModelPricing(model)
@@ -479,13 +480,9 @@ export function calculateCost(
479480
: pricing.input / 1_000_000)
480481

481482
const outputCost = completionTokens * (pricing.output / 1_000_000)
482-
const totalCost = inputCost + outputCost
483-
484-
const costMultiplier = customMultiplier ?? getCostMultiplier()
485-
486-
const finalInputCost = inputCost * costMultiplier
487-
const finalOutputCost = outputCost * costMultiplier
488-
const finalTotalCost = totalCost * costMultiplier
483+
const finalInputCost = inputCost * (inputMultiplier ?? 1)
484+
const finalOutputCost = outputCost * (outputMultiplier ?? 1)
485+
const finalTotalCost = finalInputCost + finalOutputCost
489486

490487
return {
491488
input: Number.parseFloat(finalInputCost.toFixed(8)), // Use 8 decimal places for small costs

0 commit comments

Comments
 (0)