Skip to content

Commit 92992e7

Browse files
committed
added utils files for each provider, consolidated gemini utils, added dynamic verbosity and reasoning fetcher
1 parent 0e4d1ea commit 92992e7

File tree

32 files changed

+1422
-1117
lines changed

32 files changed

+1422
-1117
lines changed

apps/docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"license": "Apache-2.0",
66
"scripts": {
7-
"dev": "next dev --port 3001",
7+
"dev": "next dev --port 7322",
88
"build": "fumadocs-mdx && NODE_OPTIONS='--max-old-space-size=8192' next build",
99
"start": "next start",
1010
"postinstall": "fumadocs-mdx",

apps/sim/blocks/blocks/agent.ts

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
getHostedModels,
99
getMaxTemperature,
1010
getProviderIcon,
11+
getReasoningEffortValuesForModel,
12+
getVerbosityValuesForModel,
1113
MODELS_WITH_REASONING_EFFORT,
1214
MODELS_WITH_VERBOSITY,
1315
providers,
@@ -114,12 +116,47 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
114116
type: 'dropdown',
115117
placeholder: 'Select reasoning effort...',
116118
options: [
117-
{ label: 'none', id: 'none' },
118-
{ label: 'minimal', id: 'minimal' },
119119
{ label: 'low', id: 'low' },
120120
{ label: 'medium', id: 'medium' },
121121
{ label: 'high', id: 'high' },
122122
],
123+
dependsOn: ['model'],
124+
fetchOptions: async (blockId: string) => {
125+
const { useSubBlockStore } = await import('@/stores/workflows/subblock/store')
126+
const { useWorkflowRegistry } = await import('@/stores/workflows/registry/store')
127+
128+
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
129+
if (!activeWorkflowId) {
130+
return [
131+
{ label: 'low', id: 'low' },
132+
{ label: 'medium', id: 'medium' },
133+
{ label: 'high', id: 'high' },
134+
]
135+
}
136+
137+
const workflowValues = useSubBlockStore.getState().workflowValues[activeWorkflowId]
138+
const blockValues = workflowValues?.[blockId]
139+
const modelValue = blockValues?.model as string
140+
141+
if (!modelValue) {
142+
return [
143+
{ label: 'low', id: 'low' },
144+
{ label: 'medium', id: 'medium' },
145+
{ label: 'high', id: 'high' },
146+
]
147+
}
148+
149+
const validOptions = getReasoningEffortValuesForModel(modelValue)
150+
if (!validOptions) {
151+
return [
152+
{ label: 'low', id: 'low' },
153+
{ label: 'medium', id: 'medium' },
154+
{ label: 'high', id: 'high' },
155+
]
156+
}
157+
158+
return validOptions.map((opt) => ({ label: opt, id: opt }))
159+
},
123160
value: () => 'medium',
124161
condition: {
125162
field: 'model',
@@ -136,6 +173,43 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
136173
{ label: 'medium', id: 'medium' },
137174
{ label: 'high', id: 'high' },
138175
],
176+
dependsOn: ['model'],
177+
fetchOptions: async (blockId: string) => {
178+
const { useSubBlockStore } = await import('@/stores/workflows/subblock/store')
179+
const { useWorkflowRegistry } = await import('@/stores/workflows/registry/store')
180+
181+
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
182+
if (!activeWorkflowId) {
183+
return [
184+
{ label: 'low', id: 'low' },
185+
{ label: 'medium', id: 'medium' },
186+
{ label: 'high', id: 'high' },
187+
]
188+
}
189+
190+
const workflowValues = useSubBlockStore.getState().workflowValues[activeWorkflowId]
191+
const blockValues = workflowValues?.[blockId]
192+
const modelValue = blockValues?.model as string
193+
194+
if (!modelValue) {
195+
return [
196+
{ label: 'low', id: 'low' },
197+
{ label: 'medium', id: 'medium' },
198+
{ label: 'high', id: 'high' },
199+
]
200+
}
201+
202+
const validOptions = getVerbosityValuesForModel(modelValue)
203+
if (!validOptions) {
204+
return [
205+
{ label: 'low', id: 'low' },
206+
{ label: 'medium', id: 'medium' },
207+
{ label: 'high', id: 'high' },
208+
]
209+
}
210+
211+
return validOptions.map((opt) => ({ label: opt, id: opt }))
212+
},
139213
value: () => 'medium',
140214
condition: {
141215
field: 'model',

apps/sim/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"node": ">=20.0.0"
99
},
1010
"scripts": {
11-
"dev": "next dev --port 3000",
11+
"dev": "next dev --port 7321",
1212
"dev:webpack": "next dev --webpack",
1313
"dev:sockets": "bun run socket-server/index.ts",
1414
"dev:full": "concurrently -n \"App,Realtime\" -c \"cyan,magenta\" \"bun run dev\" \"bun run dev:sockets\"",

apps/sim/providers/anthropic/index.ts

Lines changed: 53 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,23 @@ import Anthropic from '@anthropic-ai/sdk'
22
import { createLogger } from '@/lib/logs/console/logger'
33
import type { StreamingExecution } from '@/executor/types'
44
import { MAX_TOOL_ITERATIONS } from '@/providers'
5+
import {
6+
checkForForcedToolUsage,
7+
createReadableStreamFromAnthropicStream,
8+
generateToolUseId,
9+
} from '@/providers/anthropic/utils'
10+
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
11+
import type {
12+
ProviderConfig,
13+
ProviderRequest,
14+
ProviderResponse,
15+
TimeSegment,
16+
} from '@/providers/types'
17+
import { prepareToolExecution, prepareToolsWithUsageControl } from '@/providers/utils'
518
import { executeTool } from '@/tools'
6-
import { getProviderDefaultModel, getProviderModels } from '../models'
7-
import type { ProviderConfig, ProviderRequest, ProviderResponse, TimeSegment } from '../types'
8-
import { prepareToolExecution, prepareToolsWithUsageControl, trackForcedToolUsage } from '../utils'
919

1020
const logger = createLogger('AnthropicProvider')
1121

12-
/**
13-
* Helper to wrap Anthropic streaming into a browser-friendly ReadableStream
14-
*/
15-
function createReadableStreamFromAnthropicStream(
16-
anthropicStream: AsyncIterable<any>
17-
): ReadableStream {
18-
return new ReadableStream({
19-
async start(controller) {
20-
try {
21-
for await (const event of anthropicStream) {
22-
if (event.type === 'content_block_delta' && event.delta?.text) {
23-
controller.enqueue(new TextEncoder().encode(event.delta.text))
24-
}
25-
}
26-
controller.close()
27-
} catch (err) {
28-
controller.error(err)
29-
}
30-
},
31-
})
32-
}
33-
3422
export const anthropicProvider: ProviderConfig = {
3523
id: 'anthropic',
3624
name: 'Anthropic',
@@ -48,11 +36,6 @@ export const anthropicProvider: ProviderConfig = {
4836

4937
const anthropic = new Anthropic({ apiKey: request.apiKey })
5038

51-
// Helper function to generate a simple unique ID for tool uses
52-
const generateToolUseId = (toolName: string) => {
53-
return `${toolName}-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`
54-
}
55-
5639
// Transform messages to Anthropic format
5740
const messages: any[] = []
5841

@@ -393,44 +376,17 @@ ${fieldDescriptions}
393376
},
394377
]
395378

396-
// Helper function to check for forced tool usage in Anthropic responses
397-
const checkForForcedToolUsage = (response: any, toolChoice: any) => {
398-
if (
399-
typeof toolChoice === 'object' &&
400-
toolChoice !== null &&
401-
Array.isArray(response.content)
402-
) {
403-
const toolUses = response.content.filter((item: any) => item.type === 'tool_use')
404-
405-
if (toolUses.length > 0) {
406-
// Convert Anthropic tool_use format to a format trackForcedToolUsage can understand
407-
const adaptedToolCalls = toolUses.map((tool: any) => ({
408-
name: tool.name,
409-
}))
410-
411-
// Convert Anthropic tool_choice format to match OpenAI format for tracking
412-
const adaptedToolChoice =
413-
toolChoice.type === 'tool' ? { function: { name: toolChoice.name } } : toolChoice
414-
415-
const result = trackForcedToolUsage(
416-
adaptedToolCalls,
417-
adaptedToolChoice,
418-
logger,
419-
'anthropic',
420-
forcedTools,
421-
usedForcedTools
422-
)
423-
// Make the behavior consistent with the initial check
424-
hasUsedForcedTool = result.hasUsedForcedTool
425-
usedForcedTools = result.usedForcedTools
426-
return result
427-
}
428-
}
429-
return null
430-
}
431-
432379
// Check if a forced tool was used in the first response
433-
checkForForcedToolUsage(currentResponse, originalToolChoice)
380+
const firstCheckResult = checkForForcedToolUsage(
381+
currentResponse,
382+
originalToolChoice,
383+
forcedTools,
384+
usedForcedTools
385+
)
386+
if (firstCheckResult) {
387+
hasUsedForcedTool = firstCheckResult.hasUsedForcedTool
388+
usedForcedTools = firstCheckResult.usedForcedTools
389+
}
434390

435391
try {
436392
while (iterationCount < MAX_TOOL_ITERATIONS) {
@@ -576,7 +532,16 @@ ${fieldDescriptions}
576532
currentResponse = await anthropic.messages.create(nextPayload)
577533

578534
// Check if any forced tools were used in this response
579-
checkForForcedToolUsage(currentResponse, nextPayload.tool_choice)
535+
const nextCheckResult = checkForForcedToolUsage(
536+
currentResponse,
537+
nextPayload.tool_choice,
538+
forcedTools,
539+
usedForcedTools
540+
)
541+
if (nextCheckResult) {
542+
hasUsedForcedTool = nextCheckResult.hasUsedForcedTool
543+
usedForcedTools = nextCheckResult.usedForcedTools
544+
}
580545

581546
const nextModelEndTime = Date.now()
582547
const thisModelTime = nextModelEndTime - nextModelStartTime
@@ -746,44 +711,17 @@ ${fieldDescriptions}
746711
},
747712
]
748713

749-
// Helper function to check for forced tool usage in Anthropic responses
750-
const checkForForcedToolUsage = (response: any, toolChoice: any) => {
751-
if (
752-
typeof toolChoice === 'object' &&
753-
toolChoice !== null &&
754-
Array.isArray(response.content)
755-
) {
756-
const toolUses = response.content.filter((item: any) => item.type === 'tool_use')
757-
758-
if (toolUses.length > 0) {
759-
// Convert Anthropic tool_use format to a format trackForcedToolUsage can understand
760-
const adaptedToolCalls = toolUses.map((tool: any) => ({
761-
name: tool.name,
762-
}))
763-
764-
// Convert Anthropic tool_choice format to match OpenAI format for tracking
765-
const adaptedToolChoice =
766-
toolChoice.type === 'tool' ? { function: { name: toolChoice.name } } : toolChoice
767-
768-
const result = trackForcedToolUsage(
769-
adaptedToolCalls,
770-
adaptedToolChoice,
771-
logger,
772-
'anthropic',
773-
forcedTools,
774-
usedForcedTools
775-
)
776-
// Make the behavior consistent with the initial check
777-
hasUsedForcedTool = result.hasUsedForcedTool
778-
usedForcedTools = result.usedForcedTools
779-
return result
780-
}
781-
}
782-
return null
783-
}
784-
785714
// Check if a forced tool was used in the first response
786-
checkForForcedToolUsage(currentResponse, originalToolChoice)
715+
const firstCheckResult = checkForForcedToolUsage(
716+
currentResponse,
717+
originalToolChoice,
718+
forcedTools,
719+
usedForcedTools
720+
)
721+
if (firstCheckResult) {
722+
hasUsedForcedTool = firstCheckResult.hasUsedForcedTool
723+
usedForcedTools = firstCheckResult.usedForcedTools
724+
}
787725

788726
try {
789727
while (iterationCount < MAX_TOOL_ITERATIONS) {
@@ -925,7 +863,16 @@ ${fieldDescriptions}
925863
currentResponse = await anthropic.messages.create(nextPayload)
926864

927865
// Check if any forced tools were used in this response
928-
checkForForcedToolUsage(currentResponse, nextPayload.tool_choice)
866+
const nextCheckResult = checkForForcedToolUsage(
867+
currentResponse,
868+
nextPayload.tool_choice,
869+
forcedTools,
870+
usedForcedTools
871+
)
872+
if (nextCheckResult) {
873+
hasUsedForcedTool = nextCheckResult.hasUsedForcedTool
874+
usedForcedTools = nextCheckResult.usedForcedTools
875+
}
929876

930877
const nextModelEndTime = Date.now()
931878
const thisModelTime = nextModelEndTime - nextModelStartTime
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { createLogger } from '@/lib/logs/console/logger'
2+
import { trackForcedToolUsage } from '@/providers/utils'
3+
4+
const logger = createLogger('AnthropicUtils')
5+
6+
/**
7+
* Helper to wrap Anthropic streaming into a browser-friendly ReadableStream
8+
*/
9+
export function createReadableStreamFromAnthropicStream(
10+
anthropicStream: AsyncIterable<any>
11+
): ReadableStream {
12+
return new ReadableStream({
13+
async start(controller) {
14+
try {
15+
for await (const event of anthropicStream) {
16+
if (event.type === 'content_block_delta' && event.delta?.text) {
17+
controller.enqueue(new TextEncoder().encode(event.delta.text))
18+
}
19+
}
20+
controller.close()
21+
} catch (err) {
22+
controller.error(err)
23+
}
24+
},
25+
})
26+
}
27+
28+
/**
29+
* Helper function to generate a simple unique ID for tool uses
30+
*/
31+
export function generateToolUseId(toolName: string): string {
32+
return `${toolName}-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`
33+
}
34+
35+
/**
36+
* Helper function to check for forced tool usage in Anthropic responses
37+
*/
38+
export function checkForForcedToolUsage(
39+
response: any,
40+
toolChoice: any,
41+
forcedTools: string[],
42+
usedForcedTools: string[]
43+
): { hasUsedForcedTool: boolean; usedForcedTools: string[] } | null {
44+
if (typeof toolChoice === 'object' && toolChoice !== null && Array.isArray(response.content)) {
45+
const toolUses = response.content.filter((item: any) => item.type === 'tool_use')
46+
47+
if (toolUses.length > 0) {
48+
// Convert Anthropic tool_use format to a format trackForcedToolUsage can understand
49+
const adaptedToolCalls = toolUses.map((tool: any) => ({
50+
name: tool.name,
51+
}))
52+
53+
// Convert Anthropic tool_choice format to match OpenAI format for tracking
54+
const adaptedToolChoice =
55+
toolChoice.type === 'tool' ? { function: { name: toolChoice.name } } : toolChoice
56+
57+
const result = trackForcedToolUsage(
58+
adaptedToolCalls,
59+
adaptedToolChoice,
60+
logger,
61+
'anthropic',
62+
forcedTools,
63+
usedForcedTools
64+
)
65+
66+
return result
67+
}
68+
}
69+
return null
70+
}

0 commit comments

Comments
 (0)