Flowise/packages/components/nodes/chatmodels/ChatGoogleGenerativeAI/utils/tools.ts

137 lines
4.5 KiB
TypeScript

import {
Tool as GenerativeAITool,
ToolConfig,
FunctionCallingMode,
FunctionDeclaration,
FunctionDeclarationsTool,
FunctionDeclarationSchema
} from '@google/generative-ai'
import { ToolChoice } from '@langchain/core/language_models/chat_models'
import { StructuredToolInterface } from '@langchain/core/tools'
import { isLangChainTool } from '@langchain/core/utils/function_calling'
import { isOpenAITool, ToolDefinition } from '@langchain/core/language_models/base'
import { convertToGenerativeAITools } from './common.js'
import { GoogleGenerativeAIToolType } from './types.js'
import { removeAdditionalProperties } from './zod_to_genai_parameters.js'
export function convertToolsToGenAI(
tools: GoogleGenerativeAIToolType[],
extra?: {
toolChoice?: ToolChoice
allowedFunctionNames?: string[]
}
): {
tools: GenerativeAITool[]
toolConfig?: ToolConfig
} {
// Extract function declaration processing to a separate function
const genAITools = processTools(tools)
// Simplify tool config creation
const toolConfig = createToolConfig(genAITools, extra)
return { tools: genAITools, toolConfig }
}
function processTools(tools: GoogleGenerativeAIToolType[]): GenerativeAITool[] {
let functionDeclarationTools: FunctionDeclaration[] = []
const genAITools: GenerativeAITool[] = []
tools.forEach((tool) => {
if (isLangChainTool(tool)) {
const [convertedTool] = convertToGenerativeAITools([tool as StructuredToolInterface])
if (convertedTool.functionDeclarations) {
functionDeclarationTools.push(...convertedTool.functionDeclarations)
}
} else if (isOpenAITool(tool)) {
const { functionDeclarations } = convertOpenAIToolToGenAI(tool)
if (functionDeclarations) {
functionDeclarationTools.push(...functionDeclarations)
} else {
throw new Error('Failed to convert OpenAI structured tool to GenerativeAI tool')
}
} else {
genAITools.push(tool as GenerativeAITool)
}
})
const genAIFunctionDeclaration = genAITools.find((t) => 'functionDeclarations' in t)
if (genAIFunctionDeclaration) {
return genAITools.map((tool) => {
if (functionDeclarationTools?.length > 0 && 'functionDeclarations' in tool) {
const newTool = {
functionDeclarations: [...(tool.functionDeclarations || []), ...functionDeclarationTools]
}
// Clear the functionDeclarationTools array so it is not passed again
functionDeclarationTools = []
return newTool
}
return tool
})
}
return [
...genAITools,
...(functionDeclarationTools.length > 0
? [
{
functionDeclarations: functionDeclarationTools
}
]
: [])
]
}
function convertOpenAIToolToGenAI(tool: ToolDefinition): FunctionDeclarationsTool {
return {
functionDeclarations: [
{
name: tool.function.name,
description: tool.function.description,
parameters: removeAdditionalProperties(tool.function.parameters) as FunctionDeclarationSchema
}
]
}
}
function createToolConfig(
genAITools: GenerativeAITool[],
extra?: {
toolChoice?: ToolChoice
allowedFunctionNames?: string[]
}
): ToolConfig | undefined {
if (!genAITools.length || !extra) return undefined
const { toolChoice, allowedFunctionNames } = extra
const modeMap: Record<string, FunctionCallingMode> = {
any: FunctionCallingMode.ANY,
auto: FunctionCallingMode.AUTO,
none: FunctionCallingMode.NONE
}
if (toolChoice && ['any', 'auto', 'none'].includes(toolChoice as string)) {
return {
functionCallingConfig: {
mode: modeMap[toolChoice as keyof typeof modeMap] ?? 'MODE_UNSPECIFIED',
allowedFunctionNames
}
}
}
if (typeof toolChoice === 'string' || allowedFunctionNames) {
return {
functionCallingConfig: {
mode: FunctionCallingMode.ANY,
allowedFunctionNames: [
...(allowedFunctionNames ?? []),
...(toolChoice && typeof toolChoice === 'string' ? [toolChoice] : [])
]
}
}
}
return undefined
}