Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/components/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -2183,6 +2183,27 @@
}
]
},
{
"name": "baiduQianfanEmbeddings",
"models": [
{
"label": "Embedding-V1",
"name": "Embedding-V1"
},
{
"label": "bge-large-zh",
"name": "bge-large-zh"
},
{
"label": "bge-large-en",
"name": "bge-large-en"
},
{
"label": "tao-8k",
"name": "tao-8k"
}
]
},
{
"name": "mistralAIEmbeddings",
"models": [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
jest.mock('@langchain/baidu-qianfan', () => ({
BaiduQianfanEmbeddings: jest.fn().mockImplementation((fields) => ({ fields }))
}))

jest.mock('../../../src/utils', () => ({
getBaseClasses: jest.fn().mockReturnValue(['Embeddings']),
getCredentialData: jest.fn(),
getCredentialParam: jest.fn()
}))

jest.mock('../../../src/modelLoader', () => ({
MODEL_TYPE: { EMBEDDING: 'embedding' },
getModels: jest.fn()
}))

import { getCredentialData, getCredentialParam } from '../../../src/utils'
import { getModels } from '../../../src/modelLoader'

const { nodeClass: BaiduQianfanEmbedding } = require('./BaiduQianfanEmbedding')

describe('BaiduQianfanEmbedding', () => {
beforeEach(() => {
jest.clearAllMocks()
})

it('loads embedding model options from the shared model loader', async () => {
;(getModels as jest.Mock).mockResolvedValue([{ label: 'Embedding-V1', name: 'Embedding-V1' }])

const node = new BaiduQianfanEmbedding()
const models = await node.loadMethods.listModels()

expect(getModels).toHaveBeenCalledWith('embedding', 'baiduQianfanEmbeddings')
expect(models).toEqual([{ label: 'Embedding-V1', name: 'Embedding-V1' }])
})

it('maps credential, custom model names, and optional embedding parameters into BaiduQianfanEmbeddings', async () => {
;(getCredentialData as jest.Mock).mockResolvedValue({
qianfanAccessKey: 'access-key',
qianfanSecretKey: 'secret-key'
})
;(getCredentialParam as jest.Mock).mockImplementation((key, credentialData) => credentialData[key])

const node = new BaiduQianfanEmbedding()
const model = await node.init(
{
credential: 'cred-1',
inputs: {
modelName: 'bge-large-zh',
customModelName: 'Qwen3-Embedding-4B',
stripNewLines: true,
batchSize: '8',
timeout: '15000'
}
},
'',
{}
)

expect(model.fields).toMatchObject({
modelName: 'Qwen3-Embedding-4B',
qianfanAccessKey: 'access-key',
qianfanSecretKey: 'secret-key',
stripNewLines: true,
batchSize: 8,
timeout: 15000
})
})

it('preserves explicit zero values for numeric parameters', async () => {
;(getCredentialData as jest.Mock).mockResolvedValue({
qianfanAccessKey: 'access-key',
qianfanSecretKey: 'secret-key'
})
;(getCredentialParam as jest.Mock).mockImplementation((key, credentialData) => credentialData[key])

const node = new BaiduQianfanEmbedding()
const model = await node.init(
{
credential: 'cred-1',
inputs: {
modelName: 'Embedding-V1',
batchSize: '0',
timeout: '0'
}
},
'',
{}
)

expect(model.fields).toMatchObject({
modelName: 'Embedding-V1',
batchSize: 0,
timeout: 0
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { BaiduQianfanEmbeddings, BaiduQianfanEmbeddingsParams } from '@langchain/baidu-qianfan'
import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface'
import { MODEL_TYPE, getModels } from '../../../src/modelLoader'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'

class BaiduQianfanEmbedding_Embeddings implements INode {
label: string
name: string
version: number
type: string
icon: string
category: string
description: string
baseClasses: string[]
credential: INodeParams
inputs: INodeParams[]

constructor() {
this.label = 'Baidu Qianfan Embedding'
this.name = 'baiduQianfanEmbeddings'
this.version = 1.0
this.type = 'BaiduQianfanEmbeddings'
this.icon = 'baiduwenxin.svg'
this.category = 'Embeddings'
this.description = 'Baidu Qianfan API to generate embeddings for a given text'
this.baseClasses = [this.type, ...getBaseClasses(BaiduQianfanEmbeddings)]
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['baiduQianfanApi']
}
this.inputs = [
{
label: 'Model Name',
name: 'modelName',
type: 'asyncOptions',
loadMethod: 'listModels',
default: 'Embedding-V1'
},
{
label: 'Custom Model Name',
name: 'customModelName',
type: 'string',
placeholder: 'Qwen3-Embedding-4B',
description: 'Custom model name to use. If provided, it will override the selected model.',
additionalParams: true,
optional: true
},
{
label: 'Strip New Lines',
name: 'stripNewLines',
type: 'boolean',
optional: true,
additionalParams: true,
description: 'Remove new lines from input text before embedding to reduce token count'
},
{
label: 'Batch Size',
name: 'batchSize',
type: 'number',
optional: true,
default: 1,
additionalParams: true,
description: 'Number of texts sent in each embedding request',
warning:
'Qianfan has stricter limits on individual text length. If you encounter a length error, reduce chunk size to 500 and set Batch Size to 1.'
},
{
label: 'Timeout',
name: 'timeout',
type: 'number',
optional: true,
additionalParams: true,
description: 'Request timeout in milliseconds'
}
]
}

//@ts-ignore
loadMethods = {
async listModels(): Promise<INodeOptionsValue[]> {
return await getModels(MODEL_TYPE.EMBEDDING, 'baiduQianfanEmbeddings')
}
}

async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const modelName = nodeData.inputs?.modelName as BaiduQianfanEmbeddingsParams['modelName']
const customModelName = nodeData.inputs?.customModelName as string
const stripNewLines = nodeData.inputs?.stripNewLines as boolean
const batchSize = nodeData.inputs?.batchSize as string
const timeout = nodeData.inputs?.timeout as string

const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const qianfanAccessKey = getCredentialParam('qianfanAccessKey', credentialData, nodeData)
const qianfanSecretKey = getCredentialParam('qianfanSecretKey', credentialData, nodeData)

const obj: Partial<BaiduQianfanEmbeddingsParams> & {
qianfanAccessKey?: string
qianfanSecretKey?: string
} = {
modelName: (customModelName || modelName) as BaiduQianfanEmbeddingsParams['modelName'],
qianfanAccessKey,
qianfanSecretKey
}

if (typeof stripNewLines === 'boolean') obj.stripNewLines = stripNewLines
if (batchSize !== undefined && batchSize !== null && batchSize !== '') obj.batchSize = parseInt(batchSize, 10)
if (timeout !== undefined && timeout !== null && timeout !== '') obj.timeout = parseInt(timeout, 10)

const model = new BaiduQianfanEmbeddings(obj)
return model
}
}

module.exports = { nodeClass: BaiduQianfanEmbedding_Embeddings }
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.