From fa32aed7c2c78b12670e608b8b08a1ea0efea1a1 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Fri, 15 Sep 2023 21:39:15 -0400 Subject: [PATCH 01/18] Initial, incomplete, implementation, including toTemplate() --- langchain/src/hub/googlemakersuitehub.ts | 79 +++++++++++++++++++ .../src/hub/tests/googlemakersuitehub.test.ts | 16 ++++ 2 files changed, 95 insertions(+) create mode 100644 langchain/src/hub/googlemakersuitehub.ts create mode 100644 langchain/src/hub/tests/googlemakersuitehub.test.ts diff --git a/langchain/src/hub/googlemakersuitehub.ts b/langchain/src/hub/googlemakersuitehub.ts new file mode 100644 index 000000000000..8555e874f4a0 --- /dev/null +++ b/langchain/src/hub/googlemakersuitehub.ts @@ -0,0 +1,79 @@ +import { GooglePaLMTextInput } from "../llms/googlepalm.js"; +import { GooglePaLMChatInput } from "../chat_models/googlepalm.js"; +import { PromptTemplate } from "../prompts/index.js"; + +export interface MakerSuiteHubConfig { + cacheTimeout: number, +} + +export interface MakerSuitePromptVariable { + variableId: string, + displayName: string, +} + +export interface MakerSuitePromptData { + textPrompt: { + value?: string, + variables?: MakerSuitePromptVariable[], + }, + runSettings?: GooglePaLMTextInput | GooglePaLMChatInput, + // There may be other values we're not concerned about +} + +export class MakerSuitePrompt { + + promptData: MakerSuitePromptData; + + constructor(promptData: MakerSuitePromptData) { + this.promptData = promptData; + } + + toTemplate(): PromptTemplate { + const value = this.promptData?.textPrompt?.value ?? ""; + const promptString = value.replaceAll(/{{.*:(.+):.*}}/g, "{$1}"); + return PromptTemplate.fromTemplate(promptString); + } + +} + +interface CacheEntry { + updated: number, + prompt: MakerSuitePrompt, +} + +export class MakerSuiteHub { + + cache: Record; + + cacheTimeout: number; + + constructor(config?: MakerSuiteHubConfig){ + this.cacheTimeout = config?.cacheTimeout ?? 0; + } + + isValid(entry: CacheEntry): boolean { + // If we don't have a record, it can't be valid + // And if the cache timeout is 0, we will always refresh, so the cache is invalid + if (!entry || this.cacheTimeout === 0) { + return false; + } + + const now = Date.now(); + const expiration = entry.updated + this.cacheTimeout; + return expiration < now; + } + + async forceLoad(id: string): Promise { + console.log('forceLoad', id); + return new MakerSuitePrompt({ + textPrompt: {} + }); + } + + async load(id: string): Promise { + const entry = this.cache[id]; + const ret = this.isValid(entry) ? entry.prompt : await this.forceLoad(id); + return ret; + } + +} \ No newline at end of file diff --git a/langchain/src/hub/tests/googlemakersuitehub.test.ts b/langchain/src/hub/tests/googlemakersuitehub.test.ts new file mode 100644 index 000000000000..eebeb4181e4c --- /dev/null +++ b/langchain/src/hub/tests/googlemakersuitehub.test.ts @@ -0,0 +1,16 @@ +import { describe, test } from "@jest/globals"; +import {MakerSuitePrompt} from "../googlemakersuitehub.js"; + +describe("Google Maker Suite Hub", () => { + + test("Prompt template", () => { + const prompt = new MakerSuitePrompt({ + textPrompt: { + value: "What would be a good name for a company that makes {{30E275F8-0B60-4E71-843D-9865F4D4EFD4:product:}}?" + } + }); + const template = prompt.toTemplate(); + expect(template.template).toEqual("What would be a good name for a company that makes {product}?") + }); + +}) \ No newline at end of file From e73f50fcf2772a71ce8f83163fdb9052a81db4f9 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Sat, 16 Sep 2023 18:27:29 -0400 Subject: [PATCH 02/18] Initial implementation of prompt types --- langchain/src/hub/googlemakersuitehub.ts | 132 +++++++++++++++++- .../googlemakersuite-files/chatPrompt.json | 35 +++++ .../googlemakersuite-files/dataPrompt.json | 81 +++++++++++ .../googlemakersuite-files/textPrompt.json | 43 ++++++ .../src/hub/tests/googlemakersuitehub.test.ts | 49 +++++-- 5 files changed, 326 insertions(+), 14 deletions(-) create mode 100644 langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json create mode 100644 langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json create mode 100644 langchain/src/hub/tests/googlemakersuite-files/textPrompt.json diff --git a/langchain/src/hub/googlemakersuitehub.ts b/langchain/src/hub/googlemakersuitehub.ts index 8555e874f4a0..1e105c52564a 100644 --- a/langchain/src/hub/googlemakersuitehub.ts +++ b/langchain/src/hub/googlemakersuitehub.ts @@ -1,39 +1,159 @@ -import { GooglePaLMTextInput } from "../llms/googlepalm.js"; -import { GooglePaLMChatInput } from "../chat_models/googlepalm.js"; +import { protos } from "@google-ai/generativelanguage"; + +import { GooglePaLM } from "../llms/googlepalm.js"; +import { ChatGooglePaLM } from "../chat_models/googlepalm.js"; import { PromptTemplate } from "../prompts/index.js"; +import { BaseChatModel } from "../chat_models/base.js"; +import { LLM } from "../llms/base.js"; +import {Runnable} from "../schema/runnable/index.js"; export interface MakerSuiteHubConfig { cacheTimeout: number, } +type MakerSuitePromptType = "text" | "data" | "chat"; + export interface MakerSuitePromptVariable { variableId: string, displayName: string, } -export interface MakerSuitePromptData { +export interface MakerSuiteRunSettings { + temperature?: number, + model: string, + candidateCount?: number, + topP?: number, + topK?: number, + maxOutputTokens: number, + safetySettings?: protos.google.ai.generativelanguage.v1beta2.ISafetySetting[], +} + +export interface MakerSuiteTextPromptData { textPrompt: { value?: string, variables?: MakerSuitePromptVariable[], }, - runSettings?: GooglePaLMTextInput | GooglePaLMChatInput, - // There may be other values we're not concerned about + runSettings?: MakerSuiteRunSettings, + testExamples?: unknown, +} + +export interface MakerSuiteDataPromptColumn { + columnId: string, + displayName: string, + isInput?: boolean, +} + +export interface MakerSuiteDataPromptRow { + rowId: string, + columnBindings: Record, +} + +export interface MakerSuiteDataPromptData { + dataPrompt: { + preamble: string, + columns: MakerSuiteDataPromptColumn[], + rows: MakerSuiteDataPromptRow[], + rowsUsed: string[], + }, + runSettings?: MakerSuiteRunSettings, + testExamples?: unknown, +} + +export interface MakerSuiteChatExchange { + request?: string, + response?: string, + source: string, + id: string, +} + +export interface MakerSuiteChatPromptData { + multiturnPrompt: { + preamble: string, + primingExchanges: MakerSuiteChatExchange[], + sessions: { + sessionExchanges: MakerSuiteChatExchange[] + }[] + }, + runSettings?: MakerSuiteRunSettings, } +export type MakerSuitePromptData = + MakerSuiteTextPromptData | + MakerSuiteDataPromptData | + MakerSuiteChatPromptData; + export class MakerSuitePrompt { + promptType: MakerSuitePromptType; + promptData: MakerSuitePromptData; constructor(promptData: MakerSuitePromptData) { this.promptData = promptData; + this._determinePromptType(); + } + + _determinePromptType() { + if (Object.hasOwn(this.promptData, "textPrompt")) { + this.promptType = "text"; + + } else if (Object.hasOwn(this.promptData, "dataPrompt")) { + this.promptType = "data"; + + } else if (Object.hasOwn(this.promptData, "multiturnPrompt")) { + this.promptType = "chat"; + + } else { + const error = new Error("Unable to identify prompt type."); + (error as any).promptData = this.promptData; + throw error; + } + } + + _promptValueText(): string { + return (this.promptData as MakerSuiteTextPromptData)?.textPrompt?.value ?? ""; + } + + _promptValueData(): string { + return "FIXME"; + } + + _promptValueChat(): string { + return "FIXME"; + } + + _promptValue(): string { + switch (this.promptType) { + case "text": return this._promptValueText(); + case "data": return this._promptValueData(); + case "chat": return this._promptValueChat(); + default: throw new Error(`Invalid promptType: ${this.promptType}`); + } } toTemplate(): PromptTemplate { - const value = this.promptData?.textPrompt?.value ?? ""; + const value = this._promptValue(); const promptString = value.replaceAll(/{{.*:(.+):.*}}/g, "{$1}"); return PromptTemplate.fromTemplate(promptString); } + toModel(): LLM | BaseChatModel { + const modelName = this.promptData?.runSettings?.model || "models/text-bison-001"; + const modelSettings = { + modelName, + ...this.promptData?.runSettings + } + if (this.promptType === "chat") { + return new ChatGooglePaLM(modelSettings); + } else { + return new GooglePaLM(modelSettings); + } + } + + toChain() { + return this.toTemplate().pipe(this.toModel() as Runnable); + } + } interface CacheEntry { diff --git a/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json b/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json new file mode 100644 index 000000000000..471f14130feb --- /dev/null +++ b/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json @@ -0,0 +1,35 @@ +{ + "runSettings": { + "temperature": 0.25, + "model": "models/chat-bison-001", + "candidateCount": 1, + "topP": 0.95, + "topK": 40, + "maxOutputTokens": 1024 + }, + "multiturnPrompt": { + "preamble": "You are responsible for returning date and time information based on the user\u0027s input. All your date and time responses should be as an ISO8601 formatted response that include the year, month, day, hour, minute, second, and time zone offset. You should only return this time information and nothing else.\n", + "primingExchanges": [{ + "request": "What time is it?", + "response": "2023-09-16T02:03:04-0500", + "source": "MODEL_EDITED", + "id": "4B26EE6A-6171-4626-80B6-207491D106BF" + }, { + "source": "USER", + "id": "36D72C06-423E-4E00-BA27-DEC44CBA5A13" + }], + "sessions": [{ + "sessionExchanges": [{ + "request": "What time is it?", + "response": "The time is 2023-09-16T02:04:05-0500.", + "source": "MODEL", + "id": "9E4D6C9A-81D2-4EE9-BE8B-96C84F1FDA64" + }, { + "request": "What time was it one week ago?", + "response": "The time one week ago was 2023-09-09T02:04:05-0500.", + "source": "MODEL", + "id": "EA76CED1-F626-46F3-8CCA-4C9C821B2003" + }] + }] + } +} \ No newline at end of file diff --git a/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json b/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json new file mode 100644 index 000000000000..79a5483e0673 --- /dev/null +++ b/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json @@ -0,0 +1,81 @@ +{ + "dataPrompt": { + "preamble": "Given a product description, you should return a name for that product that includes something about rainbows.", + "columns": [{ + "columnId": "CFA438B7-313B-4A50-AF74-3ACBC9216A4B", + "displayName": "description:", + "isInput": true + }, { + "columnId": "7D38B937-6636-4AAC-9DFD-B4A60D98C011", + "displayName": "product:" + }], + "rows": [{ + "columnBindings": { + "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "socks", + "7D38B937-6636-4AAC-9DFD-B4A60D98C011": "spectrum socks" + }, + "rowId": "D412F0BE-5C81-401D-917C-758A6F14EEC4" + }, { + "columnBindings": { + "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "hair ties", + "7D38B937-6636-4AAC-9DFD-B4A60D98C011": "rainbows^2" + }, + "rowId": "C77A45B4-74B4-41F3-AD8E-06F0C1D9D820" + }], + "rowsUsed": ["D412F0BE-5C81-401D-917C-758A6F14EEC4", "C77A45B4-74B4-41F3-AD8E-06F0C1D9D820"] + }, + "runSettings": { + "temperature": 0.7, + "model": "models/text-bison-001", + "candidateCount": 1, + "topP": 0.95, + "topK": 40, + "maxOutputTokens": 1024, + "safetySettings": [{ + "category": "HARM_CATEGORY_DEROGATORY", + "threshold": "BLOCK_LOW_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_TOXICITY", + "threshold": "BLOCK_LOW_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_VIOLENCE", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_SEXUAL", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_MEDICAL", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_DANGEROUS", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }] + }, + "testExamples": [{ + "inputBindings": { + "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "condoms" + }, + "modelResponses": [{ + "response": "rainbow rhapsody", + "safetyRatings": [{ + "category": "HARM_CATEGORY_DEROGATORY", + "probability": "NEGLIGIBLE" + }, { + "category": "HARM_CATEGORY_TOXICITY", + "probability": "NEGLIGIBLE" + }, { + "category": "HARM_CATEGORY_VIOLENCE", + "probability": "NEGLIGIBLE" + }, { + "category": "HARM_CATEGORY_SEXUAL", + "probability": "LOW" + }, { + "category": "HARM_CATEGORY_MEDICAL", + "probability": "NEGLIGIBLE" + }, { + "category": "HARM_CATEGORY_DANGEROUS", + "probability": "NEGLIGIBLE" + }] + }] + }] +} \ No newline at end of file diff --git a/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json b/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json new file mode 100644 index 000000000000..494827af504c --- /dev/null +++ b/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json @@ -0,0 +1,43 @@ +{ + "textPrompt": { + "value": "What would be a good name for a company that makes {{30E275F8-0B60-4E71-843D-9865F4D4EFD4:product:​}}?", + "variables": [{ + "variableId": "30E275F8-0B60-4E71-843D-9865F4D4EFD4", + "displayName": "product" + }] + }, + "runSettings": { + "temperature": 0.7, + "model": "models/text-bison-001", + "candidateCount": 1, + "topP": 0.95, + "topK": 40, + "maxOutputTokens": 1024, + "safetySettings": [{ + "category": "HARM_CATEGORY_DEROGATORY", + "threshold": "BLOCK_LOW_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_TOXICITY", + "threshold": "BLOCK_LOW_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_VIOLENCE", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_SEXUAL", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_MEDICAL", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, { + "category": "HARM_CATEGORY_DANGEROUS", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }] + }, + "testExamples": [{ + "inputBindings": { + "30E275F8-0B60-4E71-843D-9865F4D4EFD4": "" + }, + "modelResponses": [{ + }] + }] +} \ No newline at end of file diff --git a/langchain/src/hub/tests/googlemakersuitehub.test.ts b/langchain/src/hub/tests/googlemakersuitehub.test.ts index eebeb4181e4c..81bbf38b558f 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.test.ts +++ b/langchain/src/hub/tests/googlemakersuitehub.test.ts @@ -1,16 +1,49 @@ +import fs from "fs"; +import { fileURLToPath } from "node:url"; +import * as path from "path"; + import { describe, test } from "@jest/globals"; -import {MakerSuitePrompt} from "../googlemakersuitehub.js"; +import { MakerSuitePrompt } from "../googlemakersuitehub.js"; describe("Google Maker Suite Hub", () => { - test("Prompt template", () => { - const prompt = new MakerSuitePrompt({ - textPrompt: { - value: "What would be a good name for a company that makes {{30E275F8-0B60-4E71-843D-9865F4D4EFD4:product:}}?" - } + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + + const chatFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/chatPrompt.json`, "utf8")); + const dataFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/dataPrompt.json`, "utf8")); + const textFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/textPrompt.json`, "utf8")); + + describe("Prompt", () => { + + test("text type", () => { + const prompt = new MakerSuitePrompt(textFile); + expect(prompt.promptType).toEqual("text"); + }); + + test("text template", () => { + const prompt = new MakerSuitePrompt(textFile); + const template = prompt.toTemplate(); + expect(template.template).toEqual("What would be a good name for a company that makes {product}?") }); - const template = prompt.toTemplate(); - expect(template.template).toEqual("What would be a good name for a company that makes {product}?") + + test("text model", () => { + const prompt = new MakerSuitePrompt(textFile); + const model = prompt.toModel(); + // console.log(model.lc_namespace); + expect(model.lc_namespace).toEqual(["langchain", "llms", "googlepalm"]); + }) + + test("data type", () => { + const prompt = new MakerSuitePrompt(dataFile); + expect(prompt.promptType).toEqual("data") + }); + + test("chat type", () => { + const prompt = new MakerSuitePrompt(chatFile); + expect(prompt.promptType).toEqual("chat"); + }) + }); }) \ No newline at end of file From 159e76dc8d0fac3305eee21c67d1b87459af5ddc Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Sat, 16 Sep 2023 21:58:35 -0400 Subject: [PATCH 03/18] Test text chain --- .../hub/tests/googlemakersuitehub.int.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 langchain/src/hub/tests/googlemakersuitehub.int.test.ts diff --git a/langchain/src/hub/tests/googlemakersuitehub.int.test.ts b/langchain/src/hub/tests/googlemakersuitehub.int.test.ts new file mode 100644 index 000000000000..4b561c4ac9fc --- /dev/null +++ b/langchain/src/hub/tests/googlemakersuitehub.int.test.ts @@ -0,0 +1,31 @@ +import fs from "fs"; +import { fileURLToPath } from "node:url"; +import * as path from "path"; + +import { describe, test } from "@jest/globals"; +import { MakerSuitePrompt } from "../googlemakersuitehub.js"; + +describe("Google Maker Suite Hub Integration", () => { + + describe("Prompt", () => { + + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + + // const chatFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/chatPrompt.json`, "utf8")); + // const dataFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/dataPrompt.json`, "utf8")); + const textFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/textPrompt.json`, "utf8")); + + test("text chain", async () => { + const prompt = new MakerSuitePrompt(textFile); + const chain = prompt.toChain(); + const result = await chain.invoke({ + product: "shoes" + }); + console.log("text chain result", result); + expect(result).toBeTruthy(); + }) + + }) + +}) \ No newline at end of file From 3fb9981267e578aeb3b68adb2987cb21e32b28df Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Sat, 16 Sep 2023 21:59:06 -0400 Subject: [PATCH 04/18] Create the data template. Test data template and model. --- langchain/src/hub/googlemakersuitehub.ts | 38 ++++++++++++++++++- .../src/hub/tests/googlemakersuitehub.test.ts | 22 +++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/langchain/src/hub/googlemakersuitehub.ts b/langchain/src/hub/googlemakersuitehub.ts index 1e105c52564a..56d8a56a5405 100644 --- a/langchain/src/hub/googlemakersuitehub.ts +++ b/langchain/src/hub/googlemakersuitehub.ts @@ -115,7 +115,43 @@ export class MakerSuitePrompt { } _promptValueData(): string { - return "FIXME"; + const promptData: MakerSuiteDataPromptData = this.promptData as MakerSuiteDataPromptData; + const dataPrompt = promptData?.dataPrompt; + let prompt = `${dataPrompt?.preamble}\n` || ""; + + dataPrompt?.rows.forEach( row => { + // Add the data for each row, as long as it is listed as used + if (dataPrompt?.rowsUsed.includes(row.rowId)) { + + // Add each input column + dataPrompt?.columns.forEach( column => { + if (column.isInput) { + prompt += `${column.displayName} ${row.columnBindings[column.columnId]}\n`; + } + }); + + // Add each output column + dataPrompt?.columns.forEach( column => { + if (!column.isInput) { + prompt += `${column.displayName} ${row.columnBindings[column.columnId]}\n`; + } + }); + + } + }); + + // Add the input column prompts + dataPrompt?.columns.forEach( column => { + if (column.isInput) { + prompt += `${column.displayName} {${column.displayName.replace(":", "")}}\n` + } + }) + + // Add just the first output column + const firstOutput = dataPrompt?.columns.find( column => !column.isInput ); + prompt += `${firstOutput?.displayName} `; + + return prompt; } _promptValueChat(): string { diff --git a/langchain/src/hub/tests/googlemakersuitehub.test.ts b/langchain/src/hub/tests/googlemakersuitehub.test.ts index 81bbf38b558f..18f9b859cb5b 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.test.ts +++ b/langchain/src/hub/tests/googlemakersuitehub.test.ts @@ -39,6 +39,28 @@ describe("Google Maker Suite Hub", () => { expect(prompt.promptType).toEqual("data") }); + test("data template", () => { + const prompt = new MakerSuitePrompt(dataFile); + const template = prompt.toTemplate(); + // console.log("data template", template.template); + expect(template.template).toEqual( + "Given a product description, you should return a name for that product that includes something about rainbows.\n" + + "description: socks\n" + + "product: spectrum socks\n" + + "description: hair ties\n" + + "product: rainbows^2\n" + + "description: {description}\n" + + "product: " + ); + }) + + test("data model", () => { + const prompt = new MakerSuitePrompt(dataFile); + const model = prompt.toModel(); + // console.log(model.lc_namespace); + expect(model.lc_namespace).toEqual(["langchain", "llms", "googlepalm"]); + }) + test("chat type", () => { const prompt = new MakerSuitePrompt(chatFile); expect(prompt.promptType).toEqual("chat"); From 59b344c63a6aefbba6ad602726c7b3bfabfda271 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Sun, 17 Sep 2023 12:45:17 -0400 Subject: [PATCH 05/18] Chat prompt and model. Test same. --- langchain/src/hub/googlemakersuitehub.ts | 43 +++++++++++++++++-- .../src/hub/tests/googlemakersuitehub.test.ts | 14 +++++- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/langchain/src/hub/googlemakersuitehub.ts b/langchain/src/hub/googlemakersuitehub.ts index 56d8a56a5405..3daa0fb77781 100644 --- a/langchain/src/hub/googlemakersuitehub.ts +++ b/langchain/src/hub/googlemakersuitehub.ts @@ -1,11 +1,13 @@ import { protos } from "@google-ai/generativelanguage"; +import { google } from "@google-ai/generativelanguage/build/protos/protos.js"; import { GooglePaLM } from "../llms/googlepalm.js"; import { ChatGooglePaLM } from "../chat_models/googlepalm.js"; import { PromptTemplate } from "../prompts/index.js"; import { BaseChatModel } from "../chat_models/base.js"; import { LLM } from "../llms/base.js"; -import {Runnable} from "../schema/runnable/index.js"; +import { Runnable } from "../schema/runnable/index.js"; +import IExample = google.ai.generativelanguage.v1beta2.IExample; export interface MakerSuiteHubConfig { cacheTimeout: number, @@ -155,7 +157,7 @@ export class MakerSuitePrompt { } _promptValueChat(): string { - return "FIXME"; + return (this.promptData as MakerSuiteChatPromptData)?.multiturnPrompt?.preamble ?? ""; } _promptValue(): string { @@ -173,14 +175,47 @@ export class MakerSuitePrompt { return PromptTemplate.fromTemplate(promptString); } + _modelName(): string { + let ret = this.promptData?.runSettings?.model; + if (!ret) { + ret = this.promptType === "chat" ? + "models/chat-bison-001" : + "models/text-bison-001"; + } + return ret; + } + + _examples(): IExample[] { + const promptData: MakerSuiteChatPromptData = this.promptData as MakerSuiteChatPromptData; + const ret: IExample[] = promptData?.multiturnPrompt?.primingExchanges.map( exchange => { + const example:IExample = {}; + if (exchange?.request) { + example.input = { + content: exchange.request + } + } + if (exchange?.response) { + example.output = { + content: exchange.response + } + } + return example; + }).filter( value => Object.keys(value).length ); + return ret; + } + toModel(): LLM | BaseChatModel { - const modelName = this.promptData?.runSettings?.model || "models/text-bison-001"; + const modelName = this._modelName(); const modelSettings = { modelName, ...this.promptData?.runSettings } if (this.promptType === "chat") { - return new ChatGooglePaLM(modelSettings); + const examples = this._examples(); + return new ChatGooglePaLM({ + examples, + ...modelSettings + }); } else { return new GooglePaLM(modelSettings); } diff --git a/langchain/src/hub/tests/googlemakersuitehub.test.ts b/langchain/src/hub/tests/googlemakersuitehub.test.ts index 18f9b859cb5b..d49689f9f0d3 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.test.ts +++ b/langchain/src/hub/tests/googlemakersuitehub.test.ts @@ -4,6 +4,7 @@ import * as path from "path"; import { describe, test } from "@jest/globals"; import { MakerSuitePrompt } from "../googlemakersuitehub.js"; +import {ChatGooglePaLM} from "../../chat_models/googlepalm.js"; describe("Google Maker Suite Hub", () => { @@ -57,7 +58,6 @@ describe("Google Maker Suite Hub", () => { test("data model", () => { const prompt = new MakerSuitePrompt(dataFile); const model = prompt.toModel(); - // console.log(model.lc_namespace); expect(model.lc_namespace).toEqual(["langchain", "llms", "googlepalm"]); }) @@ -66,6 +66,18 @@ describe("Google Maker Suite Hub", () => { expect(prompt.promptType).toEqual("chat"); }) + test("chat model", () => { + const prompt = new MakerSuitePrompt(chatFile); + const model = prompt.toModel(); + expect(model.lc_namespace).toEqual(["langchain", "chat_models", "googlepalm"]); + expect((model as ChatGooglePaLM).examples).toEqual([ + { + input: { content: 'What time is it?' }, + output: { content: '2023-09-16T02:03:04-0500' } + } + ]); + }) + }); }) \ No newline at end of file From 5bf0da243cbd1ee20aeab27b4a6a36875b1bea38 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 27 Sep 2023 07:55:51 -0400 Subject: [PATCH 06/18] Refactor the VertexAI connection into a more generic connection to allow for connections to other Google APIs --- langchain/src/types/googlevertexai-types.ts | 14 ++-- .../src/util/googlevertexai-connection.ts | 69 ++++++++++++------- langchain/src/util/googlevertexai-webauth.ts | 4 +- 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/langchain/src/types/googlevertexai-types.ts b/langchain/src/types/googlevertexai-types.ts index f6b957055939..3765a10f65c2 100644 --- a/langchain/src/types/googlevertexai-types.ts +++ b/langchain/src/types/googlevertexai-types.ts @@ -1,11 +1,13 @@ import { BaseLLMParams } from "../llms/base.js"; -export interface GoogleVertexAIConnectionParams { +export interface GoogleConnectionParams { + authOptions?: AuthOptions; +} + +export interface GoogleVertexAIConnectionParams extends GoogleConnectionParams { /** Hostname for the API call */ endpoint?: string; - authOptions?: AuthOptions; - /** Region where the LLM is stored */ location?: string; @@ -53,12 +55,12 @@ export interface GoogleVertexAIBaseLLMInput GoogleVertexAIConnectionParams, GoogleVertexAIModelParams {} -export interface GoogleVertexAIResponse { +export interface GoogleResponse { // eslint-disable-next-line @typescript-eslint/no-explicit-any data: any; } -export interface GoogleVertexAIBasePrediction extends GoogleVertexAIResponse { +export interface GoogleVertexAIBasePrediction extends GoogleResponse { // eslint-disable-next-line @typescript-eslint/no-explicit-any safetyAttributes?: any; } @@ -71,7 +73,7 @@ export interface GoogleVertexAILLMResponse< }; } -export interface GoogleVertexAIAbstractedClient { +export interface GoogleAbstractedClient { request: (opts: { url?: string; method?: "GET" | "POST"; diff --git a/langchain/src/util/googlevertexai-connection.ts b/langchain/src/util/googlevertexai-connection.ts index de89847025d3..6550fc543c3f 100644 --- a/langchain/src/util/googlevertexai-connection.ts +++ b/langchain/src/util/googlevertexai-connection.ts @@ -6,44 +6,30 @@ import type { GoogleVertexAIConnectionParams, GoogleVertexAILLMResponse, GoogleVertexAIModelParams, - GoogleVertexAIResponse, - GoogleVertexAIAbstractedClient, + GoogleResponse, + GoogleAbstractedClient } from "../types/googlevertexai-types.js"; -export abstract class GoogleVertexAIConnection< +export abstract class GoogleConnection< CallOptions extends AsyncCallerCallOptions, - ResponseType extends GoogleVertexAIResponse, - AuthOptions -> implements GoogleVertexAIConnectionParams + ResponseType extends GoogleResponse +> { caller: AsyncCaller; - endpoint = "us-central1-aiplatform.googleapis.com"; - - location = "us-central1"; - - apiVersion = "v1"; - - client: GoogleVertexAIAbstractedClient; + client: GoogleAbstractedClient; constructor( - fields: GoogleVertexAIConnectionParams | undefined, caller: AsyncCaller, - client: GoogleVertexAIAbstractedClient + client: GoogleAbstractedClient ) { this.caller = caller; - - this.endpoint = fields?.endpoint ?? this.endpoint; - this.location = fields?.location ?? this.location; - this.apiVersion = fields?.apiVersion ?? this.apiVersion; this.client = client; } abstract buildUrl(): Promise; - buildMethod(): string { - return "POST"; - } + abstract buildMethod(): string; async _request( data: unknown | undefined, @@ -73,6 +59,41 @@ export abstract class GoogleVertexAIConnection< throw x; } } + +} + +export abstract class GoogleVertexAIConnection< + CallOptions extends AsyncCallerCallOptions, + ResponseType extends GoogleResponse, + AuthOptions +> + extends GoogleConnection + implements GoogleVertexAIConnectionParams +{ + endpoint = "us-central1-aiplatform.googleapis.com"; + + location = "us-central1"; + + apiVersion = "v1"; + + constructor( + fields: GoogleVertexAIConnectionParams | undefined, + caller: AsyncCaller, + client: GoogleAbstractedClient + ) { + super(caller, client); + this.caller = caller; + + this.endpoint = fields?.endpoint ?? this.endpoint; + this.location = fields?.location ?? this.location; + this.apiVersion = fields?.apiVersion ?? this.apiVersion; + this.client = client; + } + + buildMethod(): string { + return "POST"; + } + } export class GoogleVertexAILLMConnection< @@ -86,12 +107,12 @@ export class GoogleVertexAILLMConnection< { model: string; - client: GoogleVertexAIAbstractedClient; + client: GoogleAbstractedClient; constructor( fields: GoogleVertexAIBaseLLMInput | undefined, caller: AsyncCaller, - client: GoogleVertexAIAbstractedClient + client: GoogleAbstractedClient ) { super(fields, caller, client); this.client = client; diff --git a/langchain/src/util/googlevertexai-webauth.ts b/langchain/src/util/googlevertexai-webauth.ts index 2983a2e44235..4f56830cbd64 100644 --- a/langchain/src/util/googlevertexai-webauth.ts +++ b/langchain/src/util/googlevertexai-webauth.ts @@ -4,14 +4,14 @@ import { Credentials, } from "web-auth-library/google"; import { getEnvironmentVariable } from "./env.js"; -import type { GoogleVertexAIAbstractedClient } from "../types/googlevertexai-types.js"; +import type { GoogleAbstractedClient } from "../types/googlevertexai-types.js"; export type WebGoogleAuthOptions = { credentials: string | Credentials; scope?: string | string[]; }; -export class WebGoogleAuth implements GoogleVertexAIAbstractedClient { +export class WebGoogleAuth implements GoogleAbstractedClient { options: WebGoogleAuthOptions; constructor(options?: WebGoogleAuthOptions) { From 3668a893b0d8490b92306fe9b1fc74dc2b6d66bc Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 27 Sep 2023 07:58:17 -0400 Subject: [PATCH 07/18] Refactor the VertexAI connection into a more generic connection to allow for connections to other Google APIs --- langchain/src/vectorstores/googlevertexai.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/langchain/src/vectorstores/googlevertexai.ts b/langchain/src/vectorstores/googlevertexai.ts index 73f8378227f7..a3630f9dfabe 100644 --- a/langchain/src/vectorstores/googlevertexai.ts +++ b/langchain/src/vectorstores/googlevertexai.ts @@ -12,7 +12,7 @@ import { } from "../util/async_caller.js"; import { GoogleVertexAIConnectionParams, - GoogleVertexAIResponse, + GoogleResponse, } from "../types/googlevertexai-types.js"; import { Docstore } from "../schema/index.js"; @@ -46,7 +46,7 @@ interface DeployedIndex { // There are other attributes, but we don't care about them right now } -interface IndexEndpointResponse extends GoogleVertexAIResponse { +interface IndexEndpointResponse extends GoogleResponse { data: { deployedIndexes: DeployedIndex[]; publicEndpointDomainName: string; @@ -101,7 +101,7 @@ interface RemoveDatapointRequest { datapointIds: string[]; } -interface RemoveDatapointResponse extends GoogleVertexAIResponse { +interface RemoveDatapointResponse extends GoogleResponse { // Should be empty } @@ -165,7 +165,7 @@ interface UpsertDatapointRequest { datapoints: IndexDatapoint[]; } -interface UpsertDatapointResponse extends GoogleVertexAIResponse { +interface UpsertDatapointResponse extends GoogleResponse { // Should be empty } @@ -239,7 +239,7 @@ interface FindNeighborsResponseNearestNeighbor { neighbors: FindNeighborsResponseNeighbor[]; } -interface FindNeighborsResponse extends GoogleVertexAIResponse { +interface FindNeighborsResponse extends GoogleResponse { data: { nearestNeighbors: FindNeighborsResponseNearestNeighbor[]; }; From c5435ab61b1cc6a345716b9f543ea0f47cbb748a Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 27 Sep 2023 07:59:46 -0400 Subject: [PATCH 08/18] Add call to Google Drive to get the file. Implement forceLoad() Bug fixes in cache and logic. --- langchain/src/hub/googlemakersuitehub.ts | 82 +++++++++++++++++-- .../hub/tests/googlemakersuitehub.int.test.ts | 65 ++++++++++++++- .../src/hub/tests/googlemakersuitehub.test.ts | 65 ++++++++++++++- 3 files changed, 200 insertions(+), 12 deletions(-) diff --git a/langchain/src/hub/googlemakersuitehub.ts b/langchain/src/hub/googlemakersuitehub.ts index 3daa0fb77781..33feb80ed14a 100644 --- a/langchain/src/hub/googlemakersuitehub.ts +++ b/langchain/src/hub/googlemakersuitehub.ts @@ -1,5 +1,6 @@ import { protos } from "@google-ai/generativelanguage"; import { google } from "@google-ai/generativelanguage/build/protos/protos.js"; +import {GoogleAuth, GoogleAuthOptions} from "google-auth-library"; import { GooglePaLM } from "../llms/googlepalm.js"; import { ChatGooglePaLM } from "../chat_models/googlepalm.js"; @@ -8,9 +9,14 @@ import { BaseChatModel } from "../chat_models/base.js"; import { LLM } from "../llms/base.js"; import { Runnable } from "../schema/runnable/index.js"; import IExample = google.ai.generativelanguage.v1beta2.IExample; +import {AsyncCaller, AsyncCallerCallOptions} from "../util/async_caller.js"; +import { GoogleResponse, GoogleVertexAIConnectionParams } from "../types/googlevertexai-types.js"; +import { GoogleConnection } from "../util/googlevertexai-connection.js"; export interface MakerSuiteHubConfig { cacheTimeout: number, + + caller?: AsyncCaller, } type MakerSuitePromptType = "text" | "data" | "chat"; @@ -227,19 +233,74 @@ export class MakerSuitePrompt { } -interface CacheEntry { +interface DriveFileReadParams extends GoogleVertexAIConnectionParams{ + fileId: string; +} + +interface DriveCallOptions extends AsyncCallerCallOptions { + // Empty, I think +} + +interface DriveFileMakerSuiteResponse extends GoogleResponse { + data: MakerSuitePromptData; +} + +export class DriveFileReadConnection + extends GoogleConnection + implements DriveFileReadParams +{ + + endpoint: string; + + apiVersion: string; + + fileId: string; + + constructor( + fields: DriveFileReadParams, + caller: AsyncCaller + ) { + super(caller, new GoogleAuth({ + scopes: "https://www.googleapis.com/auth/drive.readonly", + ...fields.authOptions + })); + + this.endpoint = fields.endpoint ?? "www.googleapis.com"; + this.apiVersion = fields.apiVersion ?? "v3"; + + this.fileId = fields.fileId; + } + + async buildUrl(): Promise { + return `https://${this.endpoint}/drive/${this.apiVersion}/files/${this.fileId}?alt=media`; + } + + buildMethod(): string { + return "GET"; + } + + async request(options?: DriveCallOptions): Promise { + return this._request(undefined, options ?? {}); + } + +} + +export interface CacheEntry { updated: number, prompt: MakerSuitePrompt, } export class MakerSuiteHub { - cache: Record; + cache: Record = {}; cacheTimeout: number; + caller: AsyncCaller; + constructor(config?: MakerSuiteHubConfig){ this.cacheTimeout = config?.cacheTimeout ?? 0; + this.caller = config?.caller ?? new AsyncCaller({}); } isValid(entry: CacheEntry): boolean { @@ -251,14 +312,21 @@ export class MakerSuiteHub { const now = Date.now(); const expiration = entry.updated + this.cacheTimeout; - return expiration < now; + return expiration > now; } async forceLoad(id: string): Promise { - console.log('forceLoad', id); - return new MakerSuitePrompt({ - textPrompt: {} - }); + const fields: DriveFileReadParams = { + fileId: id + }; + const connection = new DriveFileReadConnection(fields,this.caller); + const result = await connection.request(); + const ret = new MakerSuitePrompt(result.data); + this.cache[id] = { + prompt: ret, + updated: Date.now(), + }; + return ret; } async load(id: string): Promise { diff --git a/langchain/src/hub/tests/googlemakersuitehub.int.test.ts b/langchain/src/hub/tests/googlemakersuitehub.int.test.ts index 4b561c4ac9fc..a825f5c9cadd 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.int.test.ts +++ b/langchain/src/hub/tests/googlemakersuitehub.int.test.ts @@ -1,9 +1,15 @@ +// noinspection DuplicatedCode + import fs from "fs"; import { fileURLToPath } from "node:url"; import * as path from "path"; import { describe, test } from "@jest/globals"; -import { MakerSuitePrompt } from "../googlemakersuitehub.js"; +import {DriveFileReadConnection, MakerSuiteHub, MakerSuitePrompt} from "../googlemakersuitehub.js"; +import {AsyncCaller} from "../../util/async_caller.js"; +import {HumanMessage} from "../../schema/index.js"; +import {ChatGooglePaLM} from "../../chat_models/googlepalm.js"; +import {GooglePaLM} from "../../llms/googlepalm.js"; describe("Google Maker Suite Hub Integration", () => { @@ -12,8 +18,8 @@ describe("Google Maker Suite Hub Integration", () => { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - // const chatFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/chatPrompt.json`, "utf8")); - // const dataFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/dataPrompt.json`, "utf8")); + const chatFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/chatPrompt.json`, "utf8")); + const dataFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/dataPrompt.json`, "utf8")); const textFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/textPrompt.json`, "utf8")); test("text chain", async () => { @@ -26,6 +32,59 @@ describe("Google Maker Suite Hub Integration", () => { expect(result).toBeTruthy(); }) + test("data chain", async () => { + const prompt = new MakerSuitePrompt(dataFile); + const chain = prompt.toChain(); + const result = await chain.invoke({ + description: "shoes" + }); + console.log("data chain result", result); + expect(result).toBeTruthy(); + + }) + + test("chat model", async () => { + const prompt = new MakerSuitePrompt(chatFile); + const model = prompt.toModel() as ChatGooglePaLM; + const message = new HumanMessage("Hello!"); + const result = await model.call([message]); + expect(result).toBeTruthy(); + console.log({ result }); + + }) + + }) + + describe("Drive", () => { + test("file get media", async () => { + const fileId = "1IAWobj3BYvbj5X3JOAKaoXTcNJlZLdpK"; + const caller = new AsyncCaller({}); + const connection = new DriveFileReadConnection({fileId}, caller); + console.log('connection client',connection?.client); + const result = await connection.request(); + console.log(result); + }) + }) + + describe("Hub", () => { + + const hub = new MakerSuiteHub(); + + test("text model", async () => { + const prompt = await hub.load("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); + const model = prompt.toModel() as GooglePaLM; + const result = await model.call("What would be a good name for a company that makes socks") + console.log("text chain result", result); + expect(result).toBeTruthy(); + }) + + test("text chain", async () => { + const prompt = await hub.load("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); + const result = await prompt.toChain().invoke({product: "socks"}); + console.log("text chain result", result); + expect(result).toBeTruthy(); + }) + }) }) \ No newline at end of file diff --git a/langchain/src/hub/tests/googlemakersuitehub.test.ts b/langchain/src/hub/tests/googlemakersuitehub.test.ts index d49689f9f0d3..815c59c3e300 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.test.ts +++ b/langchain/src/hub/tests/googlemakersuitehub.test.ts @@ -2,8 +2,8 @@ import fs from "fs"; import { fileURLToPath } from "node:url"; import * as path from "path"; -import { describe, test } from "@jest/globals"; -import { MakerSuitePrompt } from "../googlemakersuitehub.js"; +import {describe, expect, test} from "@jest/globals"; +import {MakerSuiteHub, MakerSuitePrompt} from "../googlemakersuitehub.js"; import {ChatGooglePaLM} from "../../chat_models/googlepalm.js"; describe("Google Maker Suite Hub", () => { @@ -80,4 +80,65 @@ describe("Google Maker Suite Hub", () => { }); + describe("MakerSuiteHub", () => { + + test("isValid no entry", () => { + const nonexistentId = "nonexistent"; + const hub = new MakerSuiteHub({cacheTimeout: 1000}); + const entry = hub.cache[nonexistentId]; + const isValid = hub.isValid(entry); + expect(isValid).toEqual(false); + }); + + test("isValid timeout 0", () => { + // This should never be valid because the cache timeout will be 0 + const fakeId = "fake"; + const hub = new MakerSuiteHub({cacheTimeout: 0}); + const entry = { + updated: Date.now(), + prompt: new MakerSuitePrompt({ + textPrompt: { + value: "test" + } + }) + }; + hub.cache[fakeId] = entry; + const isValid = hub.isValid(entry); + expect(isValid).toEqual(false); + }); + + test("isValid valid", () => { + const fakeId = "fake"; + const hub = new MakerSuiteHub({cacheTimeout: 60000}); + const entry = { + updated: Date.now(), + prompt: new MakerSuitePrompt({ + textPrompt: { + value: "test" + } + }) + }; + hub.cache[fakeId] = entry; + const isValid = hub.isValid(entry); + expect(isValid).toEqual(true); + }); + + test("isValid timeout", () => { + const fakeId = "fake"; + const hub = new MakerSuiteHub({cacheTimeout: 60000}); + const entry = { + updated: Date.now() - 100000, + prompt: new MakerSuitePrompt({ + textPrompt: { + value: "test" + } + }) + }; + hub.cache[fakeId] = entry; + const isValid = hub.isValid(entry); + expect(isValid).toEqual(false); + }); + + }); + }) \ No newline at end of file From d1b9735b0c76bdf99ce3e50f56f88420419c5c58 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 27 Sep 2023 08:04:00 -0400 Subject: [PATCH 09/18] Code cleanup --- langchain/src/hub/googlemakersuitehub.ts | 228 ++++++++++-------- .../googlemakersuite-files/chatPrompt.json | 52 ++-- .../googlemakersuite-files/dataPrompt.json | 151 +++++++----- .../googlemakersuite-files/textPrompt.json | 72 +++--- .../hub/tests/googlemakersuitehub.int.test.ts | 77 +++--- .../src/hub/tests/googlemakersuitehub.test.ts | 101 ++++---- langchain/src/types/googlevertexai-types.ts | 3 +- .../src/util/googlevertexai-connection.ts | 20 +- 8 files changed, 394 insertions(+), 310 deletions(-) diff --git a/langchain/src/hub/googlemakersuitehub.ts b/langchain/src/hub/googlemakersuitehub.ts index 33feb80ed14a..efd064b2b2d8 100644 --- a/langchain/src/hub/googlemakersuitehub.ts +++ b/langchain/src/hub/googlemakersuitehub.ts @@ -1,6 +1,6 @@ import { protos } from "@google-ai/generativelanguage"; import { google } from "@google-ai/generativelanguage/build/protos/protos.js"; -import {GoogleAuth, GoogleAuthOptions} from "google-auth-library"; +import { GoogleAuth, GoogleAuthOptions } from "google-auth-library"; import { GooglePaLM } from "../llms/googlepalm.js"; import { ChatGooglePaLM } from "../chat_models/googlepalm.js"; @@ -9,89 +9,91 @@ import { BaseChatModel } from "../chat_models/base.js"; import { LLM } from "../llms/base.js"; import { Runnable } from "../schema/runnable/index.js"; import IExample = google.ai.generativelanguage.v1beta2.IExample; -import {AsyncCaller, AsyncCallerCallOptions} from "../util/async_caller.js"; -import { GoogleResponse, GoogleVertexAIConnectionParams } from "../types/googlevertexai-types.js"; +import { AsyncCaller, AsyncCallerCallOptions } from "../util/async_caller.js"; +import { + GoogleResponse, + GoogleVertexAIConnectionParams, +} from "../types/googlevertexai-types.js"; import { GoogleConnection } from "../util/googlevertexai-connection.js"; export interface MakerSuiteHubConfig { - cacheTimeout: number, + cacheTimeout: number; - caller?: AsyncCaller, + caller?: AsyncCaller; } type MakerSuitePromptType = "text" | "data" | "chat"; export interface MakerSuitePromptVariable { - variableId: string, - displayName: string, + variableId: string; + displayName: string; } export interface MakerSuiteRunSettings { - temperature?: number, - model: string, - candidateCount?: number, - topP?: number, - topK?: number, - maxOutputTokens: number, - safetySettings?: protos.google.ai.generativelanguage.v1beta2.ISafetySetting[], + temperature?: number; + model: string; + candidateCount?: number; + topP?: number; + topK?: number; + maxOutputTokens: number; + safetySettings?: protos.google.ai.generativelanguage.v1beta2.ISafetySetting[]; } export interface MakerSuiteTextPromptData { textPrompt: { - value?: string, - variables?: MakerSuitePromptVariable[], - }, - runSettings?: MakerSuiteRunSettings, - testExamples?: unknown, + value?: string; + variables?: MakerSuitePromptVariable[]; + }; + runSettings?: MakerSuiteRunSettings; + testExamples?: unknown; } export interface MakerSuiteDataPromptColumn { - columnId: string, - displayName: string, - isInput?: boolean, + columnId: string; + displayName: string; + isInput?: boolean; } export interface MakerSuiteDataPromptRow { - rowId: string, - columnBindings: Record, + rowId: string; + columnBindings: Record; } export interface MakerSuiteDataPromptData { dataPrompt: { - preamble: string, - columns: MakerSuiteDataPromptColumn[], - rows: MakerSuiteDataPromptRow[], - rowsUsed: string[], - }, - runSettings?: MakerSuiteRunSettings, - testExamples?: unknown, + preamble: string; + columns: MakerSuiteDataPromptColumn[]; + rows: MakerSuiteDataPromptRow[]; + rowsUsed: string[]; + }; + runSettings?: MakerSuiteRunSettings; + testExamples?: unknown; } export interface MakerSuiteChatExchange { - request?: string, - response?: string, - source: string, - id: string, + request?: string; + response?: string; + source: string; + id: string; } export interface MakerSuiteChatPromptData { multiturnPrompt: { - preamble: string, - primingExchanges: MakerSuiteChatExchange[], + preamble: string; + primingExchanges: MakerSuiteChatExchange[]; sessions: { - sessionExchanges: MakerSuiteChatExchange[] - }[] - }, - runSettings?: MakerSuiteRunSettings, + sessionExchanges: MakerSuiteChatExchange[]; + }[]; + }; + runSettings?: MakerSuiteRunSettings; } export type MakerSuitePromptData = - MakerSuiteTextPromptData | - MakerSuiteDataPromptData | - MakerSuiteChatPromptData; + | MakerSuiteTextPromptData + | MakerSuiteDataPromptData + | MakerSuiteChatPromptData; export class MakerSuitePrompt { - promptType: MakerSuitePromptType; promptData: MakerSuitePromptData; @@ -104,13 +106,10 @@ export class MakerSuitePrompt { _determinePromptType() { if (Object.hasOwn(this.promptData, "textPrompt")) { this.promptType = "text"; - } else if (Object.hasOwn(this.promptData, "dataPrompt")) { this.promptType = "data"; - } else if (Object.hasOwn(this.promptData, "multiturnPrompt")) { this.promptType = "chat"; - } else { const error = new Error("Unable to identify prompt type."); (error as any).promptData = this.promptData; @@ -119,59 +118,74 @@ export class MakerSuitePrompt { } _promptValueText(): string { - return (this.promptData as MakerSuiteTextPromptData)?.textPrompt?.value ?? ""; + return ( + (this.promptData as MakerSuiteTextPromptData)?.textPrompt?.value ?? "" + ); } _promptValueData(): string { - const promptData: MakerSuiteDataPromptData = this.promptData as MakerSuiteDataPromptData; + const promptData: MakerSuiteDataPromptData = this + .promptData as MakerSuiteDataPromptData; const dataPrompt = promptData?.dataPrompt; let prompt = `${dataPrompt?.preamble}\n` || ""; - dataPrompt?.rows.forEach( row => { + dataPrompt?.rows.forEach((row) => { // Add the data for each row, as long as it is listed as used if (dataPrompt?.rowsUsed.includes(row.rowId)) { - // Add each input column - dataPrompt?.columns.forEach( column => { + dataPrompt?.columns.forEach((column) => { if (column.isInput) { - prompt += `${column.displayName} ${row.columnBindings[column.columnId]}\n`; + prompt += `${column.displayName} ${ + row.columnBindings[column.columnId] + }\n`; } }); // Add each output column - dataPrompt?.columns.forEach( column => { + dataPrompt?.columns.forEach((column) => { if (!column.isInput) { - prompt += `${column.displayName} ${row.columnBindings[column.columnId]}\n`; + prompt += `${column.displayName} ${ + row.columnBindings[column.columnId] + }\n`; } }); - } }); // Add the input column prompts - dataPrompt?.columns.forEach( column => { + dataPrompt?.columns.forEach((column) => { if (column.isInput) { - prompt += `${column.displayName} {${column.displayName.replace(":", "")}}\n` + prompt += `${column.displayName} {${column.displayName.replace( + ":", + "" + )}}\n`; } - }) + }); // Add just the first output column - const firstOutput = dataPrompt?.columns.find( column => !column.isInput ); + const firstOutput = dataPrompt?.columns.find((column) => !column.isInput); prompt += `${firstOutput?.displayName} `; return prompt; } _promptValueChat(): string { - return (this.promptData as MakerSuiteChatPromptData)?.multiturnPrompt?.preamble ?? ""; + return ( + (this.promptData as MakerSuiteChatPromptData)?.multiturnPrompt + ?.preamble ?? "" + ); } _promptValue(): string { switch (this.promptType) { - case "text": return this._promptValueText(); - case "data": return this._promptValueData(); - case "chat": return this._promptValueChat(); - default: throw new Error(`Invalid promptType: ${this.promptType}`); + case "text": + return this._promptValueText(); + case "data": + return this._promptValueData(); + case "chat": + return this._promptValueChat(); + default: + throw new Error(`Invalid promptType: ${this.promptType}`); } } @@ -184,29 +198,33 @@ export class MakerSuitePrompt { _modelName(): string { let ret = this.promptData?.runSettings?.model; if (!ret) { - ret = this.promptType === "chat" ? - "models/chat-bison-001" : - "models/text-bison-001"; + ret = + this.promptType === "chat" + ? "models/chat-bison-001" + : "models/text-bison-001"; } return ret; } _examples(): IExample[] { - const promptData: MakerSuiteChatPromptData = this.promptData as MakerSuiteChatPromptData; - const ret: IExample[] = promptData?.multiturnPrompt?.primingExchanges.map( exchange => { - const example:IExample = {}; - if (exchange?.request) { - example.input = { - content: exchange.request + const promptData: MakerSuiteChatPromptData = this + .promptData as MakerSuiteChatPromptData; + const ret: IExample[] = promptData?.multiturnPrompt?.primingExchanges + .map((exchange) => { + const example: IExample = {}; + if (exchange?.request) { + example.input = { + content: exchange.request, + }; } - } - if (exchange?.response) { - example.output = { - content: exchange.response + if (exchange?.response) { + example.output = { + content: exchange.response, + }; } - } - return example; - }).filter( value => Object.keys(value).length ); + return example; + }) + .filter((value) => Object.keys(value).length); return ret; } @@ -214,13 +232,13 @@ export class MakerSuitePrompt { const modelName = this._modelName(); const modelSettings = { modelName, - ...this.promptData?.runSettings - } + ...this.promptData?.runSettings, + }; if (this.promptType === "chat") { const examples = this._examples(); return new ChatGooglePaLM({ examples, - ...modelSettings + ...modelSettings, }); } else { return new GooglePaLM(modelSettings); @@ -230,10 +248,10 @@ export class MakerSuitePrompt { toChain() { return this.toTemplate().pipe(this.toModel() as Runnable); } - } -interface DriveFileReadParams extends GoogleVertexAIConnectionParams{ +interface DriveFileReadParams + extends GoogleVertexAIConnectionParams { fileId: string; } @@ -249,21 +267,20 @@ export class DriveFileReadConnection extends GoogleConnection implements DriveFileReadParams { - endpoint: string; apiVersion: string; fileId: string; - constructor( - fields: DriveFileReadParams, - caller: AsyncCaller - ) { - super(caller, new GoogleAuth({ - scopes: "https://www.googleapis.com/auth/drive.readonly", - ...fields.authOptions - })); + constructor(fields: DriveFileReadParams, caller: AsyncCaller) { + super( + caller, + new GoogleAuth({ + scopes: "https://www.googleapis.com/auth/drive.readonly", + ...fields.authOptions, + }) + ); this.endpoint = fields.endpoint ?? "www.googleapis.com"; this.apiVersion = fields.apiVersion ?? "v3"; @@ -279,26 +296,26 @@ export class DriveFileReadConnection return "GET"; } - async request(options?: DriveCallOptions): Promise { + async request( + options?: DriveCallOptions + ): Promise { return this._request(undefined, options ?? {}); } - } export interface CacheEntry { - updated: number, - prompt: MakerSuitePrompt, + updated: number; + prompt: MakerSuitePrompt; } export class MakerSuiteHub { - cache: Record = {}; cacheTimeout: number; caller: AsyncCaller; - constructor(config?: MakerSuiteHubConfig){ + constructor(config?: MakerSuiteHubConfig) { this.cacheTimeout = config?.cacheTimeout ?? 0; this.caller = config?.caller ?? new AsyncCaller({}); } @@ -317,9 +334,9 @@ export class MakerSuiteHub { async forceLoad(id: string): Promise { const fields: DriveFileReadParams = { - fileId: id + fileId: id, }; - const connection = new DriveFileReadConnection(fields,this.caller); + const connection = new DriveFileReadConnection(fields, this.caller); const result = await connection.request(); const ret = new MakerSuitePrompt(result.data); this.cache[id] = { @@ -334,5 +351,4 @@ export class MakerSuiteHub { const ret = this.isValid(entry) ? entry.prompt : await this.forceLoad(id); return ret; } - -} \ No newline at end of file +} diff --git a/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json b/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json index 471f14130feb..ab221bcbe65b 100644 --- a/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json +++ b/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json @@ -9,27 +9,35 @@ }, "multiturnPrompt": { "preamble": "You are responsible for returning date and time information based on the user\u0027s input. All your date and time responses should be as an ISO8601 formatted response that include the year, month, day, hour, minute, second, and time zone offset. You should only return this time information and nothing else.\n", - "primingExchanges": [{ - "request": "What time is it?", - "response": "2023-09-16T02:03:04-0500", - "source": "MODEL_EDITED", - "id": "4B26EE6A-6171-4626-80B6-207491D106BF" - }, { - "source": "USER", - "id": "36D72C06-423E-4E00-BA27-DEC44CBA5A13" - }], - "sessions": [{ - "sessionExchanges": [{ + "primingExchanges": [ + { "request": "What time is it?", - "response": "The time is 2023-09-16T02:04:05-0500.", - "source": "MODEL", - "id": "9E4D6C9A-81D2-4EE9-BE8B-96C84F1FDA64" - }, { - "request": "What time was it one week ago?", - "response": "The time one week ago was 2023-09-09T02:04:05-0500.", - "source": "MODEL", - "id": "EA76CED1-F626-46F3-8CCA-4C9C821B2003" - }] - }] + "response": "2023-09-16T02:03:04-0500", + "source": "MODEL_EDITED", + "id": "4B26EE6A-6171-4626-80B6-207491D106BF" + }, + { + "source": "USER", + "id": "36D72C06-423E-4E00-BA27-DEC44CBA5A13" + } + ], + "sessions": [ + { + "sessionExchanges": [ + { + "request": "What time is it?", + "response": "The time is 2023-09-16T02:04:05-0500.", + "source": "MODEL", + "id": "9E4D6C9A-81D2-4EE9-BE8B-96C84F1FDA64" + }, + { + "request": "What time was it one week ago?", + "response": "The time one week ago was 2023-09-09T02:04:05-0500.", + "source": "MODEL", + "id": "EA76CED1-F626-46F3-8CCA-4C9C821B2003" + } + ] + } + ] } -} \ No newline at end of file +} diff --git a/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json b/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json index 79a5483e0673..6a5d6878f99e 100644 --- a/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json +++ b/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json @@ -1,28 +1,37 @@ { "dataPrompt": { "preamble": "Given a product description, you should return a name for that product that includes something about rainbows.", - "columns": [{ - "columnId": "CFA438B7-313B-4A50-AF74-3ACBC9216A4B", - "displayName": "description:", - "isInput": true - }, { - "columnId": "7D38B937-6636-4AAC-9DFD-B4A60D98C011", - "displayName": "product:" - }], - "rows": [{ - "columnBindings": { - "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "socks", - "7D38B937-6636-4AAC-9DFD-B4A60D98C011": "spectrum socks" + "columns": [ + { + "columnId": "CFA438B7-313B-4A50-AF74-3ACBC9216A4B", + "displayName": "description:", + "isInput": true }, - "rowId": "D412F0BE-5C81-401D-917C-758A6F14EEC4" - }, { - "columnBindings": { - "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "hair ties", - "7D38B937-6636-4AAC-9DFD-B4A60D98C011": "rainbows^2" + { + "columnId": "7D38B937-6636-4AAC-9DFD-B4A60D98C011", + "displayName": "product:" + } + ], + "rows": [ + { + "columnBindings": { + "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "socks", + "7D38B937-6636-4AAC-9DFD-B4A60D98C011": "spectrum socks" + }, + "rowId": "D412F0BE-5C81-401D-917C-758A6F14EEC4" }, - "rowId": "C77A45B4-74B4-41F3-AD8E-06F0C1D9D820" - }], - "rowsUsed": ["D412F0BE-5C81-401D-917C-758A6F14EEC4", "C77A45B4-74B4-41F3-AD8E-06F0C1D9D820"] + { + "columnBindings": { + "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "hair ties", + "7D38B937-6636-4AAC-9DFD-B4A60D98C011": "rainbows^2" + }, + "rowId": "C77A45B4-74B4-41F3-AD8E-06F0C1D9D820" + } + ], + "rowsUsed": [ + "D412F0BE-5C81-401D-917C-758A6F14EEC4", + "C77A45B4-74B4-41F3-AD8E-06F0C1D9D820" + ] }, "runSettings": { "temperature": 0.7, @@ -31,51 +40,69 @@ "topP": 0.95, "topK": 40, "maxOutputTokens": 1024, - "safetySettings": [{ - "category": "HARM_CATEGORY_DEROGATORY", - "threshold": "BLOCK_LOW_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_TOXICITY", - "threshold": "BLOCK_LOW_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_VIOLENCE", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_SEXUAL", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_MEDICAL", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_DANGEROUS", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }] - }, - "testExamples": [{ - "inputBindings": { - "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "condoms" - }, - "modelResponses": [{ - "response": "rainbow rhapsody", - "safetyRatings": [{ + "safetySettings": [ + { "category": "HARM_CATEGORY_DEROGATORY", - "probability": "NEGLIGIBLE" - }, { + "threshold": "BLOCK_LOW_AND_ABOVE" + }, + { "category": "HARM_CATEGORY_TOXICITY", - "probability": "NEGLIGIBLE" - }, { + "threshold": "BLOCK_LOW_AND_ABOVE" + }, + { "category": "HARM_CATEGORY_VIOLENCE", - "probability": "NEGLIGIBLE" - }, { + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, + { "category": "HARM_CATEGORY_SEXUAL", - "probability": "LOW" - }, { + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, + { "category": "HARM_CATEGORY_MEDICAL", - "probability": "NEGLIGIBLE" - }, { + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, + { "category": "HARM_CATEGORY_DANGEROUS", - "probability": "NEGLIGIBLE" - }] - }] - }] -} \ No newline at end of file + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + } + ] + }, + "testExamples": [ + { + "inputBindings": { + "CFA438B7-313B-4A50-AF74-3ACBC9216A4B": "condoms" + }, + "modelResponses": [ + { + "response": "rainbow rhapsody", + "safetyRatings": [ + { + "category": "HARM_CATEGORY_DEROGATORY", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_TOXICITY", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_VIOLENCE", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_SEXUAL", + "probability": "LOW" + }, + { + "category": "HARM_CATEGORY_MEDICAL", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS", + "probability": "NEGLIGIBLE" + } + ] + } + ] + } + ] +} diff --git a/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json b/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json index 494827af504c..15297be307d5 100644 --- a/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json +++ b/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json @@ -1,10 +1,12 @@ { "textPrompt": { "value": "What would be a good name for a company that makes {{30E275F8-0B60-4E71-843D-9865F4D4EFD4:product:​}}?", - "variables": [{ - "variableId": "30E275F8-0B60-4E71-843D-9865F4D4EFD4", - "displayName": "product" - }] + "variables": [ + { + "variableId": "30E275F8-0B60-4E71-843D-9865F4D4EFD4", + "displayName": "product" + } + ] }, "runSettings": { "temperature": 0.7, @@ -13,31 +15,39 @@ "topP": 0.95, "topK": 40, "maxOutputTokens": 1024, - "safetySettings": [{ - "category": "HARM_CATEGORY_DEROGATORY", - "threshold": "BLOCK_LOW_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_TOXICITY", - "threshold": "BLOCK_LOW_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_VIOLENCE", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_SEXUAL", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_MEDICAL", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, { - "category": "HARM_CATEGORY_DANGEROUS", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }] + "safetySettings": [ + { + "category": "HARM_CATEGORY_DEROGATORY", + "threshold": "BLOCK_LOW_AND_ABOVE" + }, + { + "category": "HARM_CATEGORY_TOXICITY", + "threshold": "BLOCK_LOW_AND_ABOVE" + }, + { + "category": "HARM_CATEGORY_VIOLENCE", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, + { + "category": "HARM_CATEGORY_SEXUAL", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, + { + "category": "HARM_CATEGORY_MEDICAL", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS", + "threshold": "BLOCK_MEDIUM_AND_ABOVE" + } + ] }, - "testExamples": [{ - "inputBindings": { - "30E275F8-0B60-4E71-843D-9865F4D4EFD4": "" - }, - "modelResponses": [{ - }] - }] -} \ No newline at end of file + "testExamples": [ + { + "inputBindings": { + "30E275F8-0B60-4E71-843D-9865F4D4EFD4": "" + }, + "modelResponses": [{}] + } + ] +} diff --git a/langchain/src/hub/tests/googlemakersuitehub.int.test.ts b/langchain/src/hub/tests/googlemakersuitehub.int.test.ts index a825f5c9cadd..5bef75631bc8 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.int.test.ts +++ b/langchain/src/hub/tests/googlemakersuitehub.int.test.ts @@ -5,43 +5,59 @@ import { fileURLToPath } from "node:url"; import * as path from "path"; import { describe, test } from "@jest/globals"; -import {DriveFileReadConnection, MakerSuiteHub, MakerSuitePrompt} from "../googlemakersuitehub.js"; -import {AsyncCaller} from "../../util/async_caller.js"; -import {HumanMessage} from "../../schema/index.js"; -import {ChatGooglePaLM} from "../../chat_models/googlepalm.js"; -import {GooglePaLM} from "../../llms/googlepalm.js"; +import { + DriveFileReadConnection, + MakerSuiteHub, + MakerSuitePrompt, +} from "../googlemakersuitehub.js"; +import { AsyncCaller } from "../../util/async_caller.js"; +import { HumanMessage } from "../../schema/index.js"; +import { ChatGooglePaLM } from "../../chat_models/googlepalm.js"; +import { GooglePaLM } from "../../llms/googlepalm.js"; describe("Google Maker Suite Hub Integration", () => { - describe("Prompt", () => { - const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - const chatFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/chatPrompt.json`, "utf8")); - const dataFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/dataPrompt.json`, "utf8")); - const textFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/textPrompt.json`, "utf8")); + const chatFile = JSON.parse( + fs.readFileSync( + `${__dirname}/googlemakersuite-files/chatPrompt.json`, + "utf8" + ) + ); + const dataFile = JSON.parse( + fs.readFileSync( + `${__dirname}/googlemakersuite-files/dataPrompt.json`, + "utf8" + ) + ); + const textFile = JSON.parse( + fs.readFileSync( + `${__dirname}/googlemakersuite-files/textPrompt.json`, + "utf8" + ) + ); test("text chain", async () => { const prompt = new MakerSuitePrompt(textFile); const chain = prompt.toChain(); const result = await chain.invoke({ - product: "shoes" + product: "shoes", }); console.log("text chain result", result); expect(result).toBeTruthy(); - }) + }); test("data chain", async () => { const prompt = new MakerSuitePrompt(dataFile); const chain = prompt.toChain(); const result = await chain.invoke({ - description: "shoes" + description: "shoes", }); console.log("data chain result", result); expect(result).toBeTruthy(); - - }) + }); test("chat model", async () => { const prompt = new MakerSuitePrompt(chatFile); @@ -50,41 +66,38 @@ describe("Google Maker Suite Hub Integration", () => { const result = await model.call([message]); expect(result).toBeTruthy(); console.log({ result }); - - }) - - }) + }); + }); describe("Drive", () => { test("file get media", async () => { const fileId = "1IAWobj3BYvbj5X3JOAKaoXTcNJlZLdpK"; const caller = new AsyncCaller({}); - const connection = new DriveFileReadConnection({fileId}, caller); - console.log('connection client',connection?.client); + const connection = new DriveFileReadConnection({ fileId }, caller); + console.log("connection client", connection?.client); const result = await connection.request(); console.log(result); - }) - }) + }); + }); describe("Hub", () => { - const hub = new MakerSuiteHub(); test("text model", async () => { const prompt = await hub.load("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); const model = prompt.toModel() as GooglePaLM; - const result = await model.call("What would be a good name for a company that makes socks") + const result = await model.call( + "What would be a good name for a company that makes socks" + ); console.log("text chain result", result); expect(result).toBeTruthy(); - }) + }); test("text chain", async () => { const prompt = await hub.load("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); - const result = await prompt.toChain().invoke({product: "socks"}); + const result = await prompt.toChain().invoke({ product: "socks" }); console.log("text chain result", result); expect(result).toBeTruthy(); - }) - - }) - -}) \ No newline at end of file + }); + }); +}); diff --git a/langchain/src/hub/tests/googlemakersuitehub.test.ts b/langchain/src/hub/tests/googlemakersuitehub.test.ts index 815c59c3e300..b21e03fe59f7 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.test.ts +++ b/langchain/src/hub/tests/googlemakersuitehub.test.ts @@ -2,21 +2,34 @@ import fs from "fs"; import { fileURLToPath } from "node:url"; import * as path from "path"; -import {describe, expect, test} from "@jest/globals"; -import {MakerSuiteHub, MakerSuitePrompt} from "../googlemakersuitehub.js"; -import {ChatGooglePaLM} from "../../chat_models/googlepalm.js"; +import { describe, expect, test } from "@jest/globals"; +import { MakerSuiteHub, MakerSuitePrompt } from "../googlemakersuitehub.js"; +import { ChatGooglePaLM } from "../../chat_models/googlepalm.js"; describe("Google Maker Suite Hub", () => { - const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - const chatFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/chatPrompt.json`, "utf8")); - const dataFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/dataPrompt.json`, "utf8")); - const textFile = JSON.parse(fs.readFileSync(`${__dirname}/googlemakersuite-files/textPrompt.json`, "utf8")); + const chatFile = JSON.parse( + fs.readFileSync( + `${__dirname}/googlemakersuite-files/chatPrompt.json`, + "utf8" + ) + ); + const dataFile = JSON.parse( + fs.readFileSync( + `${__dirname}/googlemakersuite-files/dataPrompt.json`, + "utf8" + ) + ); + const textFile = JSON.parse( + fs.readFileSync( + `${__dirname}/googlemakersuite-files/textPrompt.json`, + "utf8" + ) + ); describe("Prompt", () => { - test("text type", () => { const prompt = new MakerSuitePrompt(textFile); expect(prompt.promptType).toEqual("text"); @@ -25,7 +38,9 @@ describe("Google Maker Suite Hub", () => { test("text template", () => { const prompt = new MakerSuitePrompt(textFile); const template = prompt.toTemplate(); - expect(template.template).toEqual("What would be a good name for a company that makes {product}?") + expect(template.template).toEqual( + "What would be a good name for a company that makes {product}?" + ); }); test("text model", () => { @@ -33,11 +48,11 @@ describe("Google Maker Suite Hub", () => { const model = prompt.toModel(); // console.log(model.lc_namespace); expect(model.lc_namespace).toEqual(["langchain", "llms", "googlepalm"]); - }) + }); test("data type", () => { const prompt = new MakerSuitePrompt(dataFile); - expect(prompt.promptType).toEqual("data") + expect(prompt.promptType).toEqual("data"); }); test("data template", () => { @@ -46,45 +61,47 @@ describe("Google Maker Suite Hub", () => { // console.log("data template", template.template); expect(template.template).toEqual( "Given a product description, you should return a name for that product that includes something about rainbows.\n" + - "description: socks\n" + - "product: spectrum socks\n" + - "description: hair ties\n" + - "product: rainbows^2\n" + - "description: {description}\n" + - "product: " + "description: socks\n" + + "product: spectrum socks\n" + + "description: hair ties\n" + + "product: rainbows^2\n" + + "description: {description}\n" + + "product: " ); - }) + }); test("data model", () => { const prompt = new MakerSuitePrompt(dataFile); const model = prompt.toModel(); expect(model.lc_namespace).toEqual(["langchain", "llms", "googlepalm"]); - }) + }); test("chat type", () => { const prompt = new MakerSuitePrompt(chatFile); expect(prompt.promptType).toEqual("chat"); - }) + }); test("chat model", () => { const prompt = new MakerSuitePrompt(chatFile); const model = prompt.toModel(); - expect(model.lc_namespace).toEqual(["langchain", "chat_models", "googlepalm"]); + expect(model.lc_namespace).toEqual([ + "langchain", + "chat_models", + "googlepalm", + ]); expect((model as ChatGooglePaLM).examples).toEqual([ { - input: { content: 'What time is it?' }, - output: { content: '2023-09-16T02:03:04-0500' } - } + input: { content: "What time is it?" }, + output: { content: "2023-09-16T02:03:04-0500" }, + }, ]); - }) - + }); }); describe("MakerSuiteHub", () => { - test("isValid no entry", () => { const nonexistentId = "nonexistent"; - const hub = new MakerSuiteHub({cacheTimeout: 1000}); + const hub = new MakerSuiteHub({ cacheTimeout: 1000 }); const entry = hub.cache[nonexistentId]; const isValid = hub.isValid(entry); expect(isValid).toEqual(false); @@ -93,14 +110,14 @@ describe("Google Maker Suite Hub", () => { test("isValid timeout 0", () => { // This should never be valid because the cache timeout will be 0 const fakeId = "fake"; - const hub = new MakerSuiteHub({cacheTimeout: 0}); + const hub = new MakerSuiteHub({ cacheTimeout: 0 }); const entry = { updated: Date.now(), prompt: new MakerSuitePrompt({ textPrompt: { - value: "test" - } - }) + value: "test", + }, + }), }; hub.cache[fakeId] = entry; const isValid = hub.isValid(entry); @@ -109,14 +126,14 @@ describe("Google Maker Suite Hub", () => { test("isValid valid", () => { const fakeId = "fake"; - const hub = new MakerSuiteHub({cacheTimeout: 60000}); + const hub = new MakerSuiteHub({ cacheTimeout: 60000 }); const entry = { updated: Date.now(), prompt: new MakerSuitePrompt({ textPrompt: { - value: "test" - } - }) + value: "test", + }, + }), }; hub.cache[fakeId] = entry; const isValid = hub.isValid(entry); @@ -125,20 +142,18 @@ describe("Google Maker Suite Hub", () => { test("isValid timeout", () => { const fakeId = "fake"; - const hub = new MakerSuiteHub({cacheTimeout: 60000}); + const hub = new MakerSuiteHub({ cacheTimeout: 60000 }); const entry = { updated: Date.now() - 100000, prompt: new MakerSuitePrompt({ textPrompt: { - value: "test" - } - }) + value: "test", + }, + }), }; hub.cache[fakeId] = entry; const isValid = hub.isValid(entry); expect(isValid).toEqual(false); }); - }); - -}) \ No newline at end of file +}); diff --git a/langchain/src/types/googlevertexai-types.ts b/langchain/src/types/googlevertexai-types.ts index 3765a10f65c2..422aee5ae01b 100644 --- a/langchain/src/types/googlevertexai-types.ts +++ b/langchain/src/types/googlevertexai-types.ts @@ -4,7 +4,8 @@ export interface GoogleConnectionParams { authOptions?: AuthOptions; } -export interface GoogleVertexAIConnectionParams extends GoogleConnectionParams { +export interface GoogleVertexAIConnectionParams + extends GoogleConnectionParams { /** Hostname for the API call */ endpoint?: string; diff --git a/langchain/src/util/googlevertexai-connection.ts b/langchain/src/util/googlevertexai-connection.ts index 6550fc543c3f..d4aafa969fcf 100644 --- a/langchain/src/util/googlevertexai-connection.ts +++ b/langchain/src/util/googlevertexai-connection.ts @@ -7,22 +7,18 @@ import type { GoogleVertexAILLMResponse, GoogleVertexAIModelParams, GoogleResponse, - GoogleAbstractedClient + GoogleAbstractedClient, } from "../types/googlevertexai-types.js"; export abstract class GoogleConnection< CallOptions extends AsyncCallerCallOptions, ResponseType extends GoogleResponse -> -{ +> { caller: AsyncCaller; client: GoogleAbstractedClient; - constructor( - caller: AsyncCaller, - client: GoogleAbstractedClient - ) { + constructor(caller: AsyncCaller, client: GoogleAbstractedClient) { this.caller = caller; this.client = client; } @@ -59,14 +55,13 @@ export abstract class GoogleConnection< throw x; } } - } export abstract class GoogleVertexAIConnection< - CallOptions extends AsyncCallerCallOptions, - ResponseType extends GoogleResponse, - AuthOptions -> + CallOptions extends AsyncCallerCallOptions, + ResponseType extends GoogleResponse, + AuthOptions + > extends GoogleConnection implements GoogleVertexAIConnectionParams { @@ -93,7 +88,6 @@ export abstract class GoogleVertexAIConnection< buildMethod(): string { return "POST"; } - } export class GoogleVertexAILLMConnection< From 66d424caf9c4d9f0d2cc30728237443ea4c50387 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 27 Sep 2023 10:03:20 -0400 Subject: [PATCH 10/18] Move to experimental --- .../hubs/makersuite}/googlemakersuitehub.ts | 18 +++++++++--------- .../googlemakersuite-files/chatPrompt.json | 0 .../googlemakersuite-files/dataPrompt.json | 0 .../googlemakersuite-files/textPrompt.json | 0 .../tests/googlemakersuitehub.int.test.ts | 8 ++++---- .../tests/googlemakersuitehub.test.ts | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) rename langchain/src/{hub => experimental/hubs/makersuite}/googlemakersuitehub.ts (93%) rename langchain/src/{hub => experimental/hubs/makersuite}/tests/googlemakersuite-files/chatPrompt.json (100%) rename langchain/src/{hub => experimental/hubs/makersuite}/tests/googlemakersuite-files/dataPrompt.json (100%) rename langchain/src/{hub => experimental/hubs/makersuite}/tests/googlemakersuite-files/textPrompt.json (100%) rename langchain/src/{hub => experimental/hubs/makersuite}/tests/googlemakersuitehub.int.test.ts (92%) rename langchain/src/{hub => experimental/hubs/makersuite}/tests/googlemakersuitehub.test.ts (98%) diff --git a/langchain/src/hub/googlemakersuitehub.ts b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts similarity index 93% rename from langchain/src/hub/googlemakersuitehub.ts rename to langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts index efd064b2b2d8..2fe605182538 100644 --- a/langchain/src/hub/googlemakersuitehub.ts +++ b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts @@ -2,19 +2,19 @@ import { protos } from "@google-ai/generativelanguage"; import { google } from "@google-ai/generativelanguage/build/protos/protos.js"; import { GoogleAuth, GoogleAuthOptions } from "google-auth-library"; -import { GooglePaLM } from "../llms/googlepalm.js"; -import { ChatGooglePaLM } from "../chat_models/googlepalm.js"; -import { PromptTemplate } from "../prompts/index.js"; -import { BaseChatModel } from "../chat_models/base.js"; -import { LLM } from "../llms/base.js"; -import { Runnable } from "../schema/runnable/index.js"; +import {GooglePaLM} from "../../../llms/googlepalm.js"; +import { ChatGooglePaLM } from "../../../chat_models/googlepalm.js"; +import { PromptTemplate } from "../../../prompts/index.js"; +import { BaseChatModel } from "../../../chat_models/base.js"; +import { LLM } from "../../../llms/base.js"; +import { Runnable } from "../../../schema/runnable/index.js"; import IExample = google.ai.generativelanguage.v1beta2.IExample; -import { AsyncCaller, AsyncCallerCallOptions } from "../util/async_caller.js"; +import { AsyncCaller, AsyncCallerCallOptions } from "../../../util/async_caller.js"; import { GoogleResponse, GoogleVertexAIConnectionParams, -} from "../types/googlevertexai-types.js"; -import { GoogleConnection } from "../util/googlevertexai-connection.js"; +} from "../../../types/googlevertexai-types.js"; +import { GoogleConnection } from "../../../util/googlevertexai-connection.js"; export interface MakerSuiteHubConfig { cacheTimeout: number; diff --git a/langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuite-files/chatPrompt.json similarity index 100% rename from langchain/src/hub/tests/googlemakersuite-files/chatPrompt.json rename to langchain/src/experimental/hubs/makersuite/tests/googlemakersuite-files/chatPrompt.json diff --git a/langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuite-files/dataPrompt.json similarity index 100% rename from langchain/src/hub/tests/googlemakersuite-files/dataPrompt.json rename to langchain/src/experimental/hubs/makersuite/tests/googlemakersuite-files/dataPrompt.json diff --git a/langchain/src/hub/tests/googlemakersuite-files/textPrompt.json b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuite-files/textPrompt.json similarity index 100% rename from langchain/src/hub/tests/googlemakersuite-files/textPrompt.json rename to langchain/src/experimental/hubs/makersuite/tests/googlemakersuite-files/textPrompt.json diff --git a/langchain/src/hub/tests/googlemakersuitehub.int.test.ts b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts similarity index 92% rename from langchain/src/hub/tests/googlemakersuitehub.int.test.ts rename to langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts index 5bef75631bc8..bcf3508423a2 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.int.test.ts +++ b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts @@ -10,10 +10,10 @@ import { MakerSuiteHub, MakerSuitePrompt, } from "../googlemakersuitehub.js"; -import { AsyncCaller } from "../../util/async_caller.js"; -import { HumanMessage } from "../../schema/index.js"; -import { ChatGooglePaLM } from "../../chat_models/googlepalm.js"; -import { GooglePaLM } from "../../llms/googlepalm.js"; +import { AsyncCaller } from "../../../../util/async_caller.js"; +import { HumanMessage } from "../../../../schema/index.js"; +import { ChatGooglePaLM } from "../../../../chat_models/googlepalm.js"; +import { GooglePaLM } from "../../../../llms/googlepalm.js"; describe("Google Maker Suite Hub Integration", () => { describe("Prompt", () => { diff --git a/langchain/src/hub/tests/googlemakersuitehub.test.ts b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.test.ts similarity index 98% rename from langchain/src/hub/tests/googlemakersuitehub.test.ts rename to langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.test.ts index b21e03fe59f7..6816922ddd7e 100644 --- a/langchain/src/hub/tests/googlemakersuitehub.test.ts +++ b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.test.ts @@ -4,7 +4,7 @@ import * as path from "path"; import { describe, expect, test } from "@jest/globals"; import { MakerSuiteHub, MakerSuitePrompt } from "../googlemakersuitehub.js"; -import { ChatGooglePaLM } from "../../chat_models/googlepalm.js"; +import { ChatGooglePaLM } from "../../../../chat_models/googlepalm.js"; describe("Google Maker Suite Hub", () => { const __filename = fileURLToPath(import.meta.url); From 086821985b0a61578434b166b64a8a9e28755df1 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 27 Sep 2023 11:30:01 -0400 Subject: [PATCH 11/18] Lint fix --- .../src/experimental/hubs/makersuite/googlemakersuitehub.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts index 2fe605182538..b5cc666a1ac8 100644 --- a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts +++ b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts @@ -112,6 +112,7 @@ export class MakerSuitePrompt { this.promptType = "chat"; } else { const error = new Error("Unable to identify prompt type."); + // eslint-disable-next-line @typescript-eslint/no-explicit-any (error as any).promptData = this.promptData; throw error; } From f6843c7dbc489c9502670e4d413ed30e71418504 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 27 Sep 2023 14:42:01 -0400 Subject: [PATCH 12/18] Formatting fix --- .../experimental/hubs/makersuite/googlemakersuitehub.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts index b5cc666a1ac8..32194b998b78 100644 --- a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts +++ b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts @@ -2,14 +2,17 @@ import { protos } from "@google-ai/generativelanguage"; import { google } from "@google-ai/generativelanguage/build/protos/protos.js"; import { GoogleAuth, GoogleAuthOptions } from "google-auth-library"; -import {GooglePaLM} from "../../../llms/googlepalm.js"; +import { GooglePaLM } from "../../../llms/googlepalm.js"; import { ChatGooglePaLM } from "../../../chat_models/googlepalm.js"; import { PromptTemplate } from "../../../prompts/index.js"; import { BaseChatModel } from "../../../chat_models/base.js"; import { LLM } from "../../../llms/base.js"; import { Runnable } from "../../../schema/runnable/index.js"; import IExample = google.ai.generativelanguage.v1beta2.IExample; -import { AsyncCaller, AsyncCallerCallOptions } from "../../../util/async_caller.js"; +import { + AsyncCaller, + AsyncCallerCallOptions, +} from "../../../util/async_caller.js"; import { GoogleResponse, GoogleVertexAIConnectionParams, From 7080b6e0187312e3400df846d9e80ab284a100c2 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Thu, 28 Sep 2023 19:16:41 -0400 Subject: [PATCH 13/18] Make sure tests work --- .../hubs/makersuite/tests/googlemakersuitehub.int.test.ts | 2 +- .../hubs/makersuite/tests/googlemakersuitehub.test.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts index bcf3508423a2..b9ea74ba6f3d 100644 --- a/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts +++ b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts @@ -15,7 +15,7 @@ import { HumanMessage } from "../../../../schema/index.js"; import { ChatGooglePaLM } from "../../../../chat_models/googlepalm.js"; import { GooglePaLM } from "../../../../llms/googlepalm.js"; -describe("Google Maker Suite Hub Integration", () => { +describe.skip("Google Maker Suite Hub Integration", () => { describe("Prompt", () => { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); diff --git a/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.test.ts b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.test.ts index 6816922ddd7e..cd12a3de87c3 100644 --- a/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.test.ts +++ b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.test.ts @@ -29,6 +29,10 @@ describe("Google Maker Suite Hub", () => { ) ); + // We don't need a real key + // eslint-disable-next-line no-process-env + process.env.GOOGLE_PALM_API_KEY = "test"; + describe("Prompt", () => { test("text type", () => { const prompt = new MakerSuitePrompt(textFile); From 70e88934b9c60ff40c4703d213fd544b7b77ad0c Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Tue, 3 Oct 2023 17:45:28 -0400 Subject: [PATCH 14/18] Change load() and forceLoad() to pull() and forcePull() respectively. Chage some imports to import type. --- .../hubs/makersuite/googlemakersuitehub.ts | 51 +++++++++---------- .../tests/googlemakersuitehub.int.test.ts | 4 +- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts index 32194b998b78..e383d3ab029f 100644 --- a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts +++ b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts @@ -1,14 +1,12 @@ -import { protos } from "@google-ai/generativelanguage"; -import { google } from "@google-ai/generativelanguage/build/protos/protos.js"; +import type { protos } from "@google-ai/generativelanguage"; +import type { google } from "@google-ai/generativelanguage/build/protos/protos.js"; import { GoogleAuth, GoogleAuthOptions } from "google-auth-library"; import { GooglePaLM } from "../../../llms/googlepalm.js"; import { ChatGooglePaLM } from "../../../chat_models/googlepalm.js"; import { PromptTemplate } from "../../../prompts/index.js"; -import { BaseChatModel } from "../../../chat_models/base.js"; -import { LLM } from "../../../llms/base.js"; +import { BaseLanguageModel } from "../../../base_language/index.js"; import { Runnable } from "../../../schema/runnable/index.js"; -import IExample = google.ai.generativelanguage.v1beta2.IExample; import { AsyncCaller, AsyncCallerCallOptions, @@ -210,29 +208,30 @@ export class MakerSuitePrompt { return ret; } - _examples(): IExample[] { + _examples(): google.ai.generativelanguage.v1beta2.IExample[] { const promptData: MakerSuiteChatPromptData = this .promptData as MakerSuiteChatPromptData; - const ret: IExample[] = promptData?.multiturnPrompt?.primingExchanges - .map((exchange) => { - const example: IExample = {}; - if (exchange?.request) { - example.input = { - content: exchange.request, - }; - } - if (exchange?.response) { - example.output = { - content: exchange.response, - }; - } - return example; - }) - .filter((value) => Object.keys(value).length); + const ret: google.ai.generativelanguage.v1beta2.IExample[] = + promptData?.multiturnPrompt?.primingExchanges + .map((exchange) => { + const example: google.ai.generativelanguage.v1beta2.IExample = {}; + if (exchange?.request) { + example.input = { + content: exchange.request, + }; + } + if (exchange?.response) { + example.output = { + content: exchange.response, + }; + } + return example; + }) + .filter((value) => Object.keys(value).length); return ret; } - toModel(): LLM | BaseChatModel { + toModel(): BaseLanguageModel { const modelName = this._modelName(); const modelSettings = { modelName, @@ -336,7 +335,7 @@ export class MakerSuiteHub { return expiration > now; } - async forceLoad(id: string): Promise { + async forcePull(id: string): Promise { const fields: DriveFileReadParams = { fileId: id, }; @@ -350,9 +349,9 @@ export class MakerSuiteHub { return ret; } - async load(id: string): Promise { + async pull(id: string): Promise { const entry = this.cache[id]; - const ret = this.isValid(entry) ? entry.prompt : await this.forceLoad(id); + const ret = this.isValid(entry) ? entry.prompt : await this.forcePull(id); return ret; } } diff --git a/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts index b9ea74ba6f3d..e4ea2436c8ac 100644 --- a/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts +++ b/langchain/src/experimental/hubs/makersuite/tests/googlemakersuitehub.int.test.ts @@ -84,7 +84,7 @@ describe.skip("Google Maker Suite Hub Integration", () => { const hub = new MakerSuiteHub(); test("text model", async () => { - const prompt = await hub.load("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); + const prompt = await hub.pull("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); const model = prompt.toModel() as GooglePaLM; const result = await model.call( "What would be a good name for a company that makes socks" @@ -94,7 +94,7 @@ describe.skip("Google Maker Suite Hub Integration", () => { }); test("text chain", async () => { - const prompt = await hub.load("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); + const prompt = await hub.pull("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); const result = await prompt.toChain().invoke({ product: "socks" }); console.log("text chain result", result); expect(result).toBeTruthy(); From 88705de081eaa1566219749b658c87b5a6172548 Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 4 Oct 2023 22:12:50 -0400 Subject: [PATCH 15/18] Documentation --- .../ecosystem/integrations/makersuite.mdx | 124 ++++++++++++++++++ .../hubs/makersuite/googlemakersuitehub.ts | 68 ++++++++++ 2 files changed, 192 insertions(+) create mode 100644 docs/extras/ecosystem/integrations/makersuite.mdx diff --git a/docs/extras/ecosystem/integrations/makersuite.mdx b/docs/extras/ecosystem/integrations/makersuite.mdx new file mode 100644 index 000000000000..a6a7c31c7613 --- /dev/null +++ b/docs/extras/ecosystem/integrations/makersuite.mdx @@ -0,0 +1,124 @@ + +# Google MakerSuite + +Google's [MakerSuite](https://makersuite.google.com/) is a web-based playground +for creating and saving chat, text, and "data" prompts that work with the +Google PaLM API and model. These prompts include the text, which may include +"test input" that act as template parameters, and parameters for the model +including the model name, temperature, etc. + +LangChainJS provides the `MakerSuiteHub` class which lets you pull this prompt +from Google Drive, where they are saved. Once pulled, you can convert the prompt +into a LangChain Template, an LLM Model, or a chain that combines the two. This +hub class has a simple time-based in-memory cache of prompts, so it is not always +accessing the prompt saved in Google Drive. + +Using MakerSuite in this way allows you to treat it as a simple Content Management +System (CMS) of sorts or allows separation of tasks between prompt authors and +other developers. + +## Setup + +You do not need any additional packages beyond those that are required for +either the Google PaLM [text](../../modules/model_io/models/llms/integrations/google_palm) +or [chat](../../modules/model_io/models/chat/integrations/google_palm) model: + +```bash npm2yarn +npm install google-auth-library @google-ai/generativelanguage +``` + +## Credentials and Authorization + +You will need two sets of credentials: + +- An API Key to access the PaLM API. + + Create this at [Google MakerSuite](https://makersuite.google.com/app/apikey). + Then set the key as `GOOGLE_PALM_API_KEY` environment variable. + +- Credentials for a service account that has been permitted access to the + Google Drive APIs. + + These credentials may be used in one of three ways: + + - You are logged into an account (using `gcloud auth application-default login`) + permitted to that project. + - You are running on a machine using a service account that is permitted + to the project. + - You have downloaded the credentials for a service account that is permitted + to the project and set the `GOOGLE_APPLICATION_CREDENTIALS` environment + variable to the path of this file. + +This service account should also be permitted to the MakerSuite folder in Google +Drive or to the specific prompt file itself. Even if the prompt file is permitted +for anyone to read - you will still need a service account that is permitted to +access Google Drive. + +## The Prompt File ID + +The easiest way to get the ID of the prompt file is to open it in MakerSuite +and examine the URL. The URL should look something like: + +``` +https://makersuite.google.com/app/prompts/1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm +``` + +The final portion of this, `1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm` is the ID. + +We will be using this in our examples below. This prompt contains a Template +that is equivalent to: + +``` +What would be a good name for a company that makes {product} +``` + +With model parameters set that include: + +- Model name: Text Bison +- Temperature: 0.7 +- Max outputs: 1 +- Standard safety settings + +## Use + +The most typical way to use the hub consists of two parts: + +1. Creating the `MakerSuiteHub` class once. +2. Pulling the prompt, getting the chain, and providing values for the template + to get the result. + +```typescript +// Create the hub class +const hub = new MakerSuiteHub(); + +// Pull the prompt, get the chain, and invoke it with the template values +const prompt = await hub.pull("1gxLasQIeQdwR4wxtV_nb93b_g9f0GaMm"); +const result = await prompt.toChain().invoke({ product: "socks" }); +console.log("text chain result", result); +``` + +### Configuring the hub + +Since the hub implements a basic in-memory time-based cache, you can configure +how long until a prompt that is saved in the cache will be reloaded. + +This value defaults to 0, indicating it will always be loaded from Google Drive, +or you can set it to the number of milliseconds it will be valid in the cache: + +```typescript +const hub = new MakerSuiteHub({ + cacheTimeout: 3600000, // One hour +}); +``` + +### Getting the Template or Model + +In some cases, you may need to get just the template or just the model +that is represented by the prompt. + +```typescript +const template = prompt.toTemplate(); +const textModel = prompt.toModel() as GooglePaLM; +const chatModel = prompt.toModel() as ChatGooglePaLM; +``` + diff --git a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts index e383d3ab029f..6b83f335fbce 100644 --- a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts +++ b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts @@ -17,7 +17,19 @@ import { } from "../../../types/googlevertexai-types.js"; import { GoogleConnection } from "../../../util/googlevertexai-connection.js"; +/** + * Configuration that allows us to load or pull a prompt that has been created + * by the Google MakerSuite site and saved in Google Drive. + * + * There is a simple in-memory cache of these prompts that is refreshed + * after the cacheTimeout specified in the configuration. + */ export interface MakerSuiteHubConfig { + + /** + * How long, in milliseconds, before a prompt is assumed stale and should + * be refreshed from the copy in Google Drive. + */ cacheTimeout: number; caller?: AsyncCaller; @@ -89,11 +101,22 @@ export interface MakerSuiteChatPromptData { runSettings?: MakerSuiteRunSettings; } +/** + * These are the possible formats that the JSON generated by MakerSuite + * and saved in Google Drive can be. + */ export type MakerSuitePromptData = | MakerSuiteTextPromptData | MakerSuiteDataPromptData | MakerSuiteChatPromptData; +/** + * A class that represents the Prompt that has been created by MakerSuite + * and loaded from Google Drive. It exposes methods to turn this prompt + * into a Template, a Model, and into a chain consisting of these two elements. + * In general, this class should be created by the MakerSuiteHub class and + * not instantiated manually. + */ export class MakerSuitePrompt { promptType: MakerSuitePromptType; @@ -191,6 +214,10 @@ export class MakerSuitePrompt { } } + /** + * Create a template from the prompt, including any "test input" specified + * in MakerSuite as a template parameter. + */ toTemplate(): PromptTemplate { const value = this._promptValue(); const promptString = value.replaceAll(/{{.*:(.+):.*}}/g, "{$1}"); @@ -231,6 +258,10 @@ export class MakerSuitePrompt { return ret; } + /** + * Create a model from the prompt with all the parameters (model name, + * temperature, etc) set as they were in MakerSuite. + */ toModel(): BaseLanguageModel { const modelName = this._modelName(); const modelSettings = { @@ -248,6 +279,13 @@ export class MakerSuitePrompt { } } + /** + * Create a RunnableSequence based on the template and model that can + * be created from this prompt. The template will have parameters available + * based on the "test input" that was set in MakerSuite, and the model + * will have the parameters (model name, temperature, etc) from those in + * MakerSuite. + */ toChain() { return this.toTemplate().pipe(this.toModel() as Runnable); } @@ -311,6 +349,15 @@ export interface CacheEntry { prompt: MakerSuitePrompt; } +/** + * A class allowing access to MakerSuite prompts that have been saved in + * Google Drive. + * MakerSuite prompts are pulled based on their Google Drive ID (which is available + * from Google Drive or as part of the URL when the prompt is open in MakerSuite). + * There is a basic cache that will store the prompt in memory for a time specified + * in milliseconds. This defaults to 0, indicating the prompt should always be + * pulled from Google Drive. + */ export class MakerSuiteHub { cache: Record = {}; @@ -323,6 +370,14 @@ export class MakerSuiteHub { this.caller = config?.caller ?? new AsyncCaller({}); } + /** + * Is the current cache entry valid, or does it need to be refreshed. + * It will need to be refreshed under any of the following conditions: + * - It does not currently exist in the cache + * - The cacheTimeout is 0 + * - The cache was last updated longer ago than the cacheTimeout allows + * @param entry - The cache entry, including when this prompt was last refreshed + */ isValid(entry: CacheEntry): boolean { // If we don't have a record, it can't be valid // And if the cache timeout is 0, we will always refresh, so the cache is invalid @@ -335,6 +390,12 @@ export class MakerSuiteHub { return expiration > now; } + /** + * Get a MakerSuitePrompt that is specified by the Google Drive ID. + * This will always be loaded from Google Drive. + * @param id + * @return A MakerSuitePrompt which can be used to create a template, model, or chain. + */ async forcePull(id: string): Promise { const fields: DriveFileReadParams = { fileId: id, @@ -349,6 +410,13 @@ export class MakerSuiteHub { return ret; } + /** + * Get a MakerSuitePrompt that is specified by the Google Drive ID. This may be + * loaded from Google Drive or, if there is a valid copy in the cache, the cached + * copy will be returned. + * @param id + * @return A MakerSuitePrompt which can be used to create a template, model, or chain. + */ async pull(id: string): Promise { const entry = this.cache[id]; const ret = this.isValid(entry) ? entry.prompt : await this.forcePull(id); From a8bc381579b73983ff40a0a2ec376f3b223690da Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 4 Oct 2023 22:19:54 -0400 Subject: [PATCH 16/18] Entrypoint --- docs/extras/ecosystem/integrations/makersuite.mdx | 1 + langchain/scripts/create-entrypoints.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/extras/ecosystem/integrations/makersuite.mdx b/docs/extras/ecosystem/integrations/makersuite.mdx index a6a7c31c7613..8a1497427979 100644 --- a/docs/extras/ecosystem/integrations/makersuite.mdx +++ b/docs/extras/ecosystem/integrations/makersuite.mdx @@ -89,6 +89,7 @@ The most typical way to use the hub consists of two parts: ```typescript // Create the hub class +import { MakerSuiteHub } from "langchain/experimental/hubs/makersuite/googlemakersuite"; const hub = new MakerSuiteHub(); // Pull the prompt, get the chain, and invoke it with the template values diff --git a/langchain/scripts/create-entrypoints.js b/langchain/scripts/create-entrypoints.js index c5efa413791d..008e5b209d19 100644 --- a/langchain/scripts/create-entrypoints.js +++ b/langchain/scripts/create-entrypoints.js @@ -247,6 +247,8 @@ const entrypoints = { "experimental/chat_models/bittensor", "experimental/llms/bittensor": "experimental/llms/bittensor", + "experimental/hubs/makersuite/googlemakersuite": + "experimental/hubs/makersuite/googlemakersuite", // evaluation evaluation: "evaluation/index", }; From 270ab05031a89becc174c43b5f509d21d05e1e9c Mon Sep 17 00:00:00 2001 From: afirstenberg Date: Wed, 4 Oct 2023 22:29:25 -0400 Subject: [PATCH 17/18] Fix typos --- docs/extras/ecosystem/integrations/makersuite.mdx | 2 +- langchain/scripts/create-entrypoints.js | 4 ++-- .../src/experimental/hubs/makersuite/googlemakersuitehub.ts | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/extras/ecosystem/integrations/makersuite.mdx b/docs/extras/ecosystem/integrations/makersuite.mdx index 8a1497427979..a9d71fc634ce 100644 --- a/docs/extras/ecosystem/integrations/makersuite.mdx +++ b/docs/extras/ecosystem/integrations/makersuite.mdx @@ -89,7 +89,7 @@ The most typical way to use the hub consists of two parts: ```typescript // Create the hub class -import { MakerSuiteHub } from "langchain/experimental/hubs/makersuite/googlemakersuite"; +import { MakerSuiteHub } from "langchain/experimental/hubs/makersuite/googlemakersuitehub"; const hub = new MakerSuiteHub(); // Pull the prompt, get the chain, and invoke it with the template values diff --git a/langchain/scripts/create-entrypoints.js b/langchain/scripts/create-entrypoints.js index 008e5b209d19..13ccd2e0faf0 100644 --- a/langchain/scripts/create-entrypoints.js +++ b/langchain/scripts/create-entrypoints.js @@ -247,8 +247,8 @@ const entrypoints = { "experimental/chat_models/bittensor", "experimental/llms/bittensor": "experimental/llms/bittensor", - "experimental/hubs/makersuite/googlemakersuite": - "experimental/hubs/makersuite/googlemakersuite", + "experimental/hubs/makersuite/googlemakersuitehub": + "experimental/hubs/makersuite/googlemakersuitehub", // evaluation evaluation: "evaluation/index", }; diff --git a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts index 6b83f335fbce..d6acb6122b3e 100644 --- a/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts +++ b/langchain/src/experimental/hubs/makersuite/googlemakersuitehub.ts @@ -25,7 +25,6 @@ import { GoogleConnection } from "../../../util/googlevertexai-connection.js"; * after the cacheTimeout specified in the configuration. */ export interface MakerSuiteHubConfig { - /** * How long, in milliseconds, before a prompt is assumed stale and should * be refreshed from the copy in Google Drive. From de099015184c63d896f57c0b0297bb8978136c6c Mon Sep 17 00:00:00 2001 From: jacoblee93 Date: Thu, 5 Oct 2023 15:43:04 -0700 Subject: [PATCH 18/18] Fix environment tests --- docs/extras/ecosystem/integrations/makersuite.mdx | 7 +++---- langchain/scripts/check-tree-shaking.js | 1 + langchain/scripts/create-entrypoints.js | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/extras/ecosystem/integrations/makersuite.mdx b/docs/extras/ecosystem/integrations/makersuite.mdx index a9d71fc634ce..de10951c2958 100644 --- a/docs/extras/ecosystem/integrations/makersuite.mdx +++ b/docs/extras/ecosystem/integrations/makersuite.mdx @@ -1,4 +1,3 @@ - # Google MakerSuite Google's [MakerSuite](https://makersuite.google.com/) is a web-based playground @@ -7,7 +6,7 @@ Google PaLM API and model. These prompts include the text, which may include "test input" that act as template parameters, and parameters for the model including the model name, temperature, etc. -LangChainJS provides the `MakerSuiteHub` class which lets you pull this prompt +LangChain.js provides the `MakerSuiteHub` class which lets you pull this prompt from Google Drive, where they are saved. Once pulled, you can convert the prompt into a LangChain Template, an LLM Model, or a chain that combines the two. This hub class has a simple time-based in-memory cache of prompts, so it is not always @@ -20,8 +19,8 @@ other developers. ## Setup You do not need any additional packages beyond those that are required for -either the Google PaLM [text](../../modules/model_io/models/llms/integrations/google_palm) -or [chat](../../modules/model_io/models/chat/integrations/google_palm) model: +either the Google PaLM [text](/docs/modules/model_io/models/llms/integrations/google_palm) +or [chat](/docs/modules/model_io/models/chat/integrations/google_palm) model: ```bash npm2yarn npm install google-auth-library @google-ai/generativelanguage diff --git a/langchain/scripts/check-tree-shaking.js b/langchain/scripts/check-tree-shaking.js index b4ef008ba42f..c6a13196bea0 100644 --- a/langchain/scripts/check-tree-shaking.js +++ b/langchain/scripts/check-tree-shaking.js @@ -36,6 +36,7 @@ export function listExternals() { "firebase-admin/app", "firebase-admin/firestore", "web-auth-library/google", + "@google-ai/generativelanguage/build/protos/protos.js", ]; } diff --git a/langchain/scripts/create-entrypoints.js b/langchain/scripts/create-entrypoints.js index a51d40f5b4e6..6e17fda20d6d 100644 --- a/langchain/scripts/create-entrypoints.js +++ b/langchain/scripts/create-entrypoints.js @@ -414,6 +414,7 @@ const requiresOptionalDependency = [ "experimental/multimodal_embeddings/googlevertexai", "experimental/chat_models/anthropic_functions", "experimental/llms/bittensor", + "experimental/hubs/makersuite/googlemakersuitehub", ]; // List of test-exports-* packages which we use to test that the exports field