mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-22 15:01:13 +03:00
feat: add JSONPathExtractor tool (#5052)
* feat: add JSONPathExtractor tool with lodash-based path extraction - Implement JSONPathExtractor tool for extracting values from JSON using path notation - Use lodash.get for robust path extraction supporting edge cases (numeric string keys, array indexing) - Add configurable error handling with returnNullOnError parameter - Include comprehensive test suite with 34 tests covering all scenarios - Support JSON strings, objects, and arrays as input * fix lint * Update pnpm-lock.yaml * fix: exclude test files from TypeScript compilation Prevents test files from being included in the dist folder which was causing "jest is not defined" errors during server startup. --------- Co-authored-by: Henry Heng <henryheng@flowiseai.com>
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import { z } from 'zod'
|
||||
import { StructuredTool } from '@langchain/core/tools'
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses } from '../../../src/utils'
|
||||
import { get } from 'lodash'
|
||||
|
||||
/**
|
||||
* Tool that extracts values from JSON using path
|
||||
*/
|
||||
class JSONPathExtractorTool extends StructuredTool {
|
||||
name = 'json_path_extractor'
|
||||
description = 'Extract value from JSON using configured path'
|
||||
|
||||
schema = z.object({
|
||||
json: z
|
||||
.union([z.string().describe('JSON string'), z.record(z.any()).describe('JSON object'), z.array(z.any()).describe('JSON array')])
|
||||
.describe('JSON data to extract value from')
|
||||
})
|
||||
|
||||
private readonly path: string
|
||||
private readonly returnNullOnError: boolean
|
||||
|
||||
constructor(path: string, returnNullOnError: boolean = false) {
|
||||
super()
|
||||
this.path = path
|
||||
this.returnNullOnError = returnNullOnError
|
||||
}
|
||||
|
||||
async _call({ json }: z.infer<typeof this.schema>): Promise<string> {
|
||||
// Validate that path is configured
|
||||
if (!this.path) {
|
||||
if (this.returnNullOnError) {
|
||||
return 'null'
|
||||
}
|
||||
throw new Error('No extraction path configured')
|
||||
}
|
||||
|
||||
let data: any
|
||||
|
||||
// Parse JSON string if needed
|
||||
if (typeof json === 'string') {
|
||||
try {
|
||||
data = JSON.parse(json)
|
||||
} catch (error) {
|
||||
if (this.returnNullOnError) {
|
||||
return 'null'
|
||||
}
|
||||
throw new Error(`Invalid JSON string: ${error instanceof Error ? error.message : 'Parse error'}`)
|
||||
}
|
||||
} else {
|
||||
data = json
|
||||
}
|
||||
|
||||
// Extract value using lodash get
|
||||
const value = get(data, this.path)
|
||||
|
||||
if (value === undefined) {
|
||||
if (this.returnNullOnError) {
|
||||
return 'null'
|
||||
}
|
||||
const jsonPreview = JSON.stringify(data, null, 2)
|
||||
const preview = jsonPreview.length > 200 ? jsonPreview.substring(0, 200) + '...' : jsonPreview
|
||||
throw new Error(`Path "${this.path}" not found in JSON. Received: ${preview}`)
|
||||
}
|
||||
|
||||
return typeof value === 'string' ? value : JSON.stringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Node implementation for JSON Path Extractor tool
|
||||
*/
|
||||
class JSONPathExtractor_Tools implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
description: string
|
||||
baseClasses: string[]
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'JSON Path Extractor'
|
||||
this.name = 'jsonPathExtractor'
|
||||
this.version = 1.0
|
||||
this.type = 'JSONPathExtractor'
|
||||
this.icon = 'jsonpathextractor.svg'
|
||||
this.category = 'Tools'
|
||||
this.description = 'Extract values from JSON using path expressions'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(JSONPathExtractorTool)]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'JSON Path',
|
||||
name: 'path',
|
||||
type: 'string',
|
||||
description: 'Path to extract. Examples: data, user.name, items[0].id',
|
||||
placeholder: 'data'
|
||||
},
|
||||
{
|
||||
label: 'Return Null on Error',
|
||||
name: 'returnNullOnError',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return null instead of throwing error when extraction fails',
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, _: string): Promise<any> {
|
||||
const path = (nodeData.inputs?.path as string) || ''
|
||||
const returnNullOnError = (nodeData.inputs?.returnNullOnError as boolean) || false
|
||||
|
||||
if (!path) {
|
||||
throw new Error('JSON Path is required')
|
||||
}
|
||||
|
||||
return new JSONPathExtractorTool(path, returnNullOnError)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: JSONPathExtractor_Tools }
|
||||
Reference in New Issue
Block a user