Skip to content

Commit e26423c

Browse files
authored
refactor(js/plugins): implement plugin wrapper that make it easier to work with plugins directly (#3551)
1 parent 7e7ea9b commit e26423c

File tree

6 files changed

+125
-8
lines changed

6 files changed

+125
-8
lines changed

‎js/genkit/src/plugin.ts‎

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414
* limitations under the License.
1515
*/
1616

17-
import type { Action, ActionMetadata, BackgroundAction } from '@genkit-ai/core';
17+
import { type ModelAction } from '@genkit-ai/ai/model';
18+
import {
19+
GenkitError,
20+
type Action,
21+
type ActionMetadata,
22+
type BackgroundAction,
23+
} from '@genkit-ai/core';
1824
import type { Genkit } from './genkit.js';
1925
import type { ActionType } from './registry.js';
2026
export { embedder, embedderActionMetadata } from '@genkit-ai/ai/embedder';
@@ -26,7 +32,6 @@ export {
2632
} from '@genkit-ai/ai/model';
2733
export { reranker } from '@genkit-ai/ai/reranker';
2834
export { indexer, retriever } from '@genkit-ai/ai/retriever';
29-
3035
export interface PluginProvider {
3136
name: string;
3237
initializer: () => void | Promise<void>;
@@ -45,6 +50,9 @@ export interface GenkitPluginV2 {
4550
name: string
4651
) => ResolvableAction | undefined | Promise<ResolvableAction | undefined>;
4752
list?: () => ActionMetadata[] | Promise<ActionMetadata[]>;
53+
54+
// A shortcut for resolving a model.
55+
model(name: string): Promise<ModelAction>;
4856
}
4957

5058
export type GenkitPlugin = (genkit: Genkit) => PluginProvider;
@@ -85,10 +93,57 @@ export function genkitPlugin<T extends PluginInit>(
8593
});
8694
}
8795

96+
export class GenkitPluginV2Instance implements Required<GenkitPluginV2> {
97+
readonly version = 'v2';
98+
readonly name: string;
99+
100+
private plugin: Omit<GenkitPluginV2, 'version' | 'model'>;
101+
102+
constructor(plugin: Omit<GenkitPluginV2, 'version' | 'model'>) {
103+
this.name = plugin.name;
104+
this.plugin = plugin;
105+
}
106+
107+
init(): ResolvableAction[] | Promise<ResolvableAction[]> {
108+
if (!this.plugin.init) {
109+
return [];
110+
}
111+
return this.plugin.init();
112+
}
113+
114+
list(): ActionMetadata[] | Promise<ActionMetadata[]> {
115+
if (!this.plugin.list) {
116+
return [];
117+
}
118+
return this.plugin.list();
119+
}
120+
121+
resolve(
122+
actionType: ActionType,
123+
name: string
124+
): ResolvableAction | undefined | Promise<ResolvableAction | undefined> {
125+
if (!this.plugin.resolve) {
126+
return undefined;
127+
}
128+
return this.plugin.resolve(actionType, name);
129+
}
130+
131+
async model(name: string): Promise<ModelAction> {
132+
const model = await this.resolve('model', name);
133+
if (!model) {
134+
throw new GenkitError({
135+
message: `Failed to resolve model ${name} for plugin ${this.name}`,
136+
status: 'NOT_FOUND',
137+
});
138+
}
139+
return model as ModelAction;
140+
}
141+
}
142+
88143
export function genkitPluginV2(
89-
options: Omit<GenkitPluginV2, 'version'>
90-
): GenkitPluginV2 {
91-
return { ...options, version: 'v2' };
144+
options: Omit<GenkitPluginV2, 'version' | 'model'>
145+
): GenkitPluginV2Instance {
146+
return new GenkitPluginV2Instance(options);
92147
}
93148

94149
export function isPluginV2(plugin: unknown): plugin is GenkitPluginV2 {

‎js/genkit/tests/plugins_test.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ const v2Plugin = genkitPluginV2({
7878
resolve(actionType, name) {
7979
switch (actionType) {
8080
case 'model':
81+
if (name === 'not-found') {
82+
return undefined;
83+
}
8184
return model({ name }, async () => {
8285
return {};
8386
});
@@ -241,4 +244,16 @@ describe('session', () => {
241244
])
242245
);
243246
});
247+
248+
it('resolves model using model resolve helper', async () => {
249+
const act = await v2Plugin.model('foo-model');
250+
assert.ok(act);
251+
assert.strictEqual(act.__action.name, 'foo-model');
252+
assert.strictEqual(act.__action.actionType, 'model');
253+
254+
await assert.rejects(v2Plugin.model('not-found'), {
255+
name: 'GenkitError',
256+
status: 'NOT_FOUND',
257+
});
258+
});
244259
});

‎js/plugins/compat-oai/src/deepseek/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
z,
2323
} from 'genkit';
2424
import { logger } from 'genkit/logging';
25-
import { GenkitPluginV2 } from 'genkit/plugin';
25+
import { type GenkitPluginV2 } from 'genkit/plugin';
2626
import { ActionType } from 'genkit/registry';
2727
import OpenAI from 'openai';
2828
import { openAICompatible, PluginOptions } from '../index.js';

‎js/plugins/compat-oai/src/openai/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
ModelReference,
2525
z,
2626
} from 'genkit';
27-
import { GenkitPluginV2, ResolvableAction } from 'genkit/plugin';
27+
import { ResolvableAction, type GenkitPluginV2 } from 'genkit/plugin';
2828
import { ActionType } from 'genkit/registry';
2929
import OpenAI from 'openai';
3030
import {

‎js/plugins/compat-oai/src/xai/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
z,
2323
} from 'genkit';
2424
import { logger } from 'genkit/logging';
25-
import { GenkitPluginV2, ResolvableAction } from 'genkit/plugin';
25+
import { ResolvableAction, type GenkitPluginV2 } from 'genkit/plugin';
2626
import { ActionType } from 'genkit/registry';
2727
import OpenAI from 'openai';
2828
import {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { openAI } from '@genkit-ai/compat-oai/openai';
18+
19+
(async () => {
20+
const oai = openAI();
21+
const gpt4o = await oai.model('gpt-4o');
22+
const response = await gpt4o({
23+
messages: [
24+
{
25+
role: 'user',
26+
content: [{ text: 'what is a gablorken of 4!' }],
27+
},
28+
],
29+
tools: [
30+
{
31+
name: 'gablorken',
32+
description: 'calculates a gablorken',
33+
inputSchema: {
34+
type: 'object',
35+
properties: {
36+
value: {
37+
type: 'number',
38+
description: 'the value to calculate gablorken for',
39+
},
40+
},
41+
},
42+
},
43+
],
44+
});
45+
46+
console.log(JSON.stringify(response.message, undefined, 2));
47+
})();

0 commit comments

Comments
 (0)