diff --git a/.genkit/servers/tools.json b/.genkit/servers/tools.json new file mode 100644 index 000000000..74f49050f --- /dev/null +++ b/.genkit/servers/tools.json @@ -0,0 +1,4 @@ +{ + "url": "http://localhost:4000", + "timestamp": "2024-11-08T15:36:13.186Z" +} \ No newline at end of file diff --git a/js/testapps/flow-sample1/readme.md b/js/testapps/flow-sample1/readme.md new file mode 100644 index 000000000..80b404fca --- /dev/null +++ b/js/testapps/flow-sample1/readme.md @@ -0,0 +1,213 @@ + +# Flow Sample 1 + +A basic example project showcasing various Genkit flows. + +## Project Setup + +### Installation + +Ensure you have `pnpm` installed and run the following: + +```bash +pnpm install +``` + +### Build and Run + +To build and run the project, use: + +```bash +pnpm build-and-run +``` + +This compiles the TypeScript source code and runs the `index.js` file in the `lib` directory. + +--- + +## Available Flows + +### Basic Flow + +A simple flow that demonstrates the use of `run`. + +#### Command: + +```bash +genkit flow:run basic '"test input"' +``` + +#### Expected Output: + +```text +"foo: subject: test input" +``` + +--- + +### Parent Flow + +A flow that runs the `basic` flow and returns its output as a JSON string. + +#### Command: + +```bash +genkit flow:run parent +``` + +#### Expected Output: + +```text +"\"foo: subject: foo\"" +``` + +--- + +### Streamy Flow + +A streaming flow that emits incremental data based on the input number. + +#### Command: + +```bash +genkit flow:run streamy 5 +``` + +#### Expected Output: + +```text +done: 5, streamed: 5 times +``` + +The streamed content will also show intermediate outputs for each count. + +--- + +### StreamyThrowy Flow + +Similar to `streamy`, but throws an error when the count reaches 3. + +#### Command: + +```bash +genkit flow:run streamyThrowy 5 +``` + +#### Expected Output: + +Throws an error at count 3. + +--- + +### Throwy Flow + +Demonstrates error handling by throwing an error when a subject is provided. + +#### Command: + +```bash +genkit flow:run throwy '"test input"' +``` + +#### Expected Output: + +Throws an error with the message: + +```text +test input +``` + +--- + +### Throwy2 Flow + +Throws an error within the `run` function based on input. + +#### Command: + +```bash +genkit flow:run throwy2 '"test input"' +``` + +#### Expected Output: + +Throws an error with the message: + +```text +test input +``` + +--- + +### FlowMultiStepCaughtError + +A multi-step flow that catches errors and continues execution. + +#### Command: + +```bash +genkit flow:run flowMultiStepCaughtError '"input data"' +``` + +#### Expected Output: + +```text +"input data, Step 1, , Step 3" +``` + +A string indicating that Step 2 had an error, but the process continued. + +--- + +### MultiSteps Flow + +A flow demonstrating chaining multiple steps. + +#### Command: + +```bash +genkit flow:run multiSteps '"sample input"' +``` + +#### Expected Output: + +```text +42 +``` + +--- + +### LargeSteps Flow + +Processes large chunks of data across multiple steps. + +#### Command: + +```bash +genkit flow:run largeSteps +``` + +#### Expected Output: + +```text +"Finished processing large data." +``` + +--- + +## Running the Developer UI + +To visualize flows in the Developer UI, start the server: + +```bash +genkit start +``` + +Open your browser and navigate to `http://localhost:4000`. + +--- + +## Notes + +- This project requires `Node.js` version 20 or later. +- All flows are designed for demonstration and testing purposes. diff --git a/js/testapps/flow-sample1/src/index.ts b/js/testapps/flow-sample1/src/index.ts index 4fb2bfced..d55fd2a08 100644 --- a/js/testapps/flow-sample1/src/index.ts +++ b/js/testapps/flow-sample1/src/index.ts @@ -18,20 +18,35 @@ import { genkit, run, z } from 'genkit'; const ai = genkit({}); +ai.startFlowServer(); // Start the server early +console.log('Flow server initialized.'); + /** - * To run this flow; - * genkit flow:run basic "\"hello\"" + * @flow Basic + * @description A simple flow that processes a string input. + * @param {string} subject - The subject string to process. + * @returns {Promise} - A processed string output. + * + * @example + * Input: "hello" + * Output: "foo: subject: hello" */ export const basic = ai.defineFlow({ name: 'basic' }, async (subject) => { - const foo = await run('call-llm', async () => { - return `subject: ${subject}`; - }); - - return await run('call-llm1', async () => { - return `foo: ${foo}`; - }); + const foo = await run('call-llm', async () => `subject: ${subject}`); + return await run('call-llm1', async () => `foo: ${foo}`); }); +console.log('Basic flow registered.'); + +/** + * @flow Parent + * @description Runs the basic flow and returns its output as a JSON string. + * @returns {Promise} - JSON string of the basic flow's result. + * + * @example + * Input: (none) + * Output: '{"foo":"subject: foo"}' + */ export const parent = ai.defineFlow( { name: 'parent', outputSchema: z.string() }, async () => { @@ -39,7 +54,18 @@ export const parent = ai.defineFlow( } ); -// genkit flow:run streamy 5 -s +/** + * @flow Streamy + * @description Streams numbers up to a given count. + * @param {number} count - The number of iterations to stream. + * @param {function} streamingCallback - Callback function to handle each streamed value. + * @returns {Promise} - A summary string after streaming. + * + * @example + * Input: 3 + * Stream: {count: 0}, {count: 1}, {count: 2} + * Output: "done: 3, streamed: 3 times" + */ export const streamy = ai.defineStreamingFlow( { name: 'streamy', @@ -48,18 +74,25 @@ export const streamy = ai.defineStreamingFlow( streamSchema: z.object({ count: z.number() }), }, async (count, streamingCallback) => { - let i = 0; - if (streamingCallback) { - for (; i < count; i++) { - await new Promise((r) => setTimeout(r, 1000)); - streamingCallback({ count: i }); - } + for (let i = 0; i < count; i++) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + streamingCallback?.({ count: i }); } - return `done: ${count}, streamed: ${i} times`; + return `done: ${count}, streamed: ${count} times`; } ); -// genkit flow:run streamy 5 -s +/** + * @flow StreamyThrowy + * @description Streams numbers and throws an error at iteration 3. + * @param {number} count - The number of iterations to stream. + * @param {function} streamingCallback - Callback function to handle each streamed value. + * @returns {Promise} - A summary string after streaming. + * + * @example + * Input: 5 + * Stream: {count: 0}, {count: 1}, {count: 2}, Error: "whoops" + */ export const streamyThrowy = ai.defineStreamingFlow( { name: 'streamyThrowy', @@ -68,132 +101,63 @@ export const streamyThrowy = ai.defineStreamingFlow( streamSchema: z.object({ count: z.number() }), }, async (count, streamingCallback) => { - let i = 0; - if (streamingCallback) { - for (; i < count; i++) { - if (i == 3) { - throw new Error('whoops'); - } - await new Promise((r) => setTimeout(r, 1000)); - streamingCallback({ count: i }); - } + for (let i = 0; i < count; i++) { + if (i === 3) throw new Error('whoops'); + await new Promise((resolve) => setTimeout(resolve, 1000)); + streamingCallback?.({ count: i }); } - return `done: ${count}, streamed: ${i} times`; + return `done: ${count}, streamed: ${count} times`; } ); /** - * To run this flow; - * genkit flow:run throwy "\"hello\"" + * @flow Throwy + * @description Demonstrates error handling by throwing an error based on input. + * @param {string} subject - The subject to process. + * @returns {Promise} - Processed output unless an error is thrown. + * + * @example + * Input: "error" + * Output: Error: "error" */ export const throwy = ai.defineFlow( { name: 'throwy', inputSchema: z.string(), outputSchema: z.string() }, async (subject) => { - const foo = await run('call-llm', async () => { - return `subject: ${subject}`; - }); - if (subject) { - throw new Error(subject); - } - return await run('call-llm', async () => { - return `foo: ${foo}`; - }); + const foo = await run('call-llm', async () => `subject: ${subject}`); + if (subject) throw new Error(subject); + return foo; } ); /** - * To run this flow; - * genkit flow:run throwy2 "\"hello\"" + * @flow MultiSteps + * @description Demonstrates a flow with multiple steps. + * @param {string} input - Input string for processing. + * @returns {Promise} - Always returns 42 after multiple processing steps. + * + * @example + * Input: "world" + * Output: Logs intermediate steps, final return value 42. */ -export const throwy2 = ai.defineFlow( - { name: 'throwy2', inputSchema: z.string(), outputSchema: z.string() }, - async (subject) => { - const foo = await run('call-llm', async () => { - if (subject) { - throw new Error(subject); - } - return `subject: ${subject}`; - }); - return await run('call-llm', async () => { - return `foo: ${foo}`; - }); - } -); - -export const flowMultiStepCaughtError = ai.defineFlow( - { name: 'flowMultiStepCaughtError' }, - async (input) => { - let i = 1; - - const result1 = await run('step1', async () => { - return `${input} ${i++},`; - }); - - let result2 = ''; - try { - result2 = await run('step2', async () => { - if (result1) { - throw new Error('Got an error!'); - } - return `${result1} ${i++},`; - }); - } catch (e) {} - - return await run('step3', async () => { - return `${result2} ${i++}`; - }); - } -); - export const multiSteps = ai.defineFlow( { name: 'multiSteps', inputSchema: z.string(), outputSchema: z.number() }, async (input) => { - const out1 = await run('step1', async () => { - return `Hello, ${input}! step 1`; - }); - await run('step1', async () => { - return `Hello2222, ${input}! step 1`; - }); - const out2 = await run('step2', out1, async () => { - return out1 + ' Faf '; - }); - const out3 = await run('step3-array', async () => { - return [out2, out2]; - }); - const out4 = await run('step4-num', async () => { - return out3.join('-()-'); - }); + const out1 = await run('step1', async () => `Hello, ${input}!`); + const out2 = await run('step2', async () => `${out1} Step 2`); + const out3 = await run('step3-array', async () => [out2, out2]); + const out4 = await run('step4-num', async () => out3.join('-')); + console.log(out4); return 42; } ); -export const largeSteps = ai.defineFlow({ name: 'largeSteps' }, async () => { - await run('large-step1', async () => { - return generateString(100_000); - }); - await run('large-step2', async () => { - return generateString(800_000); - }); - await run('large-step3', async () => { - return generateString(900_000); - }); - await run('large-step4', async () => { - return generateString(999_000); - }); - return 'something...'; -}); - -const loremIpsum = [ - 'lorem', - 'ipsum', - 'dolor', - 'sit', - 'amet', - 'consectetur', - 'adipiscing', - 'elit', -]; +/** + * Utility function to generate a long string. + * @param {number} length - Length of the string to generate. + * @returns {string} - Generated string. + */ function generateString(length: number) { + const loremIpsum = ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur']; let str = ''; while (str.length < length) { str += loremIpsum[Math.floor(Math.random() * loremIpsum.length)] + ' '; @@ -201,4 +165,4 @@ function generateString(length: number) { return str.substring(0, length); } -ai.startFlowServer(); +console.log('Flow server state:', ai);