Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ChromaDB Instrumentation #192

Merged
merged 2 commits into from
Nov 22, 2024
Merged
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
13 changes: 7 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@langtrase/typescript-sdk",
"version": "6.2.0",
"version": "6.3.0",
"description": "A typescript SDK for Langtrace",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -48,7 +48,7 @@
"@qdrant/js-client-rest": "^1.9.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"ai": "^3.2.29",
"chromadb": "^1.8.1",
"chromadb": "^1.9.4",
"cohere-ai": "^7.9.3",
"dotenv": "^16.4.5",
"eslint": "^8.34.0",
Expand Down
182 changes: 162 additions & 20 deletions src/instrumentation/chroma/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import { version, name } from '../../../package.json'
import { APIS } from '@langtrase/trace-attributes'

class ChromaInstrumentation extends InstrumentationBase<any> {
private originalGetOrCreateCollection: any
private originalCreateCollection: any
private originalGetCollection: any

constructor () {
super(name, version)
}
Expand All @@ -32,49 +36,187 @@ class ChromaInstrumentation extends InstrumentationBase<any> {
}

init (): Array<InstrumentationModuleDefinition<any>> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this
const module = new InstrumentationNodeModuleDefinition<any>(
'chromadb',
['>=1.8.1'],
(moduleExports, moduleVersion) => {
diag.debug(`Patching ChromaDB SDK version ${moduleVersion}`)
this._patch(moduleExports, moduleVersion as string)

const ChromaClient = moduleExports.ChromaClient

// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!ChromaClient?.prototype) {
diag.error('ChromaClient not found in exports')
return moduleExports
}

// Store original methods
this.originalGetOrCreateCollection = ChromaClient.prototype.getOrCreateCollection
this.originalCreateCollection = ChromaClient.prototype.createCollection
this.originalGetCollection = ChromaClient.prototype.getCollection

// Patch getOrCreateCollection
ChromaClient.prototype.getOrCreateCollection = async function (...args: any[]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const collection = await self.originalGetOrCreateCollection.apply(this, args)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (collection) {
self._patchCollectionMethods(collection, moduleVersion)
}
return collection
}

// Patch createCollection
ChromaClient.prototype.createCollection = async function (...args: any[]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const collection = await self.originalCreateCollection.apply(this, args)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (collection) {
self._patchCollectionMethods(collection, moduleVersion)
}
return collection
}

// Patch getCollection
ChromaClient.prototype.getCollection = async function (...args: any[]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const collection = await self.originalGetCollection.apply(this, args)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (collection) {
self._patchCollectionMethods(collection, moduleVersion)
}
return collection
}

return moduleExports
},
(moduleExports, moduleVersion) => {
diag.debug(`Unpatching ChromaDB SDK version ${moduleVersion}`)
if (moduleExports !== undefined) {
this._unpatch(moduleExports)
const ChromaClient = moduleExports.ChromaClient
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ChromaClient?.prototype) {
ChromaClient.prototype.getOrCreateCollection = this.originalGetOrCreateCollection
ChromaClient.prototype.createCollection = this.originalCreateCollection
ChromaClient.prototype.getCollection = this.originalGetCollection
}
}
)

return [module]
}

private _patch (chromadb: any, moduleVersion?: string): void {
if (isWrapped(chromadb.Collection.prototype)) {
Object.keys(APIS.chromadb).forEach((api) => {
this._unwrap(chromadb.Collection.prototype, APIS.chromadb[api as keyof typeof APIS.chromadb].OPERATION as string)
})
}
private _patchCollectionMethods (collection: any, moduleVersion?: string): void {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!collection) return

// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this
Object.keys(APIS.chromadb).forEach((api) => {
if (isWrapped(chromadb.Collection.prototype)) {
this._unpatch(chromadb)
const operation = APIS.chromadb[api as keyof typeof APIS.chromadb].OPERATION as string
const original = collection[operation]

// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (original && !isWrapped(original)) {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const wrapped = (...args: any[]) => {
const patchedMethod = collectionPatch(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
function (...methodArgs: any[]) {
return original.apply(collection, methodArgs)
},
api,
self.tracer,
self.instrumentationVersion,
moduleVersion
)
return patchedMethod.apply(this, args)
}

Object.assign(wrapped, original)
wrapped.__original = original
wrapped.__wrapped = true

collection[operation] = wrapped
}
this._wrap(
chromadb.Collection.prototype,
APIS.chromadb[api as keyof typeof APIS.chromadb].OPERATION,
(originalMethod: (...args: any[]) => any) =>
collectionPatch(originalMethod, api, this.tracer, this.instrumentationVersion, moduleVersion)
)
})
}

private _patch (chromadb: any, moduleVersion?: string): void {
const ChromaClient = chromadb.ChromaClient
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ChromaClient?.prototype) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this

// Store original methods if not already stored
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!this.originalGetOrCreateCollection) {
this.originalGetOrCreateCollection = ChromaClient.prototype.getOrCreateCollection
}
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!this.originalCreateCollection) {
this.originalCreateCollection = ChromaClient.prototype.createCollection
}
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!this.originalGetCollection) {
this.originalGetCollection = ChromaClient.prototype.getCollection
}

// Patch getOrCreateCollection
ChromaClient.prototype.getOrCreateCollection = async function (...args: any[]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const collection = await self.originalGetOrCreateCollection.apply(this, args)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (collection) {
self._patchCollectionMethods(collection, moduleVersion)
}
return collection
}

// Patch createCollection
ChromaClient.prototype.createCollection = async function (...args: any[]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const collection = await self.originalCreateCollection.apply(this, args)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (collection) {
self._patchCollectionMethods(collection, moduleVersion)
}
return collection
}

// Patch getCollection
ChromaClient.prototype.getCollection = async function (...args: any[]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const collection = await self.originalGetCollection.apply(this, args)
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (collection) {
self._patchCollectionMethods(collection, moduleVersion)
}
return collection
}
}
}

private _unpatch (chromadb: any): void {
Object.keys(APIS.chromadb).forEach((api) => {
this._unwrap(chromadb.Collection.prototype, APIS.chromadb[api as keyof typeof APIS.chromadb].OPERATION as string)
})
const ChromaClient = chromadb.ChromaClient
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ChromaClient?.prototype) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (this.originalGetOrCreateCollection) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
ChromaClient.prototype.getOrCreateCollection = this.originalGetOrCreateCollection
}
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (this.originalCreateCollection) {
ChromaClient.prototype.createCollection = this.originalCreateCollection
}
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (this.originalGetCollection) {
ChromaClient.prototype.getCollection = this.originalGetCollection
}
}
}
}

Expand Down
Loading