3.2.0 (#412)
* 378: fix hook env (#402) * fix(prepare-commit-msg-hook): update error handling to provide clearer instructions for setting API keys and improve user guidance * Fix: a bug that causes an error when pushing without setting git remote (#396) * refactoring v2 (#408) * 3.2.0 * update deploy commands --------- Co-authored-by: Takanori Matsumoto <matscube@gmail.com>
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
import path from 'path';
|
||||
import 'cli-testing-library/extend-expect';
|
||||
import { exec } from 'child_process';
|
||||
import { prepareTempDir } from './utils';
|
||||
import { promisify } from 'util';
|
||||
import { render } from 'cli-testing-library';
|
||||
import { resolve } from 'path';
|
||||
import { rm } from 'fs';
|
||||
const fsExec = promisify(exec);
|
||||
const fsRemove = promisify(rm);
|
||||
|
||||
/**
|
||||
* git remote -v
|
||||
*
|
||||
* [no remotes]
|
||||
*/
|
||||
const prepareNoRemoteGitRepository = async (): Promise<{
|
||||
gitDir: string;
|
||||
cleanup: () => Promise<void>;
|
||||
}> => {
|
||||
const tempDir = await prepareTempDir();
|
||||
await fsExec('git init test', { cwd: tempDir });
|
||||
const gitDir = path.resolve(tempDir, 'test');
|
||||
|
||||
const cleanup = async () => {
|
||||
return fsRemove(tempDir, { recursive: true });
|
||||
};
|
||||
return {
|
||||
gitDir,
|
||||
cleanup
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* git remote -v
|
||||
*
|
||||
* origin /tmp/remote.git (fetch)
|
||||
* origin /tmp/remote.git (push)
|
||||
*/
|
||||
const prepareOneRemoteGitRepository = async (): Promise<{
|
||||
gitDir: string;
|
||||
cleanup: () => Promise<void>;
|
||||
}> => {
|
||||
const tempDir = await prepareTempDir();
|
||||
await fsExec('git init --bare remote.git', { cwd: tempDir });
|
||||
await fsExec('git clone remote.git test', { cwd: tempDir });
|
||||
const gitDir = path.resolve(tempDir, 'test');
|
||||
|
||||
const cleanup = async () => {
|
||||
return fsRemove(tempDir, { recursive: true });
|
||||
};
|
||||
return {
|
||||
gitDir,
|
||||
cleanup
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* git remote -v
|
||||
*
|
||||
* origin /tmp/remote.git (fetch)
|
||||
* origin /tmp/remote.git (push)
|
||||
* other ../remote2.git (fetch)
|
||||
* other ../remote2.git (push)
|
||||
*/
|
||||
const prepareTwoRemotesGitRepository = async (): Promise<{
|
||||
gitDir: string;
|
||||
cleanup: () => Promise<void>;
|
||||
}> => {
|
||||
const tempDir = await prepareTempDir();
|
||||
await fsExec('git init --bare remote.git', { cwd: tempDir });
|
||||
await fsExec('git init --bare other.git', { cwd: tempDir });
|
||||
await fsExec('git clone remote.git test', { cwd: tempDir });
|
||||
const gitDir = path.resolve(tempDir, 'test');
|
||||
await fsExec('git remote add other ../other.git', { cwd: gitDir });
|
||||
|
||||
const cleanup = async () => {
|
||||
return fsRemove(tempDir, { recursive: true });
|
||||
};
|
||||
return {
|
||||
gitDir,
|
||||
cleanup
|
||||
};
|
||||
};
|
||||
|
||||
describe('cli flow to push git branch', () => {
|
||||
it('do nothing when OCO_GITPUSH is set to false', async () => {
|
||||
const { gitDir, cleanup } = await prepareNoRemoteGitRepository();
|
||||
|
||||
await render('echo', [`'console.log("Hello World");' > index.ts`], {
|
||||
cwd: gitDir
|
||||
});
|
||||
await render('git', ['add index.ts'], { cwd: gitDir });
|
||||
|
||||
const { queryByText, findByText, userEvent } = await render(
|
||||
`OCO_AI_PROVIDER='test' OCO_GITPUSH='false' node`,
|
||||
[resolve('./out/cli.cjs')],
|
||||
{ cwd: gitDir }
|
||||
);
|
||||
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(
|
||||
await queryByText('Choose a remote to push to')
|
||||
).not.toBeInTheConsole();
|
||||
expect(
|
||||
await queryByText('Do you want to run `git push`?')
|
||||
).not.toBeInTheConsole();
|
||||
expect(
|
||||
await queryByText('Successfully pushed all commits to origin')
|
||||
).not.toBeInTheConsole();
|
||||
expect(
|
||||
await queryByText('Command failed with exit code 1')
|
||||
).not.toBeInTheConsole();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it('push and cause error when there is no remote', async () => {
|
||||
const { gitDir, cleanup } = await prepareNoRemoteGitRepository();
|
||||
|
||||
await render('echo', [`'console.log("Hello World");' > index.ts`], {
|
||||
cwd: gitDir
|
||||
});
|
||||
await render('git', ['add index.ts'], { cwd: gitDir });
|
||||
|
||||
const { queryByText, findByText, userEvent } = await render(
|
||||
`OCO_AI_PROVIDER='test' node`,
|
||||
[resolve('./out/cli.cjs')],
|
||||
{ cwd: gitDir }
|
||||
);
|
||||
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(
|
||||
await queryByText('Choose a remote to push to')
|
||||
).not.toBeInTheConsole();
|
||||
expect(
|
||||
await queryByText('Do you want to run `git push`?')
|
||||
).not.toBeInTheConsole();
|
||||
expect(
|
||||
await queryByText('Successfully pushed all commits to origin')
|
||||
).not.toBeInTheConsole();
|
||||
|
||||
expect(
|
||||
await findByText('Command failed with exit code 1')
|
||||
).toBeInTheConsole();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it('push when one remote is set', async () => {
|
||||
const { gitDir, cleanup } = await prepareOneRemoteGitRepository();
|
||||
|
||||
await render('echo', [`'console.log("Hello World");' > index.ts`], {
|
||||
cwd: gitDir
|
||||
});
|
||||
await render('git', ['add index.ts'], { cwd: gitDir });
|
||||
|
||||
const { findByText, userEvent } = await render(
|
||||
`OCO_AI_PROVIDER='test' node`,
|
||||
[resolve('./out/cli.cjs')],
|
||||
{ cwd: gitDir }
|
||||
);
|
||||
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(
|
||||
await findByText('Do you want to run `git push`?')
|
||||
).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(
|
||||
await findByText('Successfully pushed all commits to origin')
|
||||
).toBeInTheConsole();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it('push when two remotes are set', async () => {
|
||||
const { gitDir, cleanup } = await prepareTwoRemotesGitRepository();
|
||||
|
||||
await render('echo', [`'console.log("Hello World");' > index.ts`], {
|
||||
cwd: gitDir
|
||||
});
|
||||
await render('git', ['add index.ts'], { cwd: gitDir });
|
||||
|
||||
const { findByText, userEvent } = await render(
|
||||
`OCO_AI_PROVIDER='test' node`,
|
||||
[resolve('./out/cli.cjs')],
|
||||
{ cwd: gitDir }
|
||||
);
|
||||
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(await findByText('Choose a remote to push to')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(
|
||||
await findByText('Successfully pushed all commits to origin')
|
||||
).toBeInTheConsole();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
@@ -17,7 +17,7 @@ it('cli flow to generate commit message for 1 new file (staged)', async () => {
|
||||
expect(await findByText('Confirm the commit message?')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(await findByText('Choose a remote to push to')).toBeInTheConsole();
|
||||
expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole();
|
||||
@@ -46,7 +46,7 @@ it('cli flow to generate commit message for 1 changed file (not staged)', async
|
||||
|
||||
expect(await findByText('Successfully committed')).toBeInTheConsole();
|
||||
|
||||
expect(await findByText('Choose a remote to push to')).toBeInTheConsole();
|
||||
expect(await findByText('Do you want to run `git push`?')).toBeInTheConsole();
|
||||
userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(await findByText('Successfully pushed all commits to origin')).toBeInTheConsole();
|
||||
|
||||
@@ -209,7 +209,7 @@ describe('cli flow to generate commit message using @commitlint prompt-module',
|
||||
oco.userEvent.keyboard('[Enter]');
|
||||
|
||||
expect(
|
||||
await oco.findByText('Choose a remote to push to')
|
||||
await oco.findByText('Do you want to run `git push`?')
|
||||
).toBeInTheConsole();
|
||||
oco.userEvent.keyboard('[Enter]');
|
||||
|
||||
|
||||
+5
-1
@@ -15,7 +15,7 @@ export const prepareEnvironment = async (): Promise<{
|
||||
gitDir: string;
|
||||
cleanup: () => Promise<void>;
|
||||
}> => {
|
||||
const tempDir = await fsMakeTempDir(path.join(tmpdir(), 'opencommit-test-'));
|
||||
const tempDir = await prepareTempDir();
|
||||
// Create a remote git repository int the temp directory. This is necessary to execute the `git push` command
|
||||
await fsExec('git init --bare remote.git', { cwd: tempDir });
|
||||
await fsExec('git clone remote.git test', { cwd: tempDir });
|
||||
@@ -30,4 +30,8 @@ export const prepareEnvironment = async (): Promise<{
|
||||
}
|
||||
}
|
||||
|
||||
export const prepareTempDir = async(): Promise<string> => {
|
||||
return await fsMakeTempDir(path.join(tmpdir(), 'opencommit-test-'));
|
||||
}
|
||||
|
||||
export const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
+42
-34
@@ -1,5 +1,6 @@
|
||||
import { existsSync, readFileSync, rmSync } from 'fs';
|
||||
import {
|
||||
CONFIG_KEYS,
|
||||
DEFAULT_CONFIG,
|
||||
getConfig,
|
||||
setConfig
|
||||
@@ -50,14 +51,13 @@ describe('config', () => {
|
||||
describe('getConfig', () => {
|
||||
it('should prioritize local .env over global .opencommit config', async () => {
|
||||
globalConfigFile = await generateConfig('.opencommit', {
|
||||
OCO_OPENAI_API_KEY: 'global-key',
|
||||
OCO_API_KEY: 'global-key',
|
||||
OCO_MODEL: 'gpt-3.5-turbo',
|
||||
OCO_LANGUAGE: 'en'
|
||||
});
|
||||
|
||||
envConfigFile = await generateConfig('.env', {
|
||||
OCO_OPENAI_API_KEY: 'local-key',
|
||||
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key',
|
||||
OCO_API_KEY: 'local-key',
|
||||
OCO_LANGUAGE: 'fr'
|
||||
});
|
||||
|
||||
@@ -67,22 +67,21 @@ describe('config', () => {
|
||||
});
|
||||
|
||||
expect(config).not.toEqual(null);
|
||||
expect(config.OCO_OPENAI_API_KEY).toEqual('local-key');
|
||||
expect(config.OCO_API_KEY).toEqual('local-key');
|
||||
expect(config.OCO_MODEL).toEqual('gpt-3.5-turbo');
|
||||
expect(config.OCO_LANGUAGE).toEqual('fr');
|
||||
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key');
|
||||
});
|
||||
|
||||
it('should fallback to global config when local config is not set', async () => {
|
||||
globalConfigFile = await generateConfig('.opencommit', {
|
||||
OCO_OPENAI_API_KEY: 'global-key',
|
||||
OCO_API_KEY: 'global-key',
|
||||
OCO_MODEL: 'gpt-4',
|
||||
OCO_LANGUAGE: 'de',
|
||||
OCO_DESCRIPTION: 'true'
|
||||
});
|
||||
|
||||
envConfigFile = await generateConfig('.env', {
|
||||
OCO_ANTHROPIC_API_KEY: 'local-anthropic-key'
|
||||
OCO_API_URL: 'local-api-url'
|
||||
});
|
||||
|
||||
const config = getConfig({
|
||||
@@ -91,8 +90,8 @@ describe('config', () => {
|
||||
});
|
||||
|
||||
expect(config).not.toEqual(null);
|
||||
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key');
|
||||
expect(config.OCO_ANTHROPIC_API_KEY).toEqual('local-anthropic-key');
|
||||
expect(config.OCO_API_KEY).toEqual('global-key');
|
||||
expect(config.OCO_API_URL).toEqual('local-api-url');
|
||||
expect(config.OCO_MODEL).toEqual('gpt-4');
|
||||
expect(config.OCO_LANGUAGE).toEqual('de');
|
||||
expect(config.OCO_DESCRIPTION).toEqual(true);
|
||||
@@ -124,7 +123,7 @@ describe('config', () => {
|
||||
|
||||
it('should handle empty local config correctly', async () => {
|
||||
globalConfigFile = await generateConfig('.opencommit', {
|
||||
OCO_OPENAI_API_KEY: 'global-key',
|
||||
OCO_API_KEY: 'global-key',
|
||||
OCO_MODEL: 'gpt-4',
|
||||
OCO_LANGUAGE: 'es'
|
||||
});
|
||||
@@ -137,20 +136,20 @@ describe('config', () => {
|
||||
});
|
||||
|
||||
expect(config).not.toEqual(null);
|
||||
expect(config.OCO_OPENAI_API_KEY).toEqual('global-key');
|
||||
expect(config.OCO_API_KEY).toEqual('global-key');
|
||||
expect(config.OCO_MODEL).toEqual('gpt-4');
|
||||
expect(config.OCO_LANGUAGE).toEqual('es');
|
||||
});
|
||||
|
||||
it('should override global config with null values in local .env', async () => {
|
||||
globalConfigFile = await generateConfig('.opencommit', {
|
||||
OCO_OPENAI_API_KEY: 'global-key',
|
||||
OCO_API_KEY: 'global-key',
|
||||
OCO_MODEL: 'gpt-4',
|
||||
OCO_LANGUAGE: 'es'
|
||||
});
|
||||
|
||||
envConfigFile = await generateConfig('.env', {
|
||||
OCO_OPENAI_API_KEY: 'null'
|
||||
OCO_API_KEY: 'null'
|
||||
});
|
||||
|
||||
const config = getConfig({
|
||||
@@ -159,7 +158,7 @@ describe('config', () => {
|
||||
});
|
||||
|
||||
expect(config).not.toEqual(null);
|
||||
expect(config.OCO_OPENAI_API_KEY).toEqual(null);
|
||||
expect(config.OCO_API_KEY).toEqual(null);
|
||||
});
|
||||
|
||||
it('should handle empty global config', async () => {
|
||||
@@ -172,7 +171,7 @@ describe('config', () => {
|
||||
});
|
||||
|
||||
expect(config).not.toEqual(null);
|
||||
expect(config.OCO_OPENAI_API_KEY).toEqual(undefined);
|
||||
expect(config.OCO_API_KEY).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -188,12 +187,12 @@ describe('config', () => {
|
||||
expect(isGlobalConfigFileExist).toBe(false);
|
||||
|
||||
await setConfig(
|
||||
[['OCO_OPENAI_API_KEY', 'persisted-key_1']],
|
||||
[[CONFIG_KEYS.OCO_API_KEY, 'persisted-key_1']],
|
||||
globalConfigFile.filePath
|
||||
);
|
||||
|
||||
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
|
||||
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key_1');
|
||||
expect(fileContent).toContain('OCO_API_KEY=persisted-key_1');
|
||||
Object.entries(DEFAULT_CONFIG).forEach(([key, value]) => {
|
||||
expect(fileContent).toContain(`${key}=${value}`);
|
||||
});
|
||||
@@ -203,42 +202,48 @@ describe('config', () => {
|
||||
globalConfigFile = await generateConfig('.opencommit', {});
|
||||
await setConfig(
|
||||
[
|
||||
['OCO_OPENAI_API_KEY', 'new-key'],
|
||||
['OCO_MODEL', 'gpt-4']
|
||||
[CONFIG_KEYS.OCO_API_KEY, 'new-key'],
|
||||
[CONFIG_KEYS.OCO_MODEL, 'gpt-4']
|
||||
],
|
||||
globalConfigFile.filePath
|
||||
);
|
||||
|
||||
const config = getConfig({ globalPath: globalConfigFile.filePath });
|
||||
expect(config.OCO_OPENAI_API_KEY).toEqual('new-key');
|
||||
const config = getConfig({
|
||||
globalPath: globalConfigFile.filePath
|
||||
});
|
||||
expect(config.OCO_API_KEY).toEqual('new-key');
|
||||
expect(config.OCO_MODEL).toEqual('gpt-4');
|
||||
});
|
||||
|
||||
it('should update existing config values', async () => {
|
||||
globalConfigFile = await generateConfig('.opencommit', {
|
||||
OCO_OPENAI_API_KEY: 'initial-key'
|
||||
OCO_API_KEY: 'initial-key'
|
||||
});
|
||||
await setConfig(
|
||||
[['OCO_OPENAI_API_KEY', 'updated-key']],
|
||||
[[CONFIG_KEYS.OCO_API_KEY, 'updated-key']],
|
||||
globalConfigFile.filePath
|
||||
);
|
||||
|
||||
const config = getConfig({ globalPath: globalConfigFile.filePath });
|
||||
expect(config.OCO_OPENAI_API_KEY).toEqual('updated-key');
|
||||
const config = getConfig({
|
||||
globalPath: globalConfigFile.filePath
|
||||
});
|
||||
expect(config.OCO_API_KEY).toEqual('updated-key');
|
||||
});
|
||||
|
||||
it('should handle boolean and numeric values correctly', async () => {
|
||||
globalConfigFile = await generateConfig('.opencommit', {});
|
||||
await setConfig(
|
||||
[
|
||||
['OCO_TOKENS_MAX_INPUT', '8192'],
|
||||
['OCO_DESCRIPTION', 'true'],
|
||||
['OCO_ONE_LINE_COMMIT', 'false']
|
||||
[CONFIG_KEYS.OCO_TOKENS_MAX_INPUT, '8192'],
|
||||
[CONFIG_KEYS.OCO_DESCRIPTION, 'true'],
|
||||
[CONFIG_KEYS.OCO_ONE_LINE_COMMIT, 'false']
|
||||
],
|
||||
globalConfigFile.filePath
|
||||
);
|
||||
|
||||
const config = getConfig({ globalPath: globalConfigFile.filePath });
|
||||
const config = getConfig({
|
||||
globalPath: globalConfigFile.filePath
|
||||
});
|
||||
expect(config.OCO_TOKENS_MAX_INPUT).toEqual(8192);
|
||||
expect(config.OCO_DESCRIPTION).toEqual(true);
|
||||
expect(config.OCO_ONE_LINE_COMMIT).toEqual(false);
|
||||
@@ -266,12 +271,12 @@ describe('config', () => {
|
||||
expect(isGlobalConfigFileExist).toBe(false);
|
||||
|
||||
await setConfig(
|
||||
[['OCO_OPENAI_API_KEY', 'persisted-key']],
|
||||
[[CONFIG_KEYS.OCO_API_KEY, 'persisted-key']],
|
||||
globalConfigFile.filePath
|
||||
);
|
||||
|
||||
const fileContent = readFileSync(globalConfigFile.filePath, 'utf8');
|
||||
expect(fileContent).toContain('OCO_OPENAI_API_KEY=persisted-key');
|
||||
expect(fileContent).toContain('OCO_API_KEY=persisted-key');
|
||||
});
|
||||
|
||||
it('should set multiple configs in a row and keep the changes', async () => {
|
||||
@@ -279,14 +284,17 @@ describe('config', () => {
|
||||
expect(isGlobalConfigFileExist).toBe(false);
|
||||
|
||||
await setConfig(
|
||||
[['OCO_OPENAI_API_KEY', 'persisted-key']],
|
||||
[[CONFIG_KEYS.OCO_API_KEY, 'persisted-key']],
|
||||
globalConfigFile.filePath
|
||||
);
|
||||
|
||||
const fileContent1 = readFileSync(globalConfigFile.filePath, 'utf8');
|
||||
expect(fileContent1).toContain('OCO_OPENAI_API_KEY=persisted-key');
|
||||
expect(fileContent1).toContain('OCO_API_KEY=persisted-key');
|
||||
|
||||
await setConfig([['OCO_MODEL', 'gpt-4']], globalConfigFile.filePath);
|
||||
await setConfig(
|
||||
[[CONFIG_KEYS.OCO_MODEL, 'gpt-4']],
|
||||
globalConfigFile.filePath
|
||||
);
|
||||
|
||||
const fileContent2 = readFileSync(globalConfigFile.filePath, 'utf8');
|
||||
expect(fileContent2).toContain('OCO_MODEL=gpt-4');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Gemini } from '../../src/engine/gemini';
|
||||
import { GeminiEngine } from '../../src/engine/gemini';
|
||||
|
||||
import { GenerativeModel, GoogleGenerativeAI } from '@google/generative-ai';
|
||||
import {
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { OpenAI } from 'openai';
|
||||
|
||||
describe('Gemini', () => {
|
||||
let gemini: Gemini;
|
||||
let gemini: GeminiEngine;
|
||||
let mockConfig: ConfigType;
|
||||
let mockGoogleGenerativeAi: GoogleGenerativeAI;
|
||||
let mockGenerativeModel: GenerativeModel;
|
||||
@@ -20,8 +20,8 @@ describe('Gemini', () => {
|
||||
const mockGemini = () => {
|
||||
mockConfig = getConfig() as ConfigType;
|
||||
|
||||
gemini = new Gemini({
|
||||
apiKey: mockConfig.OCO_GEMINI_API_KEY,
|
||||
gemini = new GeminiEngine({
|
||||
apiKey: mockConfig.OCO_API_KEY,
|
||||
model: mockConfig.OCO_MODEL
|
||||
});
|
||||
};
|
||||
@@ -45,12 +45,10 @@ describe('Gemini', () => {
|
||||
mockConfig = getConfig() as ConfigType;
|
||||
|
||||
mockConfig.OCO_AI_PROVIDER = OCO_AI_PROVIDER_ENUM.GEMINI;
|
||||
mockConfig.OCO_GEMINI_API_KEY = 'mock-api-key';
|
||||
mockConfig.OCO_API_KEY = 'mock-api-key';
|
||||
mockConfig.OCO_MODEL = 'gemini-1.5-flash';
|
||||
|
||||
mockGoogleGenerativeAi = new GoogleGenerativeAI(
|
||||
mockConfig.OCO_GEMINI_API_KEY
|
||||
);
|
||||
mockGoogleGenerativeAi = new GoogleGenerativeAI(mockConfig.OCO_API_KEY);
|
||||
mockGenerativeModel = mockGoogleGenerativeAi.getGenerativeModel({
|
||||
model: mockConfig.OCO_MODEL
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user