Skip to content

Commit fe577b0

Browse files
author
Theodore Li
committed
Revert "feat(hosted key): Add exa hosted key (#3221)"
This reverts commit 158d523.
1 parent f88926a commit fe577b0

File tree

52 files changed

+335
-2840
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+335
-2840
lines changed

apps/sim/app/api/workspaces/[id]/byok-keys/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { getUserEntityPermissions, getWorkspaceById } from '@/lib/workspaces/per
1313

1414
const logger = createLogger('WorkspaceBYOKKeysAPI')
1515

16-
const VALID_PROVIDERS = ['openai', 'anthropic', 'google', 'mistral', 'exa'] as const
16+
const VALID_PROVIDERS = ['openai', 'anthropic', 'google', 'mistral'] as const
1717

1818
const UpsertKeySchema = z.object({
1919
providerId: z.enum(VALID_PROVIDERS),

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/hooks/use-editor-subblock-layout.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
buildCanonicalIndex,
44
evaluateSubBlockCondition,
55
isSubBlockFeatureEnabled,
6-
isSubBlockHiddenByHostedKey,
76
isSubBlockVisibleForMode,
87
} from '@/lib/workflows/subblocks/visibility'
98
import type { BlockConfig, SubBlockConfig, SubBlockType } from '@/blocks/types'
@@ -109,9 +108,6 @@ export function useEditorSubblockLayout(
109108
// Check required feature if specified - declarative feature gating
110109
if (!isSubBlockFeatureEnabled(block)) return false
111110

112-
// Hide tool API key fields when hosted
113-
if (isSubBlockHiddenByHostedKey(block)) return false
114-
115111
// Special handling for trigger-config type (legacy trigger configuration UI)
116112
if (block.type === ('trigger-config' as SubBlockType)) {
117113
const isPureTriggerBlock = config?.triggers?.enabled && config.category === 'triggers'

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
evaluateSubBlockCondition,
1717
hasAdvancedValues,
1818
isSubBlockFeatureEnabled,
19-
isSubBlockHiddenByHostedKey,
2019
isSubBlockVisibleForMode,
2120
resolveDependencyValue,
2221
} from '@/lib/workflows/subblocks/visibility'
@@ -978,7 +977,6 @@ export const WorkflowBlock = memo(function WorkflowBlock({
978977
if (block.hidden) return false
979978
if (block.hideFromPreview) return false
980979
if (!isSubBlockFeatureEnabled(block)) return false
981-
if (isSubBlockHiddenByHostedKey(block)) return false
982980

983981
const isPureTriggerBlock = config?.triggers?.enabled && config.category === 'triggers'
984982

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/byok/byok.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import {
1313
ModalFooter,
1414
ModalHeader,
1515
} from '@/components/emcn'
16-
import { AnthropicIcon, ExaAIIcon, GeminiIcon, MistralIcon, OpenAIIcon } from '@/components/icons'
16+
import { AnthropicIcon, GeminiIcon, MistralIcon, OpenAIIcon } from '@/components/icons'
1717
import { Skeleton } from '@/components/ui'
1818
import {
1919
type BYOKKey,
20+
type BYOKProviderId,
2021
useBYOKKeys,
2122
useDeleteBYOKKey,
2223
useUpsertBYOKKey,
2324
} from '@/hooks/queries/byok-keys'
24-
import type { BYOKProviderId } from '@/tools/types'
2525

2626
const logger = createLogger('BYOKSettings')
2727

@@ -60,13 +60,6 @@ const PROVIDERS: {
6060
description: 'LLM calls and Knowledge Base OCR',
6161
placeholder: 'Enter your API key',
6262
},
63-
{
64-
id: 'exa',
65-
name: 'Exa',
66-
icon: ExaAIIcon,
67-
description: 'AI-powered search and research',
68-
placeholder: 'Enter your Exa API key',
69-
},
7063
]
7164

7265
function BYOKKeySkeleton() {

apps/sim/blocks/blocks/exa.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -309,26 +309,14 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
309309
value: () => 'exa-research',
310310
condition: { field: 'operation', value: 'exa_research' },
311311
},
312-
// API Key — hidden when hosted for operations with hosted key support
312+
// API Key (common)
313313
{
314314
id: 'apiKey',
315315
title: 'API Key',
316316
type: 'short-input',
317317
placeholder: 'Enter your Exa API key',
318318
password: true,
319319
required: true,
320-
hideWhenHosted: true,
321-
condition: { field: 'operation', value: 'exa_research', not: true },
322-
},
323-
// API Key — always visible for research (no hosted key support)
324-
{
325-
id: 'apiKey',
326-
title: 'API Key',
327-
type: 'short-input',
328-
placeholder: 'Enter your Exa API key',
329-
password: true,
330-
required: true,
331-
condition: { field: 'operation', value: 'exa_research' },
332320
},
333321
],
334322
tools: {

apps/sim/blocks/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ export interface SubBlockConfig {
253253
hidden?: boolean
254254
hideFromPreview?: boolean // Hide this subblock from the workflow block preview
255255
requiresFeature?: string // Environment variable name that must be truthy for this subblock to be visible
256-
hideWhenHosted?: boolean // Hide this subblock when running on hosted sim
257256
description?: string
258257
tooltip?: string // Tooltip text displayed via info icon next to the title
259258
value?: (params: Record<string, any>) => string

apps/sim/executor/handlers/generic/generic-handler.test.ts

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,219 @@ describe('GenericBlockHandler', () => {
147147
'Block execution of Some Custom Tool failed with no error message'
148148
)
149149
})
150+
151+
describe('Knowledge block cost tracking', () => {
152+
beforeEach(() => {
153+
// Set up knowledge block mock
154+
mockBlock = {
155+
...mockBlock,
156+
config: { tool: 'knowledge_search', params: {} },
157+
}
158+
159+
mockTool = {
160+
...mockTool,
161+
id: 'knowledge_search',
162+
name: 'Knowledge Search',
163+
}
164+
165+
mockGetTool.mockImplementation((toolId) => {
166+
if (toolId === 'knowledge_search') {
167+
return mockTool
168+
}
169+
return undefined
170+
})
171+
})
172+
173+
it.concurrent(
174+
'should extract and restructure cost information from knowledge tools',
175+
async () => {
176+
const inputs = { query: 'test query' }
177+
const mockToolResponse = {
178+
success: true,
179+
output: {
180+
results: [],
181+
query: 'test query',
182+
totalResults: 0,
183+
cost: {
184+
input: 0.00001042,
185+
output: 0,
186+
total: 0.00001042,
187+
tokens: {
188+
input: 521,
189+
output: 0,
190+
total: 521,
191+
},
192+
model: 'text-embedding-3-small',
193+
pricing: {
194+
input: 0.02,
195+
output: 0,
196+
updatedAt: '2025-07-10',
197+
},
198+
},
199+
},
200+
}
201+
202+
mockExecuteTool.mockResolvedValue(mockToolResponse)
203+
204+
const result = await handler.execute(mockContext, mockBlock, inputs)
205+
206+
// Verify cost information is restructured correctly for enhanced logging
207+
expect(result).toEqual({
208+
results: [],
209+
query: 'test query',
210+
totalResults: 0,
211+
cost: {
212+
input: 0.00001042,
213+
output: 0,
214+
total: 0.00001042,
215+
},
216+
tokens: {
217+
input: 521,
218+
output: 0,
219+
total: 521,
220+
},
221+
model: 'text-embedding-3-small',
222+
})
223+
}
224+
)
225+
226+
it.concurrent('should handle knowledge_upload_chunk cost information', async () => {
227+
// Update to upload_chunk tool
228+
mockBlock.config.tool = 'knowledge_upload_chunk'
229+
mockTool.id = 'knowledge_upload_chunk'
230+
mockTool.name = 'Knowledge Upload Chunk'
231+
232+
mockGetTool.mockImplementation((toolId) => {
233+
if (toolId === 'knowledge_upload_chunk') {
234+
return mockTool
235+
}
236+
return undefined
237+
})
238+
239+
const inputs = { content: 'test content' }
240+
const mockToolResponse = {
241+
success: true,
242+
output: {
243+
data: {
244+
id: 'chunk-123',
245+
content: 'test content',
246+
chunkIndex: 0,
247+
},
248+
message: 'Successfully uploaded chunk',
249+
documentId: 'doc-123',
250+
cost: {
251+
input: 0.00000521,
252+
output: 0,
253+
total: 0.00000521,
254+
tokens: {
255+
input: 260,
256+
output: 0,
257+
total: 260,
258+
},
259+
model: 'text-embedding-3-small',
260+
pricing: {
261+
input: 0.02,
262+
output: 0,
263+
updatedAt: '2025-07-10',
264+
},
265+
},
266+
},
267+
}
268+
269+
mockExecuteTool.mockResolvedValue(mockToolResponse)
270+
271+
const result = await handler.execute(mockContext, mockBlock, inputs)
272+
273+
// Verify cost information is restructured correctly
274+
expect(result).toEqual({
275+
data: {
276+
id: 'chunk-123',
277+
content: 'test content',
278+
chunkIndex: 0,
279+
},
280+
message: 'Successfully uploaded chunk',
281+
documentId: 'doc-123',
282+
cost: {
283+
input: 0.00000521,
284+
output: 0,
285+
total: 0.00000521,
286+
},
287+
tokens: {
288+
input: 260,
289+
output: 0,
290+
total: 260,
291+
},
292+
model: 'text-embedding-3-small',
293+
})
294+
})
295+
296+
it('should pass through output unchanged for knowledge tools without cost info', async () => {
297+
const inputs = { query: 'test query' }
298+
const mockToolResponse = {
299+
success: true,
300+
output: {
301+
results: [],
302+
query: 'test query',
303+
totalResults: 0,
304+
// No cost information
305+
},
306+
}
307+
308+
mockExecuteTool.mockResolvedValue(mockToolResponse)
309+
310+
const result = await handler.execute(mockContext, mockBlock, inputs)
311+
312+
// Should return original output without cost transformation
313+
expect(result).toEqual({
314+
results: [],
315+
query: 'test query',
316+
totalResults: 0,
317+
})
318+
})
319+
320+
it.concurrent(
321+
'should process cost info for all tools (universal cost extraction)',
322+
async () => {
323+
mockBlock.config.tool = 'some_other_tool'
324+
mockTool.id = 'some_other_tool'
325+
326+
mockGetTool.mockImplementation((toolId) => {
327+
if (toolId === 'some_other_tool') {
328+
return mockTool
329+
}
330+
return undefined
331+
})
332+
333+
const inputs = { param: 'value' }
334+
const mockToolResponse = {
335+
success: true,
336+
output: {
337+
result: 'success',
338+
cost: {
339+
input: 0.001,
340+
output: 0.002,
341+
total: 0.003,
342+
tokens: { input: 100, output: 50, total: 150 },
343+
model: 'some-model',
344+
},
345+
},
346+
}
347+
348+
mockExecuteTool.mockResolvedValue(mockToolResponse)
349+
350+
const result = await handler.execute(mockContext, mockBlock, inputs)
351+
352+
expect(result).toEqual({
353+
result: 'success',
354+
cost: {
355+
input: 0.001,
356+
output: 0.002,
357+
total: 0.003,
358+
},
359+
tokens: { input: 100, output: 50, total: 150 },
360+
model: 'some-model',
361+
})
362+
}
363+
)
364+
})
150365
})

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,27 @@ export class GenericBlockHandler implements BlockHandler {
9898
throw error
9999
}
100100

101-
return result.output
101+
const output = result.output
102+
let cost = null
103+
104+
if (output?.cost) {
105+
cost = output.cost
106+
}
107+
108+
if (cost) {
109+
return {
110+
...output,
111+
cost: {
112+
input: cost.input,
113+
output: cost.output,
114+
total: cost.total,
115+
},
116+
tokens: cost.tokens,
117+
model: cost.model,
118+
}
119+
}
120+
121+
return output
102122
} catch (error: any) {
103123
if (!error.message || error.message === 'undefined (undefined)') {
104124
let errorMessage = `Block execution of ${tool?.name || block.config.tool} failed`

apps/sim/hooks/queries/byok-keys.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { createLogger } from '@sim/logger'
22
import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
33
import { API_ENDPOINTS } from '@/stores/constants'
4-
import type { BYOKProviderId } from '@/tools/types'
54

65
const logger = createLogger('BYOKKeysQueries')
76

7+
export type BYOKProviderId = 'openai' | 'anthropic' | 'google' | 'mistral'
8+
89
export interface BYOKKey {
910
id: string
1011
providerId: BYOKProviderId

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { isHosted } from '@/lib/core/config/feature-flags'
77
import { decryptSecret } from '@/lib/core/security/encryption'
88
import { getHostedModels } from '@/providers/models'
99
import { useProvidersStore } from '@/stores/providers/store'
10-
import type { BYOKProviderId } from '@/tools/types'
1110

1211
const logger = createLogger('BYOKKeys')
1312

13+
export type BYOKProviderId = 'openai' | 'anthropic' | 'google' | 'mistral'
14+
1415
export interface BYOKKeyResult {
1516
apiKey: string
1617
isBYOK: true

0 commit comments

Comments
 (0)