From 832a18828cf84b931495c01abe3a56bae0f6190a Mon Sep 17 00:00:00 2001 From: Sajal Jain Date: Mon, 12 Aug 2024 08:36:57 -0700 Subject: [PATCH 1/3] feat: add support for google gemini --- packages/flowtest-electron/package.json | 2 + .../flowtest-electron/src/ai/flowtestai.js | 6 + .../flowtest-electron/src/ai/models/gemini.js | 137 ++++++++++++++++++ .../tests/utils/flowtest-ai.test.js | 19 +++ pnpm-lock.yaml | 111 ++++++++++++-- 5 files changed, 263 insertions(+), 12 deletions(-) create mode 100644 packages/flowtest-electron/src/ai/models/gemini.js diff --git a/packages/flowtest-electron/package.json b/packages/flowtest-electron/package.json index c9ffc8b..c7c4949 100644 --- a/packages/flowtest-electron/package.json +++ b/packages/flowtest-electron/package.json @@ -33,7 +33,9 @@ "@aws-sdk/client-bedrock-runtime": "^3.583.0", "@aws-sdk/credential-provider-node": "^3.583.0", "@aws-sdk/types": "^3.577.0", + "@google/generative-ai": "^0.16.0", "@langchain/community": "^0.2.19", + "@langchain/google-genai": "^0.0.25", "@smithy/eventstream-codec": "^3.0.0", "@smithy/protocol-http": "^4.0.0", "@smithy/signature-v4": "^3.0.0", diff --git a/packages/flowtest-electron/src/ai/flowtestai.js b/packages/flowtest-electron/src/ai/flowtestai.js index 3f88bf4..778733f 100644 --- a/packages/flowtest-electron/src/ai/flowtestai.js +++ b/packages/flowtest-electron/src/ai/flowtestai.js @@ -1,4 +1,5 @@ const BedrockClaudeGenerate = require('./models/bedrock_claude'); +const GeminiGenerate = require('./models/gemini'); const OpenAIGenerate = require('./models/openai'); class FlowtestAI { @@ -13,6 +14,11 @@ class FlowtestAI { const bedrock_claude = new BedrockClaudeGenerate(model.apiKey); const functions = await bedrock_claude.filter_functions(available_functions, user_instruction); return await bedrock_claude.process_user_instruction(functions, user_instruction); + } else if (model.name === 'GEMINI') { + const available_functions = await this.get_available_functions(collection); + const gemini = new GeminiGenerate(model.apiKey); + const functions = await gemini.filter_functions(available_functions, user_instruction); + return await gemini.process_user_instruction(functions, user_instruction); } else { throw Error(`Model ${model.name} not supported`); } diff --git a/packages/flowtest-electron/src/ai/models/gemini.js b/packages/flowtest-electron/src/ai/models/gemini.js new file mode 100644 index 0000000..065d377 --- /dev/null +++ b/packages/flowtest-electron/src/ai/models/gemini.js @@ -0,0 +1,137 @@ +const { GoogleGenerativeAI } = require('@google/generative-ai'); +const { GoogleGenerativeAIEmbeddings } = require('@langchain/google-genai'); +const { TaskType } = require('@google/generative-ai'); +const { MemoryVectorStore } = require('langchain/vectorstores/memory'); + +class GeminiGenerate { + constructor(apiKey) { + this.genAI = new GoogleGenerativeAI(apiKey); + + this.embeddings = new GoogleGenerativeAIEmbeddings({ + apiKey, + model: 'text-embedding-004', // 768 dimensions + taskType: TaskType.RETRIEVAL_DOCUMENT, + title: 'Document title', + }); + } + + async filter_functions(functions, instruction) { + const documents = functions.map((f) => { + const { parameters, ...fDescription } = f.function; + return JSON.stringify(fDescription); + }); + + const vectorStore = await MemoryVectorStore.fromTexts(documents, [], this.embeddings); + + // 128 (max no of functions accepted by openAI function calling) + const retrievedDocuments = await vectorStore.similaritySearch(instruction, 10); + var selectedFunctions = []; + retrievedDocuments.forEach((document) => { + const pDocument = JSON.parse(document.pageContent); + const findF = functions.find( + (f) => f.function.name === pDocument.name && f.function.description === pDocument.description, + ); + if (findF) { + selectedFunctions = selectedFunctions.concat(findF); + } + }); + + return selectedFunctions; + } + + async process_user_instruction(functions, instruction) { + //console.log(functions.map((f) => f.function.name)); + // Define the function call format + const fn = `{"name": "function_name"}`; + + // Prepare the function string for the system prompt + const fnStr = functions.map((f) => JSON.stringify(f)).join('\n'); + + // Define the system prompt + const systemPrompt = ` + You are a helpful assistant with access to the following functions: + + ${fnStr} + + To use these functions respond with, only output function names, ignore arguments needed by those functions: + + + ${fn} + ${fn} + ... + + + Edge cases you must handle: + - If there are multiple functions that can fullfill user request, list them all. + - If there are no functions that match the user request, you will respond politely that you cannot help. + - If the user has not provided all information to execute the function call, choose the best possible set of values. Only, respond with the information requested and nothing else. + - If asked something that cannot be determined with the user's request details, respond that it is not possible to fulfill the request and explain why. + `; + + const model = this.genAI.getGenerativeModel({ + model: 'gemini-1.5-pro-latest', + systemInstruction: { + role: 'system', + parts: [{ text: systemPrompt }], + }, + }); + + // Prepare the messages for the language model + + const request = { + contents: [{ role: 'user', parts: [{ text: instruction }] }], + }; + + // Invoke the language model and get the completion + const completion = await model.generateContent(request); + + const content = completion.response.candidates[0].content.parts[0].text.trim(); + + // Extract function calls from the completion + const extractedFunctions = this.extractFunctionCalls(content); + + return extractedFunctions; + } + + extractFunctionCalls(completion) { + let content = typeof completion === 'string' ? completion : completion.content; + + // Multiple functions lookup + const mfnPattern = /(.*?)<\/multiplefunctions>/s; + const mfnMatch = content.match(mfnPattern); + + // Single function lookup + const singlePattern = /(.*?)<\/functioncall>/s; + const singleMatch = content.match(singlePattern); + + let functions = []; + + if (!mfnMatch && !singleMatch) { + // No function calls found + return null; + } else if (mfnMatch) { + // Multiple function calls found + const multiplefn = mfnMatch[1]; + const fnMatches = [...multiplefn.matchAll(/(.*?)<\/functioncall>/gs)]; + for (let fnMatch of fnMatches) { + const fnText = fnMatch[1].replace(/\\/g, ''); + try { + functions.push(JSON.parse(fnText)); + } catch { + // Ignore invalid JSON + } + } + } else { + // Single function call found + const fnText = singleMatch[1].replace(/\\/g, ''); + try { + functions.push(JSON.parse(fnText)); + } catch { + // Ignore invalid JSON + } + } + return functions; + } +} + +module.exports = GeminiGenerate; diff --git a/packages/flowtest-electron/tests/utils/flowtest-ai.test.js b/packages/flowtest-electron/tests/utils/flowtest-ai.test.js index 75f133d..953e9ec 100644 --- a/packages/flowtest-electron/tests/utils/flowtest-ai.test.js +++ b/packages/flowtest-electron/tests/utils/flowtest-ai.test.js @@ -44,4 +44,23 @@ describe('generate', () => { const nodeNames = result.map((node) => node.name); expect(nodeNames).toEqual(['addPet', 'getPetById', 'findPetsByStatus']); }, 60000); + + it('should generate functions using gemini', async () => { + const f = new FlowtestAI(); + const USER_INSTRUCTION = + 'Add a new pet to the store. \ + Then get the created pet. \ + Then get pet with status as available.'; + //const testYaml = fs.readFileSync('tests/test.yaml', { encoding: 'utf8', flag: 'r' }); + let api = await SwaggerParser.validate('tests/test.yaml'); + console.log('API name: %s, Version: %s', api.info.title, api.info.version); + const resolvedSpec = (await JsonRefs.resolveRefs(api)).resolved; + + let result = await f.generate(resolvedSpec, USER_INSTRUCTION, { + name: 'GEMINI', + apiKey: '', + }); + const nodeNames = result.map((node) => node.name); + expect(nodeNames).toEqual(['addPet', 'getPetById', 'findPetsByStatus']); + }, 60000); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 996ebc7..8207bf4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -243,9 +243,15 @@ importers: '@aws-sdk/types': specifier: ^3.577.0 version: 3.577.0 + '@google/generative-ai': + specifier: ^0.16.0 + version: 0.16.0 '@langchain/community': specifier: ^0.2.19 version: 0.2.19(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0) + '@langchain/google-genai': + specifier: ^0.0.25 + version: 0.0.25(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2)(zod@3.23.8) '@smithy/eventstream-codec': specifier: ^3.0.0 version: 3.0.0 @@ -1499,6 +1505,14 @@ packages: '@floating-ui/utils@0.2.2': resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==} + '@google/generative-ai@0.16.0': + resolution: {integrity: sha512-doB5ZNxS6m+jUZqaLCeYXfBZCdq6Ho0ibkq5/17xe1qAUZpCLWlvCDGtqFPqqO+yezNmvGatS0KhV22yiOT3DA==} + engines: {node: '>=18.0.0'} + + '@google/generative-ai@0.7.1': + resolution: {integrity: sha512-WTjMLLYL/xfA5BW6xAycRPiAX7FNHKAxrid/ayqC1QMam0KAK0NbMeS9Lubw80gVg5xFMLE+H7pw4wdNzTOlxw==} + engines: {node: '>=18.0.0'} + '@headlessui/react@1.7.19': resolution: {integrity: sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==} engines: {node: '>=10'} @@ -2361,6 +2375,14 @@ packages: resolution: {integrity: sha512-WnFiZ7R/ZUVeHO2IgcSL7Tu+CjApa26Iy99THJP5fax/NF8UQCc/ZRcw2Sb/RUuRPVm6ALDass0fSQE1L9YNJg==} engines: {node: '>=18'} + '@langchain/core@0.2.23': + resolution: {integrity: sha512-elPg6WpAkxWEIGC9u38F2anbzqfYYEy32lJdsd9dtChcHSFmFLlXqa+SnpO3R772gUuJmcu+Pd+fCvmRFy029w==} + engines: {node: '>=18'} + + '@langchain/google-genai@0.0.25': + resolution: {integrity: sha512-ODywsqTR+2gZrlqA5Hz0GFYwh4C9C/EvM+7MPP3F5r5AHKvrIJlWrlzRVzgsrqLT2UkYniftjIrQao4f23/Sgg==} + engines: {node: '>=18'} + '@langchain/openai@0.0.34': resolution: {integrity: sha512-M+CW4oXle5fdoz2T2SwdOef8pl3/1XmUx1vjn2mXUVM/128aO0l23FMF0SNBsAbRV6P+p/TuzjodchJbi0Ht/A==} engines: {node: '>=18'} @@ -6458,6 +6480,20 @@ packages: openai: optional: true + langsmith@0.1.41: + resolution: {integrity: sha512-8R7s/225Pxmv0ipMfd6sqmWVsfHLQivYlQZ0vx5K+ReoknummTenQlVK8gapk3kqRMnzkrouuRHMhWjMR6RgUA==} + peerDependencies: + '@langchain/core': '*' + langchain: '*' + openai: '*' + peerDependenciesMeta: + '@langchain/core': + optional: true + langchain: + optional: true + openai: + optional: true + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -8175,6 +8211,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -9386,8 +9427,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -9436,8 +9477,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sso-oidc': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -9479,11 +9520,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.583.0(@aws-sdk/client-sts@3.583.0)': + '@aws-sdk/client-sso-oidc@3.583.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -9522,7 +9563,6 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.2 transitivePeerDependencies: - - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso@3.583.0': @@ -9568,11 +9608,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.583.0': + '@aws-sdk/client-sts@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/client-sso-oidc': 3.583.0 '@aws-sdk/core': 3.582.0 '@aws-sdk/credential-provider-node': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0) '@aws-sdk/middleware-host-header': 3.577.0 @@ -9611,6 +9651,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.2 transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' - aws-crt '@aws-sdk/core@3.582.0': @@ -9644,7 +9685,7 @@ snapshots: '@aws-sdk/credential-provider-ini@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0)': dependencies: - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/credential-provider-env': 3.577.0 '@aws-sdk/credential-provider-process': 3.577.0 '@aws-sdk/credential-provider-sso': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) @@ -9701,7 +9742,7 @@ snapshots: '@aws-sdk/credential-provider-web-identity@3.577.0(@aws-sdk/client-sts@3.583.0)': dependencies: - '@aws-sdk/client-sts': 3.583.0 + '@aws-sdk/client-sts': 3.583.0(@aws-sdk/client-sso-oidc@3.583.0) '@aws-sdk/types': 3.577.0 '@smithy/property-provider': 3.0.0 '@smithy/types': 3.0.0 @@ -9746,7 +9787,7 @@ snapshots: '@aws-sdk/token-providers@3.577.0(@aws-sdk/client-sso-oidc@3.583.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.583.0(@aws-sdk/client-sts@3.583.0) + '@aws-sdk/client-sso-oidc': 3.583.0 '@aws-sdk/types': 3.577.0 '@smithy/property-provider': 3.0.0 '@smithy/shared-ini-file-loader': 3.0.0 @@ -11109,6 +11150,10 @@ snapshots: '@floating-ui/utils@0.2.2': {} + '@google/generative-ai@0.16.0': {} + + '@google/generative-ai@0.7.1': {} + '@headlessui/react@1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/react-virtual': 3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -11649,6 +11694,33 @@ snapshots: - langchain - openai + '@langchain/core@0.2.23(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2)': + dependencies: + ansi-styles: 5.2.0 + camelcase: 6.3.0 + decamelize: 1.2.0 + js-tiktoken: 1.0.12 + langsmith: 0.1.41(@langchain/core@0.2.23(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2))(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2) + mustache: 4.2.0 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 10.0.0 + zod: 3.23.8 + zod-to-json-schema: 3.23.0(zod@3.23.8) + transitivePeerDependencies: + - langchain + - openai + + '@langchain/google-genai@0.0.25(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2)(zod@3.23.8)': + dependencies: + '@google/generative-ai': 0.7.1 + '@langchain/core': 0.2.23(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2) + zod-to-json-schema: 3.23.0(zod@3.23.8) + transitivePeerDependencies: + - langchain + - openai + - zod + '@langchain/openai@0.0.34(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))': dependencies: '@langchain/core': 0.1.63(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2) @@ -16810,6 +16882,19 @@ snapshots: langchain: 0.2.3(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(openai@4.47.2)(ws@8.17.0) openai: 4.52.7 + langsmith@0.1.41(@langchain/core@0.2.23(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2))(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2): + dependencies: + '@types/uuid': 9.0.8 + commander: 10.0.1 + p-queue: 6.6.2 + p-retry: 4.6.2 + semver: 7.6.3 + uuid: 9.0.1 + optionalDependencies: + '@langchain/core': 0.2.23(langchain@0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0))(openai@4.47.2) + langchain: 0.1.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.583.0)(@aws-sdk/credential-provider-node@3.583.0(@aws-sdk/client-sso-oidc@3.583.0)(@aws-sdk/client-sts@3.583.0))(@smithy/eventstream-codec@3.0.0)(@smithy/protocol-http@4.0.0)(@smithy/signature-v4@3.0.0)(@smithy/util-utf8@3.0.0)(axios@1.7.2)(fast-xml-parser@4.2.5)(ignore@5.3.1)(jsdom@16.7.0)(lodash@4.17.21)(openai@4.47.2)(ws@8.17.0) + openai: 4.47.2 + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -18553,6 +18638,8 @@ snapshots: semver@7.6.2: {} + semver@7.6.3: {} + send@0.18.0: dependencies: debug: 2.6.9 From db1e9288e40efe917efcb0b3e3675b7eb4209002 Mon Sep 17 00:00:00 2001 From: Sajal Jain Date: Mon, 12 Aug 2024 09:26:17 -0700 Subject: [PATCH 2/3] feat: add gemini as options in the generate flow modal --- src/components/molecules/flow/flowtestai.js | 20 +++++ .../molecules/modals/GenerateFlowTestModal.js | 80 +++++++++++++++---- src/constants/Common.js | 1 + 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/components/molecules/flow/flowtestai.js b/src/components/molecules/flow/flowtestai.js index 9dfc435..4cd3a52 100644 --- a/src/components/molecules/flow/flowtestai.js +++ b/src/components/molecules/flow/flowtestai.js @@ -96,6 +96,26 @@ export const generateFlowData = async (instruction, modelName, modelKey, collect nodes: translateGeneratedNodesToOpenApiNodes(generatedNodes, collection.nodes), }; return flowData; + } else if (modelName === GENAI_MODELS.gemini) { + if (!collection.dotEnvVariables) { + await ipcRenderer.invoke('renderer:create-dotenv', collection.pathname, `GEMINI_APIKEY=${modelKey}`); + } else if ( + !Object.prototype.hasOwnProperty.call(collection.dotEnvVariables, 'GEMINI_APIKEY') || + modelKey != collection.dotEnvVariables['GEMINI_APIKEY'] + ) { + await addOrUpdateDotEnvironmentFile(collectionId, { + ...collection.dotEnvVariables, + GEMINI_APIKEY: modelKey, + }); + } + const generatedNodes = await ipcRenderer.invoke('renderer:generate-nodes-ai', instruction, collectionId, { + name: GENAI_MODELS.gemini, + apiKey: modelKey, + }); + const flowData = { + nodes: translateGeneratedNodesToOpenApiNodes(generatedNodes, collection.nodes), + }; + return flowData; } else { return Promise.reject(new Error(`model: ${modelName} not supported`)); } diff --git a/src/components/molecules/modals/GenerateFlowTestModal.js b/src/components/molecules/modals/GenerateFlowTestModal.js index 2541723..3964c07 100644 --- a/src/components/molecules/modals/GenerateFlowTestModal.js +++ b/src/components/molecules/modals/GenerateFlowTestModal.js @@ -30,7 +30,8 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI const [selectedModel, setSelectedModel] = useState(null); const [textareaValue, setTextareaValue] = useState(''); - const [modelKey, setModelKey] = useState(''); + const [openaiKey, setOpenAIKey] = useState(''); + const [geminiKey, setGeminiKey] = useState(''); const [bedrockAccessKeyId, setBedrockAccessKeyId] = useState(''); const [bedrockSecretAccessKey, setBedrockSecretAccessKey] = useState(''); @@ -42,7 +43,8 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI const [showFlowtestNameError, setShowFlowtestNameError] = useState(false); const [showCollectionSelectionError, setShowCollectionSelectionError] = useState(false); const [showSelectedModelError, setSelectedModelError] = useState(false); - const [showModelKeyError, setShowModelKeyError] = useState(false); + const [showOpenAIKeyError, setShowOpenAIKeyError] = useState(false); + const [showGeminiKeyError, setShowGeminiKeyError] = useState(false); const [showDescribeFlowError, setShowDescribeFlowError] = useState(false); const [showBedrockAccessKeyIdError, setShowBedrockAccessKeyIdError] = useState(false); const [showBedrockSecretAccessKeyError, setShowBedrockSecretAccessKeyError] = useState(false); @@ -50,7 +52,7 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI useEffect(() => { setSelectionCollection(collectionId ? collections.find((c) => c.id === collectionId) : {}); - }, [collectionId]); + }, [collectionId, collections]); const resetFields = () => { if (!collectionId) { @@ -60,10 +62,12 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI } setSelectedModel(null); setTextareaValue(''); - setModelKey(''); + setOpenAIKey(''); + setGeminiKey(''); setShowFlowtestNameError(false); setShowCollectionSelectionError(false); - setShowModelKeyError(false); + setShowOpenAIKeyError(false); + setShowGeminiKeyError(false); setShowDescribeFlowError(false); setSelectedModelError(false); setShowBedrockAccessKeyIdError(false); @@ -289,7 +293,18 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI 'OPENAI_APIKEY', ) ) { - setModelKey(selectedCollection.dotEnvVariables['OPENAI_APIKEY']); + setOpenAIKey(selectedCollection.dotEnvVariables['OPENAI_APIKEY']); + } + } + + if (GENAI_MODELS.gemini) { + if ( + Object.prototype.hasOwnProperty.call( + selectedCollection.dotEnvVariables, + 'GEMINI_APIKEY', + ) + ) { + setGeminiKey(selectedCollection.dotEnvVariables['GEMINI_APIKEY']); } } @@ -366,6 +381,35 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI )} )} + {selectedModel === GENAI_MODELS.gemini ? ( +
+
+ + setGeminiKey(e.target.value)} + /> +
+ {geminiKey.trim() === '' && showGeminiKeyError ? ( +
{`Please enter ${selectedModel} api key`}
+ ) : ( + '' + )} +
+ ) : ( + '' + )} {selectedModel === GENAI_MODELS.openai ? (
@@ -381,12 +425,12 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI className='nodrag nowheel block w-full bg-transparent p-2.5 outline-none' name='keyName' placeholder='Enter your OPENAI api key' - value={modelKey.trim()} + value={openaiKey.trim()} //readOnly='readonly' - onChange={(e) => setModelKey(e.target.value)} + onChange={(e) => setOpenAIKey(e.target.value)} />
- {modelKey.trim() === '' && showModelKeyError ? ( + {openaiKey.trim() === '' && showOpenAIKeyError ? (
{`Please enter ${selectedModel} api key`}
) : ( '' @@ -506,8 +550,13 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI return; } - if (selectedModel === GENAI_MODELS.openai && (!modelKey || modelKey.trim() === '')) { - setShowModelKeyError(true); + if (selectedModel === GENAI_MODELS.openai && (!openaiKey || openaiKey.trim() === '')) { + setShowOpenAIKeyError(true); + return; + } + + if (selectedModel === GENAI_MODELS.gemini && (!geminiKey || geminiKey.trim() === '')) { + setShowGeminiKeyError(true); return; } @@ -534,7 +583,8 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI setShowFlowtestNameError(false); setShowCollectionSelectionError(false); - setShowModelKeyError(false); + setShowOpenAIKeyError(false); + setShowGeminiKeyError(false); setShowBedrockAccessKeyIdError(false); setShowBedrockSecretAccessKeyError(false); setShowDescribeFlowError(false); @@ -542,8 +592,10 @@ const GenerateFlowTestModal = ({ closeFn = () => null, open = false, collectionI const creds = selectedModel === GENAI_MODELS.openai - ? modelKey - : { accessKeyId: bedrockAccessKeyId, secretAccessKey: bedrockSecretAccessKey }; + ? openaiKey + : selectedModel === GENAI_MODELS.gemini + ? geminiKey + : { accessKeyId: bedrockAccessKeyId, secretAccessKey: bedrockSecretAccessKey }; function gen() { setShowLoader(true); promiseWithTimeout( diff --git a/src/constants/Common.js b/src/constants/Common.js index 9b3238e..914ec6a 100644 --- a/src/constants/Common.js +++ b/src/constants/Common.js @@ -32,4 +32,5 @@ export const OBJ_TYPES = { export const GENAI_MODELS = { openai: 'OPENAI', bedrock_claude: 'BEDROCK_CLAUDE', + gemini: 'GEMINI', }; From ad0ee95a80ea62810e731bb6271d9ec1dee533e2 Mon Sep 17 00:00:00 2001 From: Sajal Jain Date: Mon, 12 Aug 2024 09:27:11 -0700 Subject: [PATCH 3/3] chore: add changeset --- .changeset/early-colts-approve.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/early-colts-approve.md diff --git a/.changeset/early-colts-approve.md b/.changeset/early-colts-approve.md new file mode 100644 index 0000000..0669c6d --- /dev/null +++ b/.changeset/early-colts-approve.md @@ -0,0 +1,5 @@ +--- +'flowtestai-app': minor +--- + +Add support for google geminin function calling in generating flow