mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
Merge pull request #1245 from simstudioai/fix/copilot-billing
improvement(copilot): billing multiplier adjustments
This commit is contained in:
@@ -91,4 +91,31 @@ Copilot is your in-editor assistant that helps you build, understand, and improv
|
||||
>
|
||||
<div className="m-0 text-sm">Maximum reasoning for deep planning, debugging, and complex architectural changes.</div>
|
||||
</Card>
|
||||
</Cards>
|
||||
</Cards>
|
||||
|
||||
## Billing and Cost Calculation
|
||||
|
||||
### How Costs Are Calculated
|
||||
|
||||
Copilot usage is billed per token from the underlying LLM:
|
||||
|
||||
- **Input tokens**: billed at the provider's base rate (**at-cost**)
|
||||
- **Output tokens**: billed at **1.5×** the provider's base output rate
|
||||
|
||||
```javascript
|
||||
copilotCost = (inputTokens × inputPrice + outputTokens × (outputPrice × 1.5)) / 1,000,000
|
||||
```
|
||||
|
||||
| Component | Rate Applied |
|
||||
|----------|----------------------|
|
||||
| Input | inputPrice |
|
||||
| Output | outputPrice × 1.5 |
|
||||
|
||||
<Callout type="warning">
|
||||
Pricing shown reflects rates as of September 4, 2025. Check provider documentation for current pricing.
|
||||
</Callout>
|
||||
|
||||
<Callout type="info">
|
||||
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.
|
||||
</Callout>
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ const UpdateCostSchema = z.object({
|
||||
input: z.number().min(0, 'Input tokens must be a non-negative number'),
|
||||
output: z.number().min(0, 'Output tokens must be a non-negative number'),
|
||||
model: z.string().min(1, 'Model is required'),
|
||||
multiplier: z.number().min(0),
|
||||
inputMultiplier: z.number().min(0),
|
||||
outputMultiplier: z.number().min(0),
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -75,14 +76,15 @@ export async function POST(req: NextRequest) {
|
||||
)
|
||||
}
|
||||
|
||||
const { userId, input, output, model, multiplier } = validation.data
|
||||
const { userId, input, output, model, inputMultiplier, outputMultiplier } = validation.data
|
||||
|
||||
logger.info(`[${requestId}] Processing cost update`, {
|
||||
userId,
|
||||
input,
|
||||
output,
|
||||
model,
|
||||
multiplier,
|
||||
inputMultiplier,
|
||||
outputMultiplier,
|
||||
})
|
||||
|
||||
const finalPromptTokens = input
|
||||
@@ -95,7 +97,8 @@ export async function POST(req: NextRequest) {
|
||||
finalPromptTokens,
|
||||
finalCompletionTokens,
|
||||
false,
|
||||
multiplier
|
||||
inputMultiplier,
|
||||
outputMultiplier
|
||||
)
|
||||
|
||||
logger.info(`[${requestId}] Cost calculation result`, {
|
||||
@@ -104,7 +107,8 @@ export async function POST(req: NextRequest) {
|
||||
promptTokens: finalPromptTokens,
|
||||
completionTokens: finalCompletionTokens,
|
||||
totalTokens: totalTokens,
|
||||
multiplier,
|
||||
inputMultiplier,
|
||||
outputMultiplier,
|
||||
costResult,
|
||||
})
|
||||
|
||||
|
||||
@@ -226,6 +226,7 @@ describe('Copilot Chat API Route', () => {
|
||||
mode: 'agent',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
chatId: 'chat-123',
|
||||
}),
|
||||
})
|
||||
)
|
||||
@@ -289,6 +290,7 @@ describe('Copilot Chat API Route', () => {
|
||||
mode: 'agent',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
chatId: 'chat-123',
|
||||
}),
|
||||
})
|
||||
)
|
||||
@@ -341,6 +343,7 @@ describe('Copilot Chat API Route', () => {
|
||||
mode: 'agent',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
chatId: 'chat-123',
|
||||
}),
|
||||
})
|
||||
)
|
||||
@@ -430,6 +433,7 @@ describe('Copilot Chat API Route', () => {
|
||||
mode: 'ask',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
chatId: 'chat-123',
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
@@ -378,6 +378,7 @@ export async function POST(req: NextRequest) {
|
||||
...(typeof effectivePrefetch === 'boolean' ? { prefetch: effectivePrefetch } : {}),
|
||||
...(session?.user?.name && { userName: session.user.name }),
|
||||
...(agentContexts.length > 0 && { context: agentContexts }),
|
||||
...(actualChatId ? { chatId: actualChatId } : {}),
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCostMultiplier, isHosted } from '@/lib/environment'
|
||||
import { isHosted } from '@/lib/environment'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { anthropicProvider } from '@/providers/anthropic'
|
||||
import { azureOpenAIProvider } from '@/providers/azure-openai'
|
||||
@@ -444,7 +444,8 @@ export function calculateCost(
|
||||
promptTokens = 0,
|
||||
completionTokens = 0,
|
||||
useCachedInput = false,
|
||||
customMultiplier?: number
|
||||
inputMultiplier?: number,
|
||||
outputMultiplier?: number
|
||||
) {
|
||||
// First check if it's an embedding model
|
||||
let pricing = getEmbeddingModelPricing(model)
|
||||
@@ -479,13 +480,9 @@ export function calculateCost(
|
||||
: pricing.input / 1_000_000)
|
||||
|
||||
const outputCost = completionTokens * (pricing.output / 1_000_000)
|
||||
const totalCost = inputCost + outputCost
|
||||
|
||||
const costMultiplier = customMultiplier ?? getCostMultiplier()
|
||||
|
||||
const finalInputCost = inputCost * costMultiplier
|
||||
const finalOutputCost = outputCost * costMultiplier
|
||||
const finalTotalCost = totalCost * costMultiplier
|
||||
const finalInputCost = inputCost * (inputMultiplier ?? 1)
|
||||
const finalOutputCost = outputCost * (outputMultiplier ?? 1)
|
||||
const finalTotalCost = finalInputCost + finalOutputCost
|
||||
|
||||
return {
|
||||
input: Number.parseFloat(finalInputCost.toFixed(8)), // Use 8 decimal places for small costs
|
||||
|
||||
Reference in New Issue
Block a user