forked from newrelic/node-newrelic
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added instrumentation for VectorStore.similaritySearch for lang…
…chain.js (newrelic#2049) Co-authored-by: Bob Evans <[email protected]>
- Loading branch information
1 parent
47a925e
commit 3c50606
Showing
16 changed files
with
551 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* Copyright 2024 New Relic Corporation. All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
'use strict' | ||
const { | ||
AI: { LANGCHAIN } | ||
} = require('../../metrics/names') | ||
const { LangChainVectorSearch, LangChainVectorSearchResult } = require('../../llm-events/langchain') | ||
const { recordEvent, shouldSkipInstrumentation } = require('./common') | ||
const { DESTINATIONS } = require('../../config/attribute-filter') | ||
const { RecorderSpec } = require('../../shim/specs') | ||
const LlmErrorMessage = require('../../llm-events/error-message') | ||
|
||
/** | ||
* Generates a LangChainVectorSearch for entire search request. | ||
* Also iterates over documents in output and generates a | ||
* LangChainVectorSearchResult for each document. | ||
* | ||
* @param {object} params input params | ||
* @param {string} params.request vector search query | ||
* @param {number} params.k vector search top k | ||
* @param {object} params.output vector search documents | ||
* @param {Agent} params.agent NR agent instance | ||
* @param {TraceSegment} params.segment active segment from vector search | ||
* @param {string} params.pkgVersion langchain version | ||
* @param {err} params.err if it exists | ||
*/ | ||
function recordVectorSearch({ request, k, output, agent, segment, pkgVersion, err }) { | ||
const vectorSearch = new LangChainVectorSearch({ | ||
agent, | ||
segment, | ||
query: request, | ||
k, | ||
documents: output, | ||
error: err !== null | ||
}) | ||
|
||
recordEvent({ agent, type: 'LlmVectorSearch', pkgVersion, msg: vectorSearch }) | ||
|
||
output.forEach((document, sequence) => { | ||
const vectorSearchResult = new LangChainVectorSearchResult({ | ||
agent, | ||
segment, | ||
metadata: document.metadata, | ||
pageContent: document.pageContent, | ||
sequence, | ||
search_id: vectorSearch.id | ||
}) | ||
|
||
recordEvent({ | ||
agent, | ||
type: 'LlmVectorSearchResult', | ||
pkgVersion, | ||
msg: vectorSearchResult | ||
}) | ||
}) | ||
|
||
if (err) { | ||
agent.errors.add( | ||
segment.transaction, | ||
err, | ||
new LlmErrorMessage({ | ||
response: output, | ||
cause: err, | ||
vectorsearch: vectorSearch | ||
}) | ||
) | ||
} | ||
} | ||
|
||
module.exports = function initialize(shim, vectorstores) { | ||
const { agent, pkgVersion } = shim | ||
|
||
if (shouldSkipInstrumentation(agent.config)) { | ||
shim.logger.debug( | ||
'langchain instrumentation is disabled. To enable set `config.ai_monitoring.enabled` to true' | ||
) | ||
return | ||
} | ||
|
||
shim.record( | ||
vectorstores.VectorStore.prototype, | ||
'similaritySearch', | ||
function wrapCall(shim, similaritySearch, fnName, args) { | ||
const [request, k] = args | ||
|
||
return new RecorderSpec({ | ||
name: `${LANGCHAIN.VECTORSTORE}/${fnName}`, | ||
promise: true, | ||
// eslint-disable-next-line max-params | ||
after(_shim, _fn, _name, err, output, segment) { | ||
if (!output) { | ||
// If we get an error, it is possible that `output = null`. | ||
// In that case, we define it to be an empty array. | ||
output = [] | ||
} | ||
|
||
segment.end() | ||
recordVectorSearch({ request, k, output, agent, segment, pkgVersion, err }) | ||
|
||
segment.transaction.trace.attributes.addAttribute(DESTINATIONS.TRANS_EVENT, 'llm', true) | ||
} | ||
}) | ||
} | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright 2023 New Relic Corporation. All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
'use strict' | ||
|
||
const { test } = require('tap') | ||
const helper = require('../../../lib/agent_helper') | ||
const GenericShim = require('../../../../lib/shim/shim') | ||
const sinon = require('sinon') | ||
|
||
test('langchain/core/vectorstore unit tests', (t) => { | ||
t.beforeEach(function (t) { | ||
const sandbox = sinon.createSandbox() | ||
const agent = helper.loadMockedAgent() | ||
agent.config.ai_monitoring = { enabled: true } | ||
agent.config.feature_flag = { langchain_instrumentation: true } | ||
const shim = new GenericShim(agent, 'langchain') | ||
shim.pkgVersion = '0.1.26' | ||
sandbox.stub(shim.logger, 'debug') | ||
sandbox.stub(shim.logger, 'warn') | ||
|
||
t.context.agent = agent | ||
t.context.shim = shim | ||
t.context.sandbox = sandbox | ||
t.context.initialize = require('../../../../lib/instrumentation/langchain/vectorstore') | ||
}) | ||
|
||
t.afterEach(function (t) { | ||
helper.unloadAgent(t.context.agent) | ||
t.context.sandbox.restore() | ||
}) | ||
|
||
function getMockModule() { | ||
function VectorStore() {} | ||
VectorStore.prototype.similaritySearch = async function call() {} | ||
return { VectorStore } | ||
} | ||
|
||
;[ | ||
{ aiMonitoring: false, langChain: true }, | ||
{ aiMonitoring: true, langChain: false }, | ||
{ aiMonitoring: false, langChain: false } | ||
].forEach(({ aiMonitoring, langChain }) => { | ||
t.test( | ||
`should not register instrumentation if ai_monitoring is ${aiMonitoring} and langchain_instrumentation is ${langChain}`, | ||
(t) => { | ||
const { shim, agent, initialize } = t.context | ||
const MockVectorstore = getMockModule() | ||
agent.config.ai_monitoring.enabled = aiMonitoring | ||
agent.config.feature_flag.langchain_instrumentation = langChain | ||
|
||
initialize(shim, MockVectorstore) | ||
t.equal(shim.logger.debug.callCount, 1, 'should log 1 debug messages') | ||
t.equal( | ||
shim.logger.debug.args[0][0], | ||
'langchain instrumentation is disabled. To enable set `config.ai_monitoring.enabled` to true' | ||
) | ||
const isWrapped = shim.isWrapped(MockVectorstore.VectorStore.prototype.similaritySearch) | ||
t.equal(isWrapped, false, 'should not wrap vectorstore similaritySearch') | ||
t.end() | ||
} | ||
) | ||
}) | ||
|
||
t.end() | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.