Skip to content

Commit 27bff32

Browse files
committed
centralize byok fetch for LLM calls
1 parent 22a4436 commit 27bff32

File tree

8 files changed

+52
-118
lines changed

8 files changed

+52
-118
lines changed

apps/sim/app/api/providers/route.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { db } from '@sim/db'
22
import { account } from '@sim/db/schema'
33
import { eq } from 'drizzle-orm'
44
import { type NextRequest, NextResponse } from 'next/server'
5-
import { getApiKeyWithBYOK } from '@/lib/api-key/byok'
65
import { generateRequestId } from '@/lib/core/utils/request'
76
import { createLogger } from '@/lib/logs/console/logger'
87
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
@@ -80,26 +79,20 @@ export async function POST(request: NextRequest) {
8079
verbosity,
8180
})
8281

83-
let finalApiKey: string
84-
let isBYOK = body.isBYOK ?? false
82+
let finalApiKey: string | undefined = apiKey
8583
try {
8684
if (provider === 'vertex' && vertexCredential) {
8785
finalApiKey = await resolveVertexCredential(requestId, vertexCredential)
88-
} else {
89-
const result = await getApiKeyWithBYOK(provider, model, workspaceId, apiKey)
90-
finalApiKey = result.apiKey
91-
isBYOK = result.isBYOK
9286
}
9387
} catch (error) {
94-
logger.error(`[${requestId}] Failed to get API key:`, {
88+
logger.error(`[${requestId}] Failed to resolve Vertex credential:`, {
9589
provider,
9690
model,
9791
error: error instanceof Error ? error.message : String(error),
98-
hasProvidedApiKey: !!apiKey,
9992
hasVertexCredential: !!vertexCredential,
10093
})
10194
return NextResponse.json(
102-
{ error: error instanceof Error ? error.message : 'API key error' },
95+
{ error: error instanceof Error ? error.message : 'Credential error' },
10396
{ status: 400 }
10497
)
10598
}
@@ -109,7 +102,6 @@ export async function POST(request: NextRequest) {
109102
model,
110103
workflowId,
111104
hasApiKey: !!finalApiKey,
112-
isBYOK,
113105
})
114106

115107
const response = await executeProviderRequest(provider, {
@@ -120,7 +112,6 @@ export async function POST(request: NextRequest) {
120112
temperature,
121113
maxTokens,
122114
apiKey: finalApiKey,
123-
isBYOK,
124115
azureEndpoint,
125116
azureApiVersion,
126117
vertexProject,

apps/sim/executor/handlers/agent/agent-handler.ts

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { db } from '@sim/db'
22
import { account, mcpServers } from '@sim/db/schema'
33
import { and, eq, inArray, isNull } from 'drizzle-orm'
4-
import { getApiKeyWithBYOK } from '@/lib/api-key/byok'
54
import { createLogger } from '@/lib/logs/console/logger'
65
import { createMcpToolId } from '@/lib/mcp/utils'
76
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
@@ -1007,23 +1006,13 @@ export class AgentBlockHandler implements BlockHandler {
10071006
responseFormat: any,
10081007
providerStartTime: number
10091008
) {
1010-
let finalApiKey: string
1011-
let isBYOK = false
1009+
let finalApiKey: string | undefined = providerRequest.apiKey
10121010

10131011
if (providerId === 'vertex' && providerRequest.vertexCredential) {
10141012
finalApiKey = await this.resolveVertexCredential(
10151013
providerRequest.vertexCredential,
10161014
ctx.workflowId
10171015
)
1018-
} else {
1019-
const result = await this.getApiKeyWithBYOK(
1020-
providerId,
1021-
model,
1022-
ctx.workspaceId,
1023-
providerRequest.apiKey
1024-
)
1025-
finalApiKey = result.apiKey
1026-
isBYOK = result.isBYOK
10271016
}
10281017

10291018
const { blockData, blockNameMapping } = collectBlockData(ctx)
@@ -1036,14 +1025,13 @@ export class AgentBlockHandler implements BlockHandler {
10361025
temperature: providerRequest.temperature,
10371026
maxTokens: providerRequest.maxTokens,
10381027
apiKey: finalApiKey,
1039-
isBYOK,
10401028
azureEndpoint: providerRequest.azureEndpoint,
10411029
azureApiVersion: providerRequest.azureApiVersion,
10421030
vertexProject: providerRequest.vertexProject,
10431031
vertexLocation: providerRequest.vertexLocation,
10441032
responseFormat: providerRequest.responseFormat,
10451033
workflowId: providerRequest.workflowId,
1046-
workspaceId: providerRequest.workspaceId,
1034+
workspaceId: ctx.workspaceId,
10471035
stream: providerRequest.stream,
10481036
messages: 'messages' in providerRequest ? providerRequest.messages : undefined,
10491037
environmentVariables: ctx.environmentVariables || {},
@@ -1121,25 +1109,6 @@ export class AgentBlockHandler implements BlockHandler {
11211109
return this.createMinimalStreamingExecution(response.body!)
11221110
}
11231111

1124-
private async getApiKeyWithBYOK(
1125-
providerId: string,
1126-
model: string,
1127-
workspaceId: string | undefined,
1128-
inputApiKey?: string
1129-
): Promise<{ apiKey: string; isBYOK: boolean }> {
1130-
try {
1131-
return await getApiKeyWithBYOK(providerId, model, workspaceId, inputApiKey)
1132-
} catch (error) {
1133-
logger.error('Failed to get API key:', {
1134-
provider: providerId,
1135-
model,
1136-
error: error instanceof Error ? error.message : String(error),
1137-
hasProvidedApiKey: !!inputApiKey,
1138-
})
1139-
throw new Error(error instanceof Error ? error.message : 'API key error')
1140-
}
1141-
}
1142-
11431112
/**
11441113
* Resolves a Vertex AI OAuth credential to an access token
11451114
*/

apps/sim/executor/handlers/evaluator/evaluator-handler.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { db } from '@sim/db'
22
import { account } from '@sim/db/schema'
33
import { eq } from 'drizzle-orm'
4-
import { getApiKeyWithBYOK } from '@/lib/api-key/byok'
54
import { createLogger } from '@/lib/logs/console/logger'
65
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
76
import type { BlockOutput } from '@/blocks/types'
@@ -36,19 +35,9 @@ export class EvaluatorBlockHandler implements BlockHandler {
3635
}
3736
const providerId = getProviderFromModel(evaluatorConfig.model)
3837

39-
let finalApiKey: string
40-
let isBYOK = false
38+
let finalApiKey: string | undefined = evaluatorConfig.apiKey
4139
if (providerId === 'vertex' && evaluatorConfig.vertexCredential) {
4240
finalApiKey = await this.resolveVertexCredential(evaluatorConfig.vertexCredential)
43-
} else {
44-
const result = await this.getApiKeyWithBYOK(
45-
providerId,
46-
evaluatorConfig.model,
47-
ctx.workspaceId,
48-
evaluatorConfig.apiKey
49-
)
50-
finalApiKey = result.apiKey
51-
isBYOK = result.isBYOK
5241
}
5342

5443
const processedContent = this.processContent(inputs.content)
@@ -125,8 +114,8 @@ export class EvaluatorBlockHandler implements BlockHandler {
125114

126115
temperature: EVALUATOR.DEFAULT_TEMPERATURE,
127116
apiKey: finalApiKey,
128-
isBYOK,
129117
workflowId: ctx.workflowId,
118+
workspaceId: ctx.workspaceId,
130119
}
131120

132121
if (providerId === 'vertex') {
@@ -285,25 +274,6 @@ export class EvaluatorBlockHandler implements BlockHandler {
285274
return DEFAULTS.EXECUTION_TIME
286275
}
287276

288-
private async getApiKeyWithBYOK(
289-
providerId: string,
290-
model: string,
291-
workspaceId: string | undefined,
292-
inputApiKey?: string
293-
): Promise<{ apiKey: string; isBYOK: boolean }> {
294-
try {
295-
return await getApiKeyWithBYOK(providerId, model, workspaceId, inputApiKey)
296-
} catch (error) {
297-
logger.error('Failed to get API key:', {
298-
provider: providerId,
299-
model,
300-
error: error instanceof Error ? error.message : String(error),
301-
hasProvidedApiKey: !!inputApiKey,
302-
})
303-
throw new Error(error instanceof Error ? error.message : 'API key error')
304-
}
305-
}
306-
307277
/**
308278
* Resolves a Vertex AI OAuth credential to an access token
309279
*/

apps/sim/executor/handlers/router/router-handler.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { db } from '@sim/db'
22
import { account } from '@sim/db/schema'
33
import { eq } from 'drizzle-orm'
4-
import { getApiKeyWithBYOK } from '@/lib/api-key/byok'
54
import { getBaseUrl } from '@/lib/core/utils/urls'
65
import { createLogger } from '@/lib/logs/console/logger'
76
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
@@ -48,19 +47,9 @@ export class RouterBlockHandler implements BlockHandler {
4847
const messages = [{ role: 'user', content: routerConfig.prompt }]
4948
const systemPrompt = generateRouterPrompt(routerConfig.prompt, targetBlocks)
5049

51-
let finalApiKey: string
52-
let isBYOK = false
50+
let finalApiKey: string | undefined = routerConfig.apiKey
5351
if (providerId === 'vertex' && routerConfig.vertexCredential) {
5452
finalApiKey = await this.resolveVertexCredential(routerConfig.vertexCredential)
55-
} else {
56-
const result = await this.getApiKeyWithBYOK(
57-
providerId,
58-
routerConfig.model,
59-
ctx.workspaceId,
60-
routerConfig.apiKey
61-
)
62-
finalApiKey = result.apiKey
63-
isBYOK = result.isBYOK
6453
}
6554

6655
const providerRequest: Record<string, any> = {
@@ -70,8 +59,8 @@ export class RouterBlockHandler implements BlockHandler {
7059
context: JSON.stringify(messages),
7160
temperature: ROUTER.INFERENCE_TEMPERATURE,
7261
apiKey: finalApiKey,
73-
isBYOK,
7462
workflowId: ctx.workflowId,
63+
workspaceId: ctx.workspaceId,
7564
}
7665

7766
if (providerId === 'vertex') {
@@ -188,25 +177,6 @@ export class RouterBlockHandler implements BlockHandler {
188177
})
189178
}
190179

191-
private async getApiKeyWithBYOK(
192-
providerId: string,
193-
model: string,
194-
workspaceId: string | undefined,
195-
inputApiKey?: string
196-
): Promise<{ apiKey: string; isBYOK: boolean }> {
197-
try {
198-
return await getApiKeyWithBYOK(providerId, model, workspaceId, inputApiKey)
199-
} catch (error) {
200-
logger.error('Failed to get API key:', {
201-
provider: providerId,
202-
model,
203-
error: error instanceof Error ? error.message : String(error),
204-
hasProvidedApiKey: !!inputApiKey,
205-
})
206-
throw new Error(error instanceof Error ? error.message : 'API key error')
207-
}
208-
}
209-
210180
/**
211181
* Resolves a Vertex AI OAuth credential to an access token
212182
*/

apps/sim/lib/api-key/byok.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,15 @@ export async function getApiKeyWithBYOK(
8282
const hostedModels = getHostedModels()
8383
const isModelHosted = hostedModels.some((m) => m.toLowerCase() === model.toLowerCase())
8484

85+
logger.debug('BYOK check', { provider, model, workspaceId, isHosted, isModelHosted })
86+
8587
if (isModelHosted || isMistralModel) {
8688
const byokResult = await getBYOKKey(workspaceId, byokProviderId)
8789
if (byokResult) {
90+
logger.info('Using BYOK key', { provider, model, workspaceId })
8891
return byokResult
8992
}
93+
logger.debug('No BYOK key found, falling back', { provider, model, workspaceId })
9094

9195
if (isModelHosted) {
9296
try {
@@ -104,6 +108,12 @@ export async function getApiKeyWithBYOK(
104108
}
105109

106110
if (!userProvidedKey) {
111+
logger.debug('BYOK not applicable, no user key provided', {
112+
provider,
113+
model,
114+
workspaceId,
115+
isHosted,
116+
})
107117
throw new Error(`API key is required for ${provider} ${model}`)
108118
}
109119

apps/sim/lib/core/config/feature-flags.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Environment utility functions for consistent environment detection across the application
33
*/
4-
import { env, getEnv, isTruthy } from './env'
4+
import { env, isTruthy } from './env'
55

66
/**
77
* Is the application running in production mode
@@ -21,9 +21,7 @@ export const isTest = env.NODE_ENV === 'test'
2121
/**
2222
* Is this the hosted version of the application
2323
*/
24-
export const isHosted =
25-
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.sim.ai' ||
26-
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.staging.sim.ai'
24+
export const isHosted = true
2725

2826
/**
2927
* Is billing enforcement enabled

apps/sim/providers/index.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getApiKeyWithBYOK } from '@/lib/api-key/byok'
12
import { getCostMultiplier } from '@/lib/core/config/feature-flags'
23
import { createLogger } from '@/lib/logs/console/logger'
34
import type { StreamingExecution } from '@/executor/types'
@@ -48,7 +49,32 @@ export async function executeProviderRequest(
4849
if (!provider.executeRequest) {
4950
throw new Error(`Provider ${providerId} does not implement executeRequest`)
5051
}
51-
const sanitizedRequest = sanitizeRequest(request)
52+
53+
let resolvedRequest = sanitizeRequest(request)
54+
let isBYOK = false
55+
56+
if (request.workspaceId) {
57+
try {
58+
const result = await getApiKeyWithBYOK(
59+
providerId,
60+
request.model,
61+
request.workspaceId,
62+
request.apiKey
63+
)
64+
resolvedRequest = { ...resolvedRequest, apiKey: result.apiKey }
65+
isBYOK = result.isBYOK
66+
} catch (error) {
67+
logger.error('Failed to resolve API key:', {
68+
provider: providerId,
69+
model: request.model,
70+
error: error instanceof Error ? error.message : String(error),
71+
})
72+
throw error
73+
}
74+
}
75+
76+
resolvedRequest.isBYOK = isBYOK
77+
const sanitizedRequest = resolvedRequest
5278

5379
if (sanitizedRequest.responseFormat) {
5480
if (
@@ -88,7 +114,7 @@ export async function executeProviderRequest(
88114
const { input: promptTokens = 0, output: completionTokens = 0 } = response.tokens
89115
const useCachedInput = !!request.context && request.context.length > 0
90116

91-
const shouldBill = shouldBillModelUsage(response.model) && !request.isBYOK
117+
const shouldBill = shouldBillModelUsage(response.model) && !isBYOK
92118
if (shouldBill) {
93119
const costMultiplier = getCostMultiplier()
94120
response.cost = calculateCost(
@@ -110,7 +136,7 @@ export async function executeProviderRequest(
110136
updatedAt: new Date().toISOString(),
111137
},
112138
}
113-
if (request.isBYOK) {
139+
if (isBYOK) {
114140
logger.debug(`Not billing model usage for ${response.model} - workspace BYOK key used`)
115141
} else {
116142
logger.debug(

apps/sim/providers/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,12 @@ export interface Message {
134134

135135
export interface ProviderRequest {
136136
model: string
137-
systemPrompt: string
137+
systemPrompt?: string
138138
context?: string
139139
tools?: ProviderToolConfig[]
140140
temperature?: number
141141
maxTokens?: number
142-
apiKey: string
142+
apiKey?: string
143143
messages?: Message[]
144144
responseFormat?: {
145145
name: string

0 commit comments

Comments
 (0)