-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
155 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { OpenAI } from "@langchain/openai"; | ||
import { VercelKVCache } from "@langchain/community/caches/vercel_kv"; | ||
import { createClient } from "@vercel/kv"; | ||
|
||
// See https://vercel.com/docs/storage/vercel-kv/kv-reference#createclient-example for connection options | ||
const cache = new VercelKVCache({ | ||
config: createClient({ | ||
url: "VERCEL_KV_API_URL", | ||
token: "VERCEL_KV_API_TOKEN", | ||
}), | ||
ttl: 3600, | ||
}); | ||
|
||
const model = new OpenAI({ cache }); |
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
34 changes: 34 additions & 0 deletions
34
libs/langchain-community/src/caches/tests/vercel_kv.int.test.ts
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,34 @@ | ||
/* eslint-disable no-process-env */ | ||
import { ChatOpenAI } from "@langchain/openai"; | ||
import { createClient } from "@vercel/kv"; | ||
import { VercelKVCache } from "../vercel_kv.js"; | ||
|
||
test.skip("VercelKVCache works with ChatOpenAI", async () => { | ||
if ( | ||
!process.env.VERCEL_KV_API_URL || | ||
!process.env.VERCEL_KV_API_TOKEN || | ||
!process.env.OPENAI_API_KEY | ||
) { | ||
throw new Error("Missing Vercel KV API URL, token, or OpenAI API key"); | ||
} | ||
|
||
const vercelKVCache = new VercelKVCache({ | ||
client: createClient({ | ||
url: process.env.VERCEL_KV_API_URL, | ||
token: process.env.VERCEL_KV_API_TOKEN, | ||
}), | ||
ttl: 60, | ||
}); | ||
|
||
const chat = new ChatOpenAI({ | ||
temperature: 0, | ||
cache: vercelKVCache, | ||
maxTokens: 10, | ||
}); | ||
|
||
const prompt = "What color is the sky?"; | ||
const result1 = await chat.invoke(prompt); | ||
const result2 = await chat.invoke(prompt); | ||
|
||
expect(result1).toEqual(result2); | ||
}); |
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,87 @@ | ||
import { kv, type VercelKV } from "@vercel/kv"; | ||
|
||
import { Generation } from "@langchain/core/outputs"; | ||
import { | ||
BaseCache, | ||
deserializeStoredGeneration, | ||
getCacheKey, | ||
serializeGeneration, | ||
} from "@langchain/core/caches"; | ||
import { StoredGeneration } from "@langchain/core/messages"; | ||
|
||
export type VercelKVCacheProps = { | ||
/** | ||
* An existing Vercel KV client | ||
*/ | ||
client?: VercelKV; | ||
/** | ||
* Time-to-live (TTL) for cached items in seconds | ||
*/ | ||
ttl?: number; | ||
}; | ||
|
||
/** | ||
* A cache that uses Vercel KV as the backing store. | ||
* @example | ||
* ```typescript | ||
* const cache = new VercelKVCache({ | ||
* ttl: 3600, // Optional: Cache entries will expire after 1 hour | ||
* }); | ||
* | ||
* // Initialize the OpenAI model with Vercel KV cache for caching responses | ||
* const model = new ChatOpenAI({ | ||
* cache, | ||
* }); | ||
* await model.invoke("How are you today?"); | ||
* const cachedValues = await cache.lookup("How are you today?", "llmKey"); | ||
* ``` | ||
*/ | ||
export class VercelKVCache extends BaseCache { | ||
private client: VercelKV; | ||
|
||
private ttl?: number; | ||
|
||
constructor(props: VercelKVCacheProps) { | ||
super(); | ||
const { client, ttl } = props; | ||
this.client = client ?? kv; | ||
this.ttl = ttl; | ||
} | ||
|
||
/** | ||
* Lookup LLM generations in cache by prompt and associated LLM key. | ||
*/ | ||
public async lookup(prompt: string, llmKey: string) { | ||
let idx = 0; | ||
let key = getCacheKey(prompt, llmKey, String(idx)); | ||
let value = await this.client.get<StoredGeneration | null>(key); | ||
const generations: Generation[] = []; | ||
|
||
while (value) { | ||
generations.push(deserializeStoredGeneration(value)); | ||
idx += 1; | ||
key = getCacheKey(prompt, llmKey, String(idx)); | ||
value = await this.client.get<StoredGeneration | null>(key); | ||
} | ||
|
||
return generations.length > 0 ? generations : null; | ||
} | ||
|
||
/** | ||
* Update the cache with the given generations. | ||
* | ||
* Note this overwrites any existing generations for the given prompt and LLM key. | ||
*/ | ||
public async update(prompt: string, llmKey: string, value: Generation[]) { | ||
for (let i = 0; i < value.length; i += 1) { | ||
const key = getCacheKey(prompt, llmKey, String(i)); | ||
const serializedValue = JSON.stringify(serializeGeneration(value[i])); | ||
|
||
if (this.ttl) { | ||
await this.client.set(key, serializedValue, { ex: this.ttl }); | ||
} else { | ||
await this.client.set(key, serializedValue); | ||
} | ||
} | ||
} | ||
} |