diff --git a/js/ai/package.json b/js/ai/package.json index 0c25ab923..2d581b428 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -32,7 +32,8 @@ "colorette": "^2.0.20", "json5": "^2.2.3", "node-fetch": "^3.3.2", - "partial-json": "^0.1.7" + "partial-json": "^0.1.7", + "uuid": "^10.0.0" }, "devDependencies": { "npm-run-all": "^4.1.5", @@ -103,6 +104,18 @@ "import": "./lib/reranker.mjs", "default": "./lib/reranker.js" }, + "./chat": { + "types": "./lib/chat.d.ts", + "require": "./lib/chat.js", + "import": "./lib/chat.mjs", + "default": "./lib/chat.js" + }, + "./session": { + "types": "./lib/session.d.ts", + "require": "./lib/session.js", + "import": "./lib/session.mjs", + "default": "./lib/session.js" + }, "./formats": { "types": "./lib/formats/index.d.ts", "require": "./lib/formats/index.js", @@ -135,6 +148,12 @@ ], "reranker": [ "lib/reranker" + ], + "chat": [ + "lib/chat" + ], + "session": [ + "lib/session" ] } } diff --git a/js/genkit/src/chat.ts b/js/ai/src/chat.ts similarity index 82% rename from js/genkit/src/chat.ts rename to js/ai/src/chat.ts index e9cf58f24..437e9887e 100644 --- a/js/genkit/src/chat.ts +++ b/js/ai/src/chat.ts @@ -14,24 +14,25 @@ * limitations under the License. */ +import { z } from '@genkit-ai/core'; +import { runInNewSpan } from '@genkit-ai/core/tracing'; import { + generate, GenerateOptions, GenerateResponse, + generateStream, GenerateStreamOptions, GenerateStreamResponse, GenerationCommonConfigSchema, MessageData, Part, -} from '@genkit-ai/ai'; -import { z } from '@genkit-ai/core'; -import { Genkit } from './genkit'; +} from './index.js'; import { BaseGenerateOptions, + runWithSession, Session, SessionStore, - runWithSession, } from './session'; -import { runInNewSpan } from './tracing'; export const MAIN_THREAD = 'main'; @@ -105,10 +106,21 @@ export class Chat { } requestBase.messages = [...(requestBase.messages ?? []), promptMessage]; } - requestBase.messages = [ - ...(options.messages ?? []), - ...(requestBase.messages ?? []), - ]; + if (hasPreamble(requestBase.messages)) { + requestBase.messages = [ + // if request base contains a preamble, always put it first + ...(getPreamble(requestBase.messages) ?? []), + // strip out the preamble from history + ...(stripPreamble(options.messages) ?? []), + // add whatever non-preamble remains from request + ...(stripPreamble(requestBase.messages) ?? []), + ]; + } else { + requestBase.messages = [ + ...(options.messages ?? []), + ...(requestBase.messages ?? []), + ]; + } this._messages = requestBase.messages; return requestBase; }); @@ -145,7 +157,7 @@ export class Chat { messages: this.messages, ...resolvedOptions, }; - let response = await this.genkit.generate({ + let response = await generate(this.session.registry, { ...request, streamingCallback, }); @@ -185,11 +197,14 @@ export class Chat { resolvedOptions = options as GenerateStreamOptions; } - const { response, stream } = await this.genkit.generateStream({ - ...(await this.requestBase), - messages: this.messages, - ...resolvedOptions, - }); + const { response, stream } = await generateStream( + this.session.registry, + { + ...(await this.requestBase), + messages: this.messages, + ...resolvedOptions, + } + ); return { response: response.finally(async () => { @@ -208,10 +223,6 @@ export class Chat { ); } - private get genkit(): Genkit { - return this.session.genkit; - } - get messages(): MessageData[] { return this._messages ?? []; } @@ -221,3 +232,15 @@ export class Chat { await this.session.updateMessages(this.threadName, messages); } } + +function hasPreamble(msgs?: MessageData[]) { + return !!msgs?.find((m) => m.metadata?.preamble); +} + +function getPreamble(msgs?: MessageData[]) { + return msgs?.filter((m) => m.metadata?.preamble); +} + +function stripPreamble(msgs?: MessageData[]) { + return msgs?.filter((m) => !m.metadata?.preamble); +} diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index b4caa795c..6c7ab00d1 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -496,6 +496,9 @@ export async function resolveModel( let out: ResolvedModel; let modelId: string; + if (!model) { + model = await registry.lookupValue('defaultModel', 'defaultModel'); + } if (!model) { throw new GenkitError({ status: 'INVALID_ARGUMENT', diff --git a/js/genkit/src/session.ts b/js/ai/src/session.ts similarity index 97% rename from js/genkit/src/session.ts rename to js/ai/src/session.ts index 7a4f7a270..1b00f54df 100644 --- a/js/genkit/src/session.ts +++ b/js/ai/src/session.ts @@ -14,6 +14,11 @@ * limitations under the License. */ +import { z } from '@genkit-ai/core'; +import { Registry } from '@genkit-ai/core/registry'; +import { AsyncLocalStorage } from 'node:async_hooks'; +import { v4 as uuidv4 } from 'uuid'; +import { Chat, ChatOptions, MAIN_THREAD, PromptRenderOptions } from './chat'; import { ExecutablePrompt, GenerateOptions, @@ -21,12 +26,7 @@ import { MessageData, isExecutablePrompt, tagAsPreamble, -} from '@genkit-ai/ai'; -import { z } from '@genkit-ai/core'; -import { AsyncLocalStorage } from 'node:async_hooks'; -import { v4 as uuidv4 } from 'uuid'; -import { Chat, ChatOptions, MAIN_THREAD, PromptRenderOptions } from './chat'; -import { Genkit } from './genkit'; +} from './index.js'; export type BaseGenerateOptions< O extends z.ZodTypeAny = z.ZodTypeAny, @@ -60,7 +60,7 @@ export class Session { private store: SessionStore; constructor( - readonly genkit: Genkit, + readonly registry: Registry, options?: { id?: string; stateSchema?: S; @@ -82,10 +82,6 @@ export class Session { } get state(): S | undefined { - // We always get state from the parent. Parent session is the source of truth. - if (this.genkit instanceof Session) { - return this.genkit.state; - } return this.sessionData!.state; } @@ -228,6 +224,8 @@ export class Session { requestBase = preamble .render({ input: renderOptions?.input, + model: (renderOptions as BaseGenerateOptions)?.model, + config: (renderOptions as BaseGenerateOptions)?.config, }) .then((rb) => { return { diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 9d01e3f79..382b80d14 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -28,8 +28,6 @@ import { export { Status, StatusCodes, StatusSchema } from './statusTypes.js'; export { JSONSchema7 }; -export const GENKIT_SESSION_STATE_INPUT_KEY = '__genkit__sessionState'; - export interface ActionMetadata< I extends z.ZodTypeAny, O extends z.ZodTypeAny, @@ -122,12 +120,6 @@ export function action< ? config.name : `${config.name.pluginId}/${config.name.actionId}`; const actionFn = async (input: I) => { - let sessionStateData: Record | undefined = undefined; - if (input?.hasOwnProperty(GENKIT_SESSION_STATE_INPUT_KEY)) { - sessionStateData = input[GENKIT_SESSION_STATE_INPUT_KEY]; - input = { ...input }; - delete input[GENKIT_SESSION_STATE_INPUT_KEY]; - } input = parseSchema(input, { schema: config.inputSchema, jsonSchema: config.inputJsonSchema, @@ -143,9 +135,6 @@ export function action< metadata.name = actionName; metadata.input = input; - if (sessionStateData) { - input[GENKIT_SESSION_STATE_INPUT_KEY] = sessionStateData; - } const output = await fn(input); metadata.output = JSON.stringify(output); diff --git a/js/genkit/package.json b/js/genkit/package.json index d5a663d3b..76cd64403 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -29,7 +29,8 @@ "dependencies": { "@genkit-ai/core": "workspace:*", "@genkit-ai/ai": "workspace:*", - "@genkit-ai/dotprompt": "workspace:*" + "@genkit-ai/dotprompt": "workspace:*", + "uuid": "^10.0.0" }, "devDependencies": { "@types/express": "^4.17.21", @@ -39,8 +40,7 @@ "tsup": "^8.0.2", "typescript": "^4.9.0", "tsx": "^4.7.1", - "@types/body-parser": "^1.19.5", - "uuid": "^10.0.0" + "@types/body-parser": "^1.19.5" }, "files": [ "genkit-ui", diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 1aedc23b2..eef0320ba 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -37,6 +37,7 @@ import { GenerateStreamResponse, GenerationCommonConfigSchema, IndexerParams, + isExecutablePrompt, ModelArgument, ModelReference, Part, @@ -53,6 +54,7 @@ import { ToolAction, ToolConfig, } from '@genkit-ai/ai'; +import { Chat, ChatOptions } from '@genkit-ai/ai/chat'; import { defineEmbedder, EmbedderAction, @@ -89,6 +91,13 @@ import { RetrieverFn, SimpleRetrieverOptions, } from '@genkit-ai/ai/retriever'; +import { + getCurrentSession, + Session, + SessionData, + SessionError, + SessionOptions, +} from '@genkit-ai/ai/session'; import { resolveTools } from '@genkit-ai/ai/tool'; import { CallableFlow, @@ -118,18 +127,10 @@ import { prompt, } from '@genkit-ai/dotprompt'; import { v4 as uuidv4 } from 'uuid'; -import { Chat, ChatOptions } from './chat.js'; import { BaseEvalDataPointSchema } from './evaluator.js'; import { logger } from './logging.js'; import { GenkitPlugin, genkitPlugin } from './plugin.js'; import { Registry } from './registry.js'; -import { - getCurrentSession, - Session, - SessionData, - SessionError, - SessionOptions, -} from './session.js'; import { toToolDefinition } from './tool.js'; /** @@ -307,9 +308,7 @@ export class Genkit { } /** - * Defines and registers a dotprompt. - * - * This is an alternative to defining and importing a .prompt file. + * Defines and registers a function-based prompt. * * ```ts * const hi = ai.definePrompt( @@ -320,8 +319,15 @@ export class Genkit { * name: z.string(), * }), * }, + * config: { + * temperature: 1, + * }, * }, - * 'hi {{ name }}' + * async (input) => { + * return { + * messages: [ { role: 'user', content: [{ text: `hi ${input.name}` }] } ], + * }; + * } * ); * const { text } = await hi({ name: 'Genkit' }); * ``` @@ -332,11 +338,13 @@ export class Genkit { CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, >( options: PromptMetadata, - template: string + fn: PromptFn ): ExecutablePrompt, O, CustomOptions>; /** - * Defines and registers a function-based prompt. + * Defines and registers a dotprompt. + * + * This is an alternative to defining and importing a .prompt file. * * ```ts * const hi = ai.definePrompt( @@ -347,15 +355,8 @@ export class Genkit { * name: z.string(), * }), * }, - * config: { - * temperature: 1, - * }, * }, - * async (input) => { - * return { - * messages: [ { role: 'user', content: [{ text: `hi ${input.name}` }] } ], - * }; - * } + * 'hi {{ name }}' * ); * const { text } = await hi({ name: 'Genkit' }); * ``` @@ -366,7 +367,7 @@ export class Genkit { CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, >( options: PromptMetadata, - fn: PromptFn + template: string ): ExecutablePrompt, O, CustomOptions>; definePrompt< @@ -490,12 +491,7 @@ export class Genkit { // ignore, no model on a render is OK? } const p = await promptAction; - const promptResult = await p({ - // this feels a litte hacky, but we need to pass session state as action - // input to make it replayable from trace view in the dev ui. - __genkit__sessionState: { state: getCurrentSession()?.state }, - ...opt.input, - }); + const promptResult = await p(opt.input); const resultOptions = { messages: promptResult.messages, docs: promptResult.docs, @@ -778,9 +774,6 @@ export class Genkit { } else { resolvedOptions = options as GenerateOptions; } - if (!resolvedOptions.model) { - resolvedOptions.model = this.options.model; - } return generate(this.registry, resolvedOptions); } @@ -885,9 +878,6 @@ export class Genkit { } else { resolvedOptions = options as GenerateOptions; } - if (!resolvedOptions.model) { - resolvedOptions.model = this.options.model; - } return generateStream(this.registry, resolvedOptions); } @@ -935,10 +925,10 @@ export class Genkit { let options: ChatOptions | undefined; let preamble: ExecutablePrompt | undefined; if (maybeOptions) { - preamble = preambleOrOptions as ExecutablePrompt; options = maybeOptions; - } else if (preambleOrOptions) { - if ((preambleOrOptions as ExecutablePrompt)?.render) { + } + if (preambleOrOptions) { + if (isExecutablePrompt(preambleOrOptions)) { preamble = preambleOrOptions as ExecutablePrompt; } else { options = preambleOrOptions as ChatOptions; @@ -961,7 +951,7 @@ export class Genkit { id: sessionId, state: options?.initialState, }; - return new Session(this, { + return new Session(this.registry, { id: sessionId, sessionData, store: options?.store, @@ -980,7 +970,7 @@ export class Genkit { } const sessionData = await options.store.get(sessionId); - return new Session(this, { + return new Session(this.registry, { id: sessionId, sessionData, store: options.store, @@ -1006,9 +996,20 @@ export class Genkit { // install the default formats in the registry configureFormats(activeRegistry); const plugins = [...(this.options.plugins ?? [])]; + if (this.options.model) { + this.registry.registerValue( + 'defaultModel', + 'defaultModel', + this.options.model + ); + } if (this.options.promptDir !== null) { const dotprompt = genkitPlugin('dotprompt', async (ai) => { - loadPromptFolder(this.registry, this.options.promptDir ?? './prompts'); + loadPromptFolder( + this.registry, + this.options.promptDir ?? './prompts', + '' + ); }); plugins.push(dotprompt); } diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index abd386cf8..8002e7bd7 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -99,6 +99,7 @@ export { type ToolRequestPart, type ToolResponsePart, } from '@genkit-ai/ai'; +export { type SessionData, type SessionStore } from '@genkit-ai/ai/session'; export { FlowActionInputSchema, FlowErrorSchema, @@ -152,4 +153,3 @@ export { } from '@genkit-ai/core'; export { loadPromptFile } from '@genkit-ai/dotprompt'; export * from './genkit.js'; -export { type SessionData, type SessionStore } from './session.js'; diff --git a/js/genkit/tests/chat_test.ts b/js/genkit/tests/chat_test.ts index 534015856..de9a5c6a4 100644 --- a/js/genkit/tests/chat_test.ts +++ b/js/genkit/tests/chat_test.ts @@ -178,6 +178,7 @@ describe('preamble', () => { model: 'programmableModel', }); pm = defineProgrammableModel(ai); + defineEchoModel(ai); }); it('swaps out preamble on prompt tool invocation', async () => { @@ -462,4 +463,76 @@ describe('preamble', () => { ], }); }); + + it('updates the preabmle on fresh chat instance', async () => { + const agent = ai.definePrompt( + { + name: 'agent', + config: { temperature: 2 }, + description: 'Agent A description', + }, + '{{ role "system"}} greet {{ @state.name }}' + ); + + const session = ai.createSession({ initialState: { name: 'Pavel' } }); + + const chat = session.chat(agent, { model: 'echoModel' }); + let response = await chat.send('hi'); + + assert.deepStrictEqual(response.messages, [ + { + role: 'system', + content: [{ text: ' greet Pavel' }], + metadata: { preamble: true }, + }, + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [ + { text: 'Echo: system: greet Pavel,hi' }, + { text: '; config: {"temperature":2}' }, + ], + }, + ]); + + await session.updateState({ name: 'Michael' }); + + const freshChat = session.chat(agent, { model: 'echoModel' }); + response = await freshChat.send('hi'); + + assert.deepStrictEqual(response.messages, [ + { + role: 'system', + content: [{ text: ' greet Michael' }], + metadata: { preamble: true }, + }, + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [ + { text: 'Echo: system: greet Pavel,hi' }, + { text: '; config: {"temperature":2}' }, + ], + }, + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [ + { + text: 'Echo: system: greet Michael,hi,Echo: system: greet Pavel,hi,; config: {"temperature":2},hi', + }, + { text: '; config: {"temperature":2}' }, + ], + }, + ]); + }); }); diff --git a/js/genkit/tests/prompts/toolPrompt.prompt b/js/genkit/tests/prompts/toolPrompt.prompt new file mode 100644 index 000000000..bc5c8e2e0 --- /dev/null +++ b/js/genkit/tests/prompts/toolPrompt.prompt @@ -0,0 +1,6 @@ +--- +description: prompt in a file +tools: + - agentA +--- +{{ role "system" }} {{ @state.name }} toolPrompt prompt \ No newline at end of file diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index df93ed5d3..5a36291fa 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -19,7 +19,12 @@ import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; import { z } from '../src/index'; -import { defineEchoModel, defineStaticResponseModel } from './helpers'; +import { + ProgrammableModel, + defineEchoModel, + defineProgrammableModel, + defineStaticResponseModel, +} from './helpers'; describe('definePrompt - dotprompt', () => { describe('default model', () => { @@ -780,3 +785,289 @@ describe('prompt', () => { assert.strictEqual(text, 'Echo: hi banana; config: {"temperature":11}'); }); }); + +describe('asTool', () => { + let ai: Genkit; + let pm: ProgrammableModel; + + beforeEach(() => { + ai = genkit({ + model: 'programmableModel', + promptDir: './tests/prompts', + }); + pm = defineProgrammableModel(ai); + }); + + it('swaps out preamble on .prompt file tool invocation', async () => { + const session = ai.createSession({ initialState: { name: 'Genkit' } }); + const agentA = ai.definePrompt( + { + name: 'agentA', + config: { temperature: 2 }, + description: 'Agent A description', + tools: ['toolPrompt'], // <--- defined in a .prompt file + }, + async () => { + return { + messages: [ + { + role: 'system', + content: [{ text: ' agent a' }], + }, + ], + }; + } + ); + + // simple hi, nothing interesting... + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [{ text: `hi ${session.state?.name} from agent a` }], + }, + }; + }; + const chat = session.chat(agentA); + let { text } = await chat.send('hi'); + assert.strictEqual(text, 'hi Genkit from agent a'); + assert.deepStrictEqual(pm.lastRequest, { + config: { + temperature: 2, + }, + messages: [ + { + content: [{ text: ' agent a' }], + metadata: { preamble: true }, + role: 'system', + }, + { + content: [{ text: 'hi' }], + role: 'user', + }, + ], + output: {}, + tools: [ + { + name: 'toolPrompt', + description: 'prompt in a file', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + ], + }); + + // transfer to toolPrompt... + + // first response be tools call, the subsequent just text response from agent b. + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [ + reqCounter++ === 0 + ? { + toolRequest: { + name: 'toolPrompt', + input: {}, + ref: 'ref123', + }, + } + : { text: 'hi from agent b' }, + ], + }, + }; + }; + + ({ text } = await chat.send('pls transfer to b')); + + assert.deepStrictEqual(text, 'hi from agent b'); + assert.deepStrictEqual(pm.lastRequest, { + config: { + // TODO: figure out if config should be swapped out as well... + temperature: 2, + }, + messages: [ + { + role: 'system', + content: [{ text: ' Genkit toolPrompt prompt' }], // <--- NOTE: swapped out the preamble + metadata: { preamble: true }, + }, + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [{ text: 'hi Genkit from agent a' }], + }, + { + role: 'user', + content: [{ text: 'pls transfer to b' }], + }, + { + role: 'model', + content: [ + { + toolRequest: { + input: {}, + name: 'toolPrompt', + ref: 'ref123', + }, + }, + ], + }, + { + role: 'tool', + content: [ + { + toolResponse: { + name: 'toolPrompt', + output: 'transferred to toolPrompt', + ref: 'ref123', + }, + }, + ], + }, + ], + output: {}, + tools: [ + { + name: 'agentA', + description: 'Agent A description', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + ], + }); + + // transfer back to to agent A... + + // first response be tools call, the subsequent just text response from agent a. + reqCounter = 0; + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [ + reqCounter++ === 0 + ? { + toolRequest: { + name: 'agentA', + input: {}, + ref: 'ref123', + }, + } + : { text: 'hi Genkit from agent a' }, + ], + }, + }; + }; + + ({ text } = await chat.send('pls transfer to a')); + + assert.deepStrictEqual(text, 'hi Genkit from agent a'); + assert.deepStrictEqual(pm.lastRequest, { + config: { + temperature: 2, + }, + messages: [ + { + role: 'system', + content: [{ text: ' agent a' }], // <--- NOTE: swapped out the preamble + metadata: { preamble: true }, + }, + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [{ text: 'hi Genkit from agent a' }], + }, + { + role: 'user', + content: [{ text: 'pls transfer to b' }], + }, + { + role: 'model', + content: [ + { + toolRequest: { + input: {}, + name: 'toolPrompt', + ref: 'ref123', + }, + }, + ], + }, + { + role: 'tool', + content: [ + { + toolResponse: { + name: 'toolPrompt', + output: 'transferred to toolPrompt', + ref: 'ref123', + }, + }, + ], + }, + { + role: 'model', + content: [{ text: 'hi from agent b' }], + }, + { + role: 'user', + content: [{ text: 'pls transfer to a' }], + }, + { + role: 'model', + content: [ + { + toolRequest: { + input: {}, + name: 'agentA', + ref: 'ref123', + }, + }, + ], + }, + { + role: 'tool', + content: [ + { + toolResponse: { + name: 'agentA', + output: 'transferred to agentA', + ref: 'ref123', + }, + }, + ], + }, + ], + output: {}, + tools: [ + { + description: 'prompt in a file', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + name: 'toolPrompt', + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + ], + }); + }); +}); diff --git a/js/plugins/dotprompt/src/metadata.ts b/js/plugins/dotprompt/src/metadata.ts index 165176919..3917fda9b 100644 --- a/js/plugins/dotprompt/src/metadata.ts +++ b/js/plugins/dotprompt/src/metadata.ts @@ -86,6 +86,7 @@ export interface PromptMetadata< */ export const PromptFrontmatterSchema = z.object({ name: z.string().optional(), + description: z.string().optional(), variant: z.string().optional(), model: z.string().optional(), tools: z.array(z.string()).optional(), @@ -154,6 +155,7 @@ export function toMetadata( return stripUndefinedOrNull({ name: fm.name, + description: fm.description, variant: fm.variant, model: fm.model, config: fm.config, diff --git a/js/plugins/dotprompt/src/prompt.ts b/js/plugins/dotprompt/src/prompt.ts index 41a15e14c..a78a14e98 100644 --- a/js/plugins/dotprompt/src/prompt.ts +++ b/js/plugins/dotprompt/src/prompt.ts @@ -26,6 +26,7 @@ import { } from '@genkit-ai/ai'; import { MessageData, ModelArgument } from '@genkit-ai/ai/model'; import { DocumentData } from '@genkit-ai/ai/retriever'; +import { getCurrentSession } from '@genkit-ai/ai/session'; import { GenkitError, z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import { parseSchema } from '@genkit-ai/core/schema'; @@ -47,8 +48,6 @@ import { compile } from './template.js'; export type PromptData = PromptFrontmatter & { template: string }; -export const GENKIT_SESSION_STATE_INPUT_KEY = '__genkit__sessionState'; - export type PromptGenerateOptions< V = unknown, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, @@ -67,6 +66,7 @@ interface RenderMetadata { export class Dotprompt implements PromptMetadata { name: string; + description?: string; variant?: string; hash: string; @@ -136,6 +136,7 @@ export class Dotprompt implements PromptMetadata { action?: PromptAction ) { this.name = options.name || 'untitledPrompt'; + this.description = options.description; this.variant = options.variant; this.model = options.model; this.input = options.input || { schema: z.any() }; @@ -181,10 +182,8 @@ export class Dotprompt implements PromptMetadata { */ renderMessages(input?: I, options?: RenderMetadata): MessageData[] { let sessionStateData: Record | undefined = undefined; - if (input?.hasOwnProperty(GENKIT_SESSION_STATE_INPUT_KEY)) { - sessionStateData = input[GENKIT_SESSION_STATE_INPUT_KEY]; - input = { ...input }; - delete input[GENKIT_SESSION_STATE_INPUT_KEY]; + if (getCurrentSession()) { + sessionStateData = { state: getCurrentSession()?.state }; } input = parseSchema(input, { schema: this.input?.schema, @@ -206,7 +205,7 @@ export class Dotprompt implements PromptMetadata { this.registry, { name: registryDefinitionKey(this.name, this.variant, options?.ns), - description: options?.description ?? 'Defined by Dotprompt', + description: options?.description ?? this.description, inputSchema: this.input?.schema, inputJsonSchema: this.input?.jsonSchema, metadata: { diff --git a/js/plugins/dotprompt/src/registry.ts b/js/plugins/dotprompt/src/registry.ts index b208a3847..42a35acf8 100644 --- a/js/plugins/dotprompt/src/registry.ts +++ b/js/plugins/dotprompt/src/registry.ts @@ -78,8 +78,8 @@ async function maybeLoadPrompt( export async function loadPromptFolder( registry: Registry, - - dir: string = './prompts' + dir: string = './prompts', + ns: string ): Promise { const promptsPath = resolve(dir); return new Promise((resolve, reject) => { @@ -119,7 +119,7 @@ export async function loadPromptFolder( .replace(`${promptsPath}/`, '') .replace(/\//g, '-'); } - loadPrompt(registry, dirEnt.path, dirEnt.name, prefix); + loadPrompt(registry, dirEnt.path, dirEnt.name, prefix, ns); } } }); @@ -137,7 +137,8 @@ export function loadPrompt( registry: Registry, path: string, filename: string, - prefix = '' + prefix = '', + ns = 'dotprompt' ): Dotprompt { let name = `${prefix ? `${prefix}-` : ''}${basename(filename, '.prompt')}`; let variant: string | null = null; @@ -151,6 +152,6 @@ export function loadPrompt( if (variant) { prompt.variant = variant; } - prompt.define({ ns: `dotprompt` }); + prompt.define({ ns }); return prompt; } diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 972e6a2bf..dfdbbd385 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: partial-json: specifier: ^0.1.7 version: 0.1.7 + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: npm-run-all: specifier: ^4.1.5 @@ -170,6 +173,9 @@ importers: '@genkit-ai/dotprompt': specifier: workspace:* version: link:../plugins/dotprompt + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@types/body-parser': specifier: ^1.19.5 @@ -195,9 +201,6 @@ importers: typescript: specifier: ^4.9.0 version: 4.9.5 - uuid: - specifier: ^10.0.0 - version: 10.0.0 plugins/chroma: dependencies: @@ -1185,7 +1188,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@0.9.0-dev.3)(@genkit-ai/core@0.9.0-dev.3) + version: 0.10.1(@genkit-ai/ai@0.9.0-dev.4)(@genkit-ai/core@0.9.0-dev.4) devDependencies: rimraf: specifier: ^6.0.1 @@ -1952,11 +1955,11 @@ packages: '@firebase/util@1.9.5': resolution: {integrity: sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==} - '@genkit-ai/ai@0.9.0-dev.3': - resolution: {integrity: sha512-fXi7onEpViZX86dPq0xWsqxivvXQMf9wH3boaNJFDJg22YvJMpb5MDV+jgzmXbKwGFCVaFAsePMBzv7Ikt703A==} + '@genkit-ai/ai@0.9.0-dev.4': + resolution: {integrity: sha512-j7mCfJnPupK9tqkESV+SVtwGAfGFB6CnIr/NXeTZleU6cupocP0uFkZKi72HbdMYk2VI38spplp5aIt4jW/wNA==} - '@genkit-ai/core@0.9.0-dev.3': - resolution: {integrity: sha512-fA8XUVYY9K77zWG0AVVHjcYe3XCYfnUuTOf11VMbvxVG3t/cHfkrKzLG/lxyO41O1vfL7xwIcaHLlpzXVQmZPQ==} + '@genkit-ai/core@0.9.0-dev.4': + resolution: {integrity: sha512-v6QpSedACJU/jKJGukJKHM5sPJdyYKPoyzAMyztWvVD12t2bkvXYL7+QyCeB/cUE7cijyO4w/2lRNyZciyAgMw==} '@google-cloud/aiplatform@3.25.0': resolution: {integrity: sha512-qKnJgbyCENjed8e1G5zZGFTxxNKhhaKQN414W2KIVHrLxMFmlMuG+3QkXPOWwXBnT5zZ7aMxypt5og0jCirpHg==} @@ -6764,9 +6767,9 @@ snapshots: dependencies: tslib: 2.6.2 - '@genkit-ai/ai@0.9.0-dev.3': + '@genkit-ai/ai@0.9.0-dev.4': dependencies: - '@genkit-ai/core': 0.9.0-dev.3 + '@genkit-ai/core': 0.9.0-dev.4 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -6776,7 +6779,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@0.9.0-dev.3': + '@genkit-ai/core@0.9.0-dev.4': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.26.0(@opentelemetry/api@1.9.0) @@ -9358,10 +9361,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@0.9.0-dev.3)(@genkit-ai/core@0.9.0-dev.3): + genkitx-openai@0.10.1(@genkit-ai/ai@0.9.0-dev.4)(@genkit-ai/core@0.9.0-dev.4): dependencies: - '@genkit-ai/ai': 0.9.0-dev.3 - '@genkit-ai/core': 0.9.0-dev.3 + '@genkit-ai/ai': 0.9.0-dev.4 + '@genkit-ai/core': 0.9.0-dev.4 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 transitivePeerDependencies: