Skip to content
Prev Previous commit
Next Next commit
stronger typing
  • Loading branch information
waleedlatif1 committed Feb 7, 2026
commit b9764648b189132aba2977c4afe7bc38be94c943
241 changes: 129 additions & 112 deletions apps/sim/providers/anthropic/core.ts

Large diffs are not rendered by default.

45 changes: 27 additions & 18 deletions apps/sim/providers/azure-openai/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { createLogger } from '@sim/logger'
import { AzureOpenAI } from 'openai'
import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions'
import type {
ChatCompletion,
ChatCompletionCreateParamsBase,
ChatCompletionCreateParamsStreaming,
ChatCompletionMessageParam,
ChatCompletionTool,
ChatCompletionToolChoiceOption,
} from 'openai/resources/chat/completions'
import type { ReasoningEffort } from 'openai/resources/shared'
import { env } from '@/lib/core/config/env'
import type { StreamingExecution } from '@/executor/types'
import { MAX_TOOL_ITERATIONS } from '@/providers'
Expand All @@ -16,6 +24,7 @@ import {
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
import { executeResponsesProviderRequest } from '@/providers/openai/core'
import type {
FunctionCallResponse,
ProviderConfig,
ProviderRequest,
ProviderResponse,
Expand Down Expand Up @@ -59,7 +68,7 @@ async function executeChatCompletionsRequest(
endpoint: azureEndpoint,
})

const allMessages: any[] = []
const allMessages: ChatCompletionMessageParam[] = []

if (request.systemPrompt) {
allMessages.push({
Expand All @@ -76,12 +85,12 @@ async function executeChatCompletionsRequest(
}

if (request.messages) {
allMessages.push(...request.messages)
allMessages.push(...(request.messages as ChatCompletionMessageParam[]))
}

const tools = request.tools?.length
const tools: ChatCompletionTool[] | undefined = request.tools?.length
? request.tools.map((tool) => ({
type: 'function',
type: 'function' as const,
function: {
name: tool.id,
description: tool.description,
Expand All @@ -90,7 +99,7 @@ async function executeChatCompletionsRequest(
}))
: undefined

const payload: any = {
const payload: ChatCompletionCreateParamsBase & { verbosity?: string } = {
model: deploymentName,
messages: allMessages,
}
Expand All @@ -99,7 +108,7 @@ async function executeChatCompletionsRequest(
if (request.maxTokens != null) payload.max_completion_tokens = request.maxTokens

if (request.reasoningEffort !== undefined && request.reasoningEffort !== 'auto')
payload.reasoning_effort = request.reasoningEffort
payload.reasoning_effort = request.reasoningEffort as ReasoningEffort
if (request.verbosity !== undefined && request.verbosity !== 'auto')
payload.verbosity = request.verbosity

Expand All @@ -123,8 +132,8 @@ async function executeChatCompletionsRequest(
const { tools: filteredTools, toolChoice } = preparedTools

if (filteredTools?.length && toolChoice) {
payload.tools = filteredTools
payload.tool_choice = toolChoice
payload.tools = filteredTools as ChatCompletionTool[]
payload.tool_choice = toolChoice as ChatCompletionToolChoiceOption

logger.info('Azure OpenAI request configuration:', {
toolCount: filteredTools.length,
Expand Down Expand Up @@ -233,7 +242,7 @@ async function executeChatCompletionsRequest(
const forcedTools = preparedTools?.forcedTools || []
let usedForcedTools: string[] = []

let currentResponse = await azureOpenAI.chat.completions.create(payload)
let currentResponse = (await azureOpenAI.chat.completions.create(payload)) as ChatCompletion
const firstResponseTime = Date.now() - initialCallTime

let content = currentResponse.choices[0]?.message?.content || ''
Expand All @@ -242,8 +251,8 @@ async function executeChatCompletionsRequest(
output: currentResponse.usage?.completion_tokens || 0,
total: currentResponse.usage?.total_tokens || 0,
}
const toolCalls = []
const toolResults = []
const toolCalls: (FunctionCallResponse & { success: boolean })[] = []
const toolResults: Record<string, unknown>[] = []
const currentMessages = [...allMessages]
let iterationCount = 0
let modelTime = firstResponseTime
Expand All @@ -262,7 +271,7 @@ async function executeChatCompletionsRequest(

const firstCheckResult = checkForForcedToolUsage(
currentResponse,
originalToolChoice,
originalToolChoice ?? 'auto',
logger,
forcedTools,
usedForcedTools
Expand Down Expand Up @@ -358,10 +367,10 @@ async function executeChatCompletionsRequest(
duration: duration,
})

let resultContent: any
let resultContent: Record<string, unknown>
if (result.success) {
toolResults.push(result.output)
resultContent = result.output
toolResults.push(result.output as Record<string, unknown>)
resultContent = result.output as Record<string, unknown>
} else {
resultContent = {
error: true,
Expand Down Expand Up @@ -411,11 +420,11 @@ async function executeChatCompletionsRequest(
}

const nextModelStartTime = Date.now()
currentResponse = await azureOpenAI.chat.completions.create(nextPayload)
currentResponse = (await azureOpenAI.chat.completions.create(nextPayload)) as ChatCompletion

const nextCheckResult = checkForForcedToolUsage(
currentResponse,
nextPayload.tool_choice,
nextPayload.tool_choice ?? 'auto',
logger,
forcedTools,
usedForcedTools
Expand Down
5 changes: 3 additions & 2 deletions apps/sim/providers/azure-openai/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Logger } from '@sim/logger'
import type OpenAI from 'openai'
import type { ChatCompletionChunk } from 'openai/resources/chat/completions'
import type { CompletionUsage } from 'openai/resources/completions'
import type { Stream } from 'openai/streaming'
Expand All @@ -20,8 +21,8 @@ export function createReadableStreamFromAzureOpenAIStream(
* Uses the shared OpenAI-compatible forced tool usage helper.
*/
export function checkForForcedToolUsage(
response: any,
toolChoice: string | { type: string; function?: { name: string }; name?: string; any?: any },
response: OpenAI.Chat.Completions.ChatCompletion,
toolChoice: string | { type: string; function?: { name: string }; name?: string },
_logger: Logger,
forcedTools: string[],
usedForcedTools: string[]
Expand Down
2 changes: 2 additions & 0 deletions apps/sim/providers/bedrock/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ export const bedrockProvider: ProviderConfig = {
input: initialCost.input,
output: initialCost.output,
total: initialCost.total,
pricing: initialCost.pricing,
}

const toolCalls: any[] = []
Expand Down Expand Up @@ -867,6 +868,7 @@ export const bedrockProvider: ProviderConfig = {
input: cost.input,
output: cost.output,
total: cost.total,
pricing: cost.pricing,
},
toolCalls:
toolCalls.length > 0
Expand Down
50 changes: 31 additions & 19 deletions apps/sim/providers/openai/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Logger } from '@sim/logger'
import type OpenAI from 'openai'
import type { StreamingExecution } from '@/executor/types'
import { MAX_TOOL_ITERATIONS } from '@/providers'
import type { Message, ProviderRequest, ProviderResponse, TimeSegment } from '@/providers/types'
Expand Down Expand Up @@ -30,7 +31,7 @@ type ToolChoice = PreparedTools['toolChoice']
* - Sets additionalProperties: false on all object types.
* - Ensures required includes ALL property keys.
*/
function enforceStrictSchema(schema: any): any {
function enforceStrictSchema(schema: Record<string, unknown>): Record<string, unknown> {
if (!schema || typeof schema !== 'object') return schema

const result = { ...schema }
Expand All @@ -41,31 +42,37 @@ function enforceStrictSchema(schema: any): any {

// Recursively process properties and ensure required includes all keys
if (result.properties && typeof result.properties === 'object') {
const propKeys = Object.keys(result.properties)
const propKeys = Object.keys(result.properties as Record<string, unknown>)
result.required = propKeys // Strict mode requires ALL properties
result.properties = Object.fromEntries(
Object.entries(result.properties).map(([key, value]) => [key, enforceStrictSchema(value)])
Object.entries(result.properties as Record<string, unknown>).map(([key, value]) => [
key,
enforceStrictSchema(value as Record<string, unknown>),
])
)
}
}

// Handle array items
if (result.type === 'array' && result.items) {
result.items = enforceStrictSchema(result.items)
result.items = enforceStrictSchema(result.items as Record<string, unknown>)
}

// Handle anyOf, oneOf, allOf
for (const keyword of ['anyOf', 'oneOf', 'allOf']) {
if (Array.isArray(result[keyword])) {
result[keyword] = result[keyword].map(enforceStrictSchema)
result[keyword] = (result[keyword] as Record<string, unknown>[]).map(enforceStrictSchema)
}
}

// Handle $defs / definitions
for (const defKey of ['$defs', 'definitions']) {
if (result[defKey] && typeof result[defKey] === 'object') {
result[defKey] = Object.fromEntries(
Object.entries(result[defKey]).map(([key, value]) => [key, enforceStrictSchema(value)])
Object.entries(result[defKey] as Record<string, unknown>).map(([key, value]) => [
key,
enforceStrictSchema(value as Record<string, unknown>),
])
)
}
}
Expand Down Expand Up @@ -123,7 +130,7 @@ export async function executeResponsesProviderRequest(

const initialInput = buildResponsesInputFromMessages(allMessages)

const basePayload: Record<string, any> = {
const basePayload: Record<string, unknown> = {
model: config.modelName,
}

Expand All @@ -139,13 +146,13 @@ export async function executeResponsesProviderRequest(

if (request.verbosity !== undefined && request.verbosity !== 'auto') {
basePayload.text = {
...(basePayload.text ?? {}),
...((basePayload.text as Record<string, unknown>) ?? {}),
verbosity: request.verbosity,
}
}

// Store response format config - for Azure with tools, we defer applying it until after tool calls complete
let deferredTextFormat: { type: string; name: string; schema: any; strict: boolean } | undefined
let deferredTextFormat: OpenAI.Responses.ResponseFormatTextJSONSchemaConfig | undefined
const hasTools = !!request.tools?.length
const isAzure = config.providerId === 'azure-openai'

Expand All @@ -171,7 +178,7 @@ export async function executeResponsesProviderRequest(
)
} else {
basePayload.text = {
...(basePayload.text ?? {}),
...((basePayload.text as Record<string, unknown>) ?? {}),
format: textFormat,
}
logger.info(`Added JSON schema response format to ${config.providerLabel} request`)
Expand Down Expand Up @@ -231,7 +238,10 @@ export async function executeResponsesProviderRequest(
}
}

const createRequestBody = (input: ResponsesInputItem[], overrides: Record<string, any> = {}) => ({
const createRequestBody = (
input: ResponsesInputItem[],
overrides: Record<string, unknown> = {}
) => ({
...basePayload,
input,
...overrides,
Expand All @@ -247,7 +257,9 @@ export async function executeResponsesProviderRequest(
}
}

const postResponses = async (body: Record<string, any>) => {
const postResponses = async (
body: Record<string, unknown>
): Promise<OpenAI.Responses.Response> => {
const response = await fetch(config.endpoint, {
method: 'POST',
headers: config.headers,
Expand Down Expand Up @@ -496,10 +508,10 @@ export async function executeResponsesProviderRequest(
duration: duration,
})

let resultContent: any
let resultContent: Record<string, unknown>
if (result.success) {
toolResults.push(result.output)
resultContent = result.output
resultContent = result.output as Record<string, unknown>
} else {
resultContent = {
error: true,
Expand Down Expand Up @@ -615,11 +627,11 @@ export async function executeResponsesProviderRequest(
}

// Make final call with the response format - build payload without tools
const finalPayload: Record<string, any> = {
const finalPayload: Record<string, unknown> = {
model: config.modelName,
input: formattedInput,
text: {
...(basePayload.text ?? {}),
...((basePayload.text as Record<string, unknown>) ?? {}),
format: deferredTextFormat,
},
}
Expand All @@ -635,7 +647,7 @@ export async function executeResponsesProviderRequest(
}
if (request.verbosity !== undefined && request.verbosity !== 'auto') {
finalPayload.text = {
...finalPayload.text,
...((finalPayload.text as Record<string, unknown>) ?? {}),
verbosity: request.verbosity,
}
}
Expand Down Expand Up @@ -679,10 +691,10 @@ export async function executeResponsesProviderRequest(
const accumulatedCost = calculateCost(request.model, tokens.input, tokens.output)

// For Azure with deferred format in streaming mode, include the format in the streaming call
const streamOverrides: Record<string, any> = { stream: true, tool_choice: 'auto' }
const streamOverrides: Record<string, unknown> = { stream: true, tool_choice: 'auto' }
if (deferredTextFormat) {
streamOverrides.text = {
...(basePayload.text ?? {}),
...((basePayload.text as Record<string, unknown>) ?? {}),
format: deferredTextFormat,
}
}
Expand Down
Loading