Skip to content
Merged
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
2 changes: 2 additions & 0 deletions js/ai/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export {
defineResource,
dynamicResource,
isDynamicResourceAction,
resource,
type DynamicResourceAction,
type ResourceAction,
type ResourceFn,
Expand Down Expand Up @@ -144,6 +145,7 @@ export {
asTool,
defineInterrupt,
defineTool,
interrupt,
type InterruptConfig,
type ToolAction,
type ToolArgument,
Expand Down
2 changes: 2 additions & 0 deletions js/ai/src/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ export function resource(
/**
* Defines a dynamic resource. Dynamic resources are just like regular resources but will not be
* registered in the Genkit registry and can be defined dynamically at runtime.
*
* @deprecated renamed to {@link resource}.
*/
export function dynamicResource(
opts: ResourceOptions,
Expand Down
30 changes: 18 additions & 12 deletions js/ai/src/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,22 +386,26 @@ export function isDynamicTool(t: unknown): t is ToolAction {
return isAction(t) && !t.__registry;
}

export function defineInterrupt<I extends z.ZodTypeAny, O extends z.ZodTypeAny>(
registry: Registry,
export function interrupt<I extends z.ZodTypeAny, O extends z.ZodTypeAny>(
config: InterruptConfig<I, O>
): ToolAction<I, O> {
const { requestMetadata, ...toolConfig } = config;

return defineTool<I, O>(
registry,
toolConfig,
async (input, { interrupt }) => {
if (!config.requestMetadata) interrupt();
else if (typeof config.requestMetadata === 'object')
interrupt(config.requestMetadata);
else interrupt(await Promise.resolve(config.requestMetadata(input)));
}
);
return tool<I, O>(toolConfig, async (input, { interrupt }) => {
if (!config.requestMetadata) interrupt();
else if (typeof config.requestMetadata === 'object')
interrupt(config.requestMetadata);
else interrupt(await Promise.resolve(config.requestMetadata(input)));
});
}

export function defineInterrupt<I extends z.ZodTypeAny, O extends z.ZodTypeAny>(
registry: Registry,
config: InterruptConfig<I, O>
): ToolAction<I, O> {
const i = interrupt(config);
registry.registerAction('tool', i);
return i;
}

/**
Expand Down Expand Up @@ -441,6 +445,8 @@ export function tool<I extends z.ZodTypeAny, O extends z.ZodTypeAny>(
/**
* Defines a dynamic tool. Dynamic tools are just like regular tools but will not be registered in the
* Genkit registry and can be defined dynamically at runtime.
*
* @deprecated renamed to {@link tool}.
*/
export function dynamicTool<I extends z.ZodTypeAny, O extends z.ZodTypeAny>(
config: ToolConfig<I, O>,
Expand Down
3 changes: 2 additions & 1 deletion js/genkit/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export {
modelActionMetadata,
modelRef,
rerankerRef,
resource,
retrieverRef,
type DocumentData,
type DynamicResourceAction,
Expand Down Expand Up @@ -116,7 +117,7 @@ export {
type SessionData,
type SessionStore,
} from '@genkit-ai/ai/session';
export { dynamicTool } from '@genkit-ai/ai/tool';
export { dynamicTool, tool } from '@genkit-ai/ai/tool';
export {
GENKIT_CLIENT_HEADER,
GENKIT_VERSION,
Expand Down
2 changes: 2 additions & 0 deletions js/genkit/src/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
export {
asTool,
dynamicTool,
interrupt,
toToolDefinition,
tool,
type ToolAction,
type ToolArgument,
type ToolConfig,
Expand Down
49 changes: 48 additions & 1 deletion js/genkit/tests/generate_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Operation, z, type JSONSchema7 } from '@genkit-ai/core';
import * as assert from 'assert';
import { beforeEach, describe, it } from 'node:test';
import { modelRef } from '../../ai/src/model';
import { interrupt } from '../../ai/src/tool';
import {
dynamicResource,
dynamicTool,
Expand Down Expand Up @@ -956,6 +957,10 @@ describe('generate', () => {
return interrupt();
}
);
const dynamicInterrupt = interrupt({
name: 'dynamicInterrupt',
description: 'description',
});

// first response is a tool call, the subsequent responses are just text response from agent b.
let reqCounter = 0;
Expand Down Expand Up @@ -990,6 +995,13 @@ describe('generate', () => {
ref: 'ref789',
},
},
{
toolRequest: {
name: 'dynamicInterrupt',
input: { doIt: true },
ref: 'ref890',
},
},
]
: [{ text: 'done' }],
},
Expand All @@ -998,7 +1010,12 @@ describe('generate', () => {

const response = await ai.generate({
prompt: 'call the tool',
tools: ['interruptingTool', 'simpleTool', 'resumableTool'],
tools: [
'interruptingTool',
'simpleTool',
'resumableTool',
dynamicInterrupt,
],
});

assert.strictEqual(reqCounter, 1);
Expand Down Expand Up @@ -1039,6 +1056,16 @@ describe('generate', () => {
},
},
},
{
metadata: { interrupt: true },
toolRequest: {
input: {
doIt: true,
},
name: 'dynamicInterrupt',
ref: 'ref890',
},
},
]);
assert.deepStrictEqual(response.message?.toJSON(), {
role: 'model',
Expand Down Expand Up @@ -1082,6 +1109,16 @@ describe('generate', () => {
},
},
},
{
metadata: { interrupt: true },
toolRequest: {
input: {
doIt: true,
},
name: 'dynamicInterrupt',
ref: 'ref890',
},
},
],
});
assert.deepStrictEqual(pm.lastRequest, {
Expand Down Expand Up @@ -1124,6 +1161,16 @@ describe('generate', () => {
$schema: 'http://json-schema.org/draft-07/schema#',
},
},
{
description: 'description',
inputSchema: {
$schema: 'http://json-schema.org/draft-07/schema#',
},
name: 'dynamicInterrupt',
outputSchema: {
$schema: 'http://json-schema.org/draft-07/schema#',
},
},
],
});
});
Expand Down