diff --git a/docs/src/content/docs/reference/scripts/chat-participants.mdx b/docs/src/content/docs/reference/scripts/chat-participants.mdx new file mode 100644 index 0000000000..cd63298df0 --- /dev/null +++ b/docs/src/content/docs/reference/scripts/chat-participants.mdx @@ -0,0 +1,47 @@ +--- +title: Chat Participants +sidebar: + order: 50 +--- +import { Code } from '@astrojs/starlight/components'; +import scriptSource from "../../../../../../packages/sample/genaisrc/multi-turn.genai.js?raw" + + +The `defChatParticipant` allows to register a function that can add new user messages in the chat sequence. +This allows to create multi-turn chat, or to simulate a conversation with multiple participants. + +```js +let turn = 0 +defChatParticipant((_, messages) => { + if (++turn === 1) _.$`Are you sure?` +}) +``` + +In the example above, the `defChatParticipant` function is used to register a function that will be called every time a new message is added to the chat. + +The function receives two arguments: the first argument is the `Chat` object, and the second argument is the list of messages that have been added to the chat since the last call to the function. + +```js +defChatParticipant(async (_, messages) => { + const text = messages.at(-1).content + ... +}) +``` + +## Tracking turns + +The participant will be called on every turn so it is important to keep track of the turns to avoid infinite loops. + +```js +let turn = 0 +defChatParticipant((_, messages) => { + if (++turn === 1) _.$`Are you sure?` +}) +``` + + +## Example: QA generator + +This script uses a multi-turn chat to generate questions, answers and validate the quality of the answers. + + diff --git a/packages/core/src/chat.ts b/packages/core/src/chat.ts index b7c59d8adb..8f03dc6a2d 100644 --- a/packages/core/src/chat.ts +++ b/packages/core/src/chat.ts @@ -402,7 +402,6 @@ async function processChatMessage( functions: ChatFunctionCallback[], chatParticipants: ChatParticipant[], schemas: Record, - vars: Partial, genVars: Record, options: GenerationOptions ): Promise { @@ -445,11 +444,7 @@ async function processChatMessage( const { label } = participantOptions || {} trace.startDetails(`🙋 participant ${label || ""}`) - const ctx = createChatTurnGenerationContext( - options, - vars, - trace - ) + const ctx = createChatTurnGenerationContext(options, trace) await generator(ctx, structuredClone(messages)) const node = ctx.node checkCancelled(cancellationToken) @@ -578,7 +573,6 @@ export async function executeChatSession( functions, chatParticipants, schemas, - vars, genVars, genOptions ) diff --git a/packages/core/src/promptcontext.ts b/packages/core/src/promptcontext.ts index 1f46355247..8f33130ab7 100644 --- a/packages/core/src/promptcontext.ts +++ b/packages/core/src/promptcontext.ts @@ -198,7 +198,7 @@ export function createPromptContext( } const ctx = Object.freeze({ - ...createChatGenerationContext(options, env, trace), + ...createChatGenerationContext(options, trace), script: () => {}, system: () => {}, env, @@ -227,7 +227,7 @@ export function createPromptContext( infoCb?.({ text: `run prompt ${label || ""}` }) const genOptions = mergeGenerationOptions(options, runOptions) - const ctx = createChatGenerationContext(genOptions, vars, trace) + const ctx = createChatGenerationContext(genOptions, trace) if (typeof generator === "string") ctx.node.children.push(createTextNode(generator)) else await generator(ctx) diff --git a/packages/core/src/runpromptcontext.ts b/packages/core/src/runpromptcontext.ts index 2842031862..469cae28f3 100644 --- a/packages/core/src/runpromptcontext.ts +++ b/packages/core/src/runpromptcontext.ts @@ -22,10 +22,8 @@ import { resolveFileDataUri } from "./file" export function createChatTurnGenerationContext( options: GenerationOptions, - vars: Partial, trace: MarkdownTrace ): ChatTurnGenerationContext & { node: PromptNode } { - const { cancellationToken, infoCb } = options || {} const node: PromptNode = { children: [] } const log = (...args: any[]) => { @@ -115,10 +113,9 @@ export interface RunPromptContextNode extends ChatGenerationContext { export function createChatGenerationContext( options: GenerationOptions, - vars: Partial, trace: MarkdownTrace ): RunPromptContextNode { - const turnCtx = createChatTurnGenerationContext(options, vars, trace) + const turnCtx = createChatTurnGenerationContext(options, trace) const node = turnCtx.node const defTool: ( diff --git a/packages/sample/genaisrc/multi-turn.genai.js b/packages/sample/genaisrc/multi-turn.genai.js index 74bae0a09d..1b826081f2 100644 --- a/packages/sample/genaisrc/multi-turn.genai.js +++ b/packages/sample/genaisrc/multi-turn.genai.js @@ -3,10 +3,9 @@ script({ title: "Multi-turn conversation", files: ["src/rag/markdown.md"], system: ["system", "system.files"], + tests: {}, }) -def("FILE", env.files) - let turn = 0 defChatParticipant( async (_, messages) => { @@ -79,4 +78,5 @@ Answer the QUESTION using the contents in FILE. { label: "answerer" } ) +def("FILE", env.files) $`Generate a set of questions for the files to build a FAQ. Format one line per question in text.`