Skip to content

genkit: support for upcoming unified plugin #8957

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- genkit: support for new unified plugin during init
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- genkit: support for new unified plugin during init
- Added support for new unified genkit plugin during `init genkit`

169 changes: 112 additions & 57 deletions src/init/features/genkit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
interface GenkitInfo {
genkitVersion: string;
cliVersion: string;
genaiVersion: string;
vertexVersion: string;
googleAiVersion: string;
templateVersion: string;
Expand All @@ -47,7 +48,7 @@ interface GenkitInfo {

// This is the next breaking change version past the latest template.
const UNKNOWN_VERSION_TOO_HIGH = "2.0.0";
const MIN_VERSION = "0.6.0";
const MIN_VERSION = "1.0.0-rc.1";

// This is the latest template. It is the default.
const LATEST_TEMPLATE = "1.0.0";
Expand Down Expand Up @@ -92,6 +93,7 @@ async function getGenkitInfo(): Promise<GenkitInfo> {

const genkitVersion = await getPackageVersion("genkit", "GENKIT_DEV_VERSION");
const cliVersion = await getPackageVersion("genkit-cli", "GENKIT_CLI_DEV_VERSION");
const genaiVersion = await getPackageVersion("@genkit-ai/google-genai", "GENKIT_GENAI_VERSION");
const vertexVersion = await getPackageVersion("@genkit-ai/vertexai", "GENKIT_VERTEX_VERSION");
const googleAiVersion = await getPackageVersion("@genkit-ai/googleai", "GENKIT_GOOGLEAI_VERSION");

Expand All @@ -110,11 +112,11 @@ async function getGenkitInfo(): Promise<GenkitInfo> {
if (!continueInstall) {
stopInstall = true;
}
} else if (semver.gte(genkitVersion, "1.0.0-rc.1")) {
// 1.0.0-rc.1 < 1.0.0
templateVersion = "1.0.0";
} else if (semver.gte(genkitVersion, "1.17.0") && semver.gte(genaiVersion, "0.0.2-rc.1")) {
// Unified plugin template
templateVersion = "1.17.0";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making this 1.17.0 a constant and using it throughout - as is, it seems like it would be easy to miss a spot when updating

} else if (semver.gte(genkitVersion, MIN_VERSION)) {
templateVersion = "0.9.0";
templateVersion = "1.0.0";
} else {
throw new FirebaseError(
`The requested version of Genkit (${genkitVersion}) is no ` +
Expand All @@ -127,6 +129,7 @@ async function getGenkitInfo(): Promise<GenkitInfo> {
cliVersion,
vertexVersion,
googleAiVersion,
genaiVersion,
templateVersion,
stopInstall,
};
Expand Down Expand Up @@ -241,55 +244,105 @@ export async function ensureVertexApiEnabled(options: Options): Promise<void> {
}

interface PluginInfo {
// The name of the plugin
plugin: string;
// Imported items from `name` (can be comma list).
imports: string;
// Comment for 'the model import line.
modelImportComment?: string;
// Initializer call.
init: string;
// Model name as an imported reference.
// Model definition
model?: string;
// Model name as a string reference.
modelStr?: string;
}

interface PromptOption {
interface ModelOption {
// Label for prompt option.
label: string;
// Provider (e.g. googleAI, vertexAI)
provider?: string;
// Plugin name.
plugin?: string;
// Package including version
package?: string;
}

/** Model to plugin name. */
function getModelOptions(genkitInfo: GenkitInfo): Record<ModelProvider, PromptOption> {
const modelOptions: Record<ModelProvider, PromptOption> = {
vertexai: {
label: "Google Cloud Vertex AI",
plugin: "@genkit-ai/vertexai",
package: `@genkit-ai/vertexai@${genkitInfo.vertexVersion}`,
},
googleai: {
label: "Google AI",
plugin: "@genkit-ai/googleai",
package: `@genkit-ai/googleai@${genkitInfo.googleAiVersion}`,
},
none: { label: "None", plugin: undefined, package: undefined },
};
function getModelOptions(genkitInfo: GenkitInfo): Record<ModelProvider, ModelOption> {
let modelOptions: Record<ModelProvider, ModelOption>;
if (semver.gte(genkitInfo.templateVersion, "1.17.0")) {
modelOptions = {
vertexai: {
label: "Google Cloud Vertex AI",
provider: "vertexai",
plugin: "@genkit-ai/google-genai",
package: `@genkit-ai/google-genai@${genkitInfo.genaiVersion}`,
},
googleai: {
label: "Google AI",
provider: "googleai",
plugin: "@genkit-ai/google-genai",
package: `@genkit-ai/google-genai@${genkitInfo.genaiVersion}`,
},
none: { label: "None" },
};
} else {
modelOptions = {
vertexai: {
label: "Google Cloud Vertex AI",
plugin: "@genkit-ai/vertexai",
package: `@genkit-ai/vertexai@${genkitInfo.vertexVersion}`,
},
googleai: {
label: "Google AI",
plugin: "@genkit-ai/googleai",
package: `@genkit-ai/googleai@${genkitInfo.googleAiVersion}`,
},
none: { label: "None" },
};
}

return modelOptions;
}

/** Plugin name to descriptor. */
const pluginToInfo: Record<string, PluginInfo> = {
"@genkit-ai/firebase": {
plugin: "@genkit-ai/firebase",
imports: "firebase",
init: `
// Load the Firebase plugin, which provides integrations with several
// Firebase services.
firebase()`.trimStart(),
},
"@genkit-ai/google-genai(vertexai)": {
plugin: "@genkit-ai/google-genai",
imports: "vertexAI",
modelImportComment: `
// Import vertexAI provider from the unified plugin. The Vertex AI API provides
// access to many models.`,
init: ` // Load the VertexAI provider. You can optionally specify your location
// and projectID by passing in a config object; if you don't, the provider
// uses the value from environment variables like GCLOUD_PROJECT and GCLOUD_LOCATION.
// If you want to use Vertex Express Mode, you can specify apiKey instead.
vertexAI({location: "global"})`,
model: 'vertexAI.model("gemini-2.5-flash")',
},
"@genkit-ai/google-genai(googleai)": {
plugin: "@genkit-ai/google-genai",
imports: "googleAI",
modelImportComment: `
// Import googleAI provider from the unified plugin. The Gemini Developer API
// provides access to several generative models.`,
init: ` // Load the GoogleAI provider. You can optionally specify your API key by
// passing in a config object; if you don't, the provider uses the value
// from the GOOGLE_GENAI_API_KEY environment variable, which is the
// recommended practice.
googleAI()`,
model: 'googleAI.model("gemini-2.5-flash")',
},
"@genkit-ai/vertexai": {
plugin: "@genkit-ai/vertexai",
imports: "vertexAI",
modelImportComment: `
// Import models from the Vertex AI plugin. The Vertex AI API provides access to
Expand All @@ -302,6 +355,7 @@ const pluginToInfo: Record<string, PluginInfo> = {
model: "gemini20Flash",
},
"@genkit-ai/googleai": {
plugin: "@genkit-ai/googleai",
imports: "googleAI",
modelImportComment: `
// Import models from the Google AI plugin. The Google AI API provides access to
Expand All @@ -316,6 +370,20 @@ const pluginToInfo: Record<string, PluginInfo> = {
},
};

function getPluginInfo(option?: ModelOption): PluginInfo {
if (option?.provider && option.plugin) {
return pluginToInfo[`${option.plugin}(${option.provider})`];
}
if (option?.plugin) {
return pluginToInfo[option.plugin];
}
return {
plugin: "",
imports: "",
init: "",
};
}

/** Basic packages required to use Genkit. */
function getBasePackages(genkitVersion: string): string[] {
const basePackages = ["express", `genkit@${genkitVersion}`];
Expand Down Expand Up @@ -352,13 +420,8 @@ export async function genkitSetup(
}

// Compile plugins list.
const plugins: string[] = [];
const pluginPackages: string[] = [];
pluginPackages.push(`@genkit-ai/firebase@${genkitInfo.genkitVersion}`);

if (modelOptions[model]?.plugin) {
plugins.push(modelOptions[model].plugin || "");
}
if (modelOptions[model]?.package) {
pluginPackages.push(modelOptions[model].package || "");
}
Expand Down Expand Up @@ -393,8 +456,7 @@ export async function genkitSetup(
}));

generateSampleFile(
modelOptions[model].plugin,
plugins,
modelOptions[model],
projectDir,
genkitInfo.templateVersion,
enableTelemetry,
Expand Down Expand Up @@ -515,25 +577,25 @@ async function installNpmPackages(

/**
* Generates a sample index.ts file.
* @param modelPlugin Model plugin name.
* @param configPlugins config plugins.
* @param modelOption Information about the model/plugin
* @param projectDir Where to put the sample
* @param templateVersion Which template the use
* @param enableTelemetry If telemetry is enabled or not.
*/
function generateSampleFile(
modelPlugin: string | undefined,
configPlugins: string[],
modelOption: ModelOption | undefined,
projectDir: string,
templateVersion: string,
enableTelemetry: boolean,
): void {
let modelImport = "";
if (modelPlugin && pluginToInfo[modelPlugin].model) {
const modelInfo = pluginToInfo[modelPlugin].model || "";
modelImport = "\n" + generateImportStatement(modelInfo, modelPlugin) + "\n";
const pluginInfo = getPluginInfo(modelOption);
if (pluginInfo.imports) {
modelImport = "\n" + generateImportStatement(pluginInfo) + "\n";
}
let modelImportComment = "";
if (modelPlugin && pluginToInfo[modelPlugin].modelImportComment) {
const comment = pluginToInfo[modelPlugin].modelImportComment || "";
modelImportComment = `\n${comment}`;
if (pluginInfo.modelImportComment) {
modelImportComment = `\n${pluginInfo.modelImportComment}`;
}
const commentedModelImport = `${modelImportComment}${modelImport}`;
const templatePath = path.join(
Expand All @@ -542,15 +604,10 @@ function generateSampleFile(
);
const template = fs.readFileSync(templatePath, "utf8");
const sample = renderConfig(
configPlugins,
pluginInfo,
template
.replace("$GENKIT_MODEL_IMPORT\n", commentedModelImport)
.replace(
"$GENKIT_MODEL",
modelPlugin
? pluginToInfo[modelPlugin].model || pluginToInfo[modelPlugin].modelStr || ""
: "'' /* TODO: Set a model. */",
),
.replace("$GENKIT_MODEL", pluginInfo.model ?? "'' /* TODO: Set a model. */"),
enableTelemetry,
);
logLabeledBullet("genkit", "Generating sample file");
Expand Down Expand Up @@ -640,21 +697,19 @@ async function updatePackageJson(nonInteractive: boolean, projectDir: string): P
}
}

function renderConfig(pluginNames: string[], template: string, enableTelemetry: boolean): string {
const imports = pluginNames
.map((pluginName) => generateImportStatement(pluginToInfo[pluginName].imports, pluginName))
.join("\n");
const plugins =
pluginNames.map((pluginName) => ` ${pluginToInfo[pluginName].init},`).join("\n") ||
" /* Add your plugins here. */";
function renderConfig(pluginInfo: PluginInfo, template: string, enableTelemetry: boolean): string {
const plugins = pluginInfo.init || " /* Add your plugins here. */";
return template
.replace("$GENKIT_CONFIG_IMPORTS", imports)
.replace("$GENKIT_CONFIG_IMPORTS", generateImportStatement(pluginInfo))
.replace("$GENKIT_CONFIG_PLUGINS", plugins)
.replaceAll("$TELEMETRY_COMMENT", enableTelemetry ? "" : "// ");
}

function generateImportStatement(imports: string, name: string): string {
return `import {${imports}} from "${name}";`;
function generateImportStatement(pluginInfo: PluginInfo): string {
if (pluginInfo.imports && pluginInfo.plugin) {
return `import {${pluginInfo.imports}} from "${pluginInfo.plugin}";`;
}
return "";
}

/**
Expand Down
57 changes: 0 additions & 57 deletions templates/genkit/firebase.0.9.0.template

This file was deleted.

Loading
Loading