@ohos-ports/genkit 1.42.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +434 -0
  3. package/lib/beta.d.mts +18 -0
  4. package/lib/beta.d.ts +18 -0
  5. package/lib/beta.js +88 -0
  6. package/lib/beta.js.map +1 -0
  7. package/lib/beta.mjs +64 -0
  8. package/lib/beta.mjs.map +1 -0
  9. package/lib/client/agent.d.mts +52 -0
  10. package/lib/client/agent.d.ts +52 -0
  11. package/lib/client/agent.js +90 -0
  12. package/lib/client/agent.js.map +1 -0
  13. package/lib/client/agent.mjs +68 -0
  14. package/lib/client/agent.mjs.map +1 -0
  15. package/lib/client/client.d.mts +80 -0
  16. package/lib/client/client.d.ts +80 -0
  17. package/lib/client/client.js +164 -0
  18. package/lib/client/client.js.map +1 -0
  19. package/lib/client/client.mjs +139 -0
  20. package/lib/client/client.mjs.map +1 -0
  21. package/lib/client/index.d.mts +6 -0
  22. package/lib/client/index.d.ts +6 -0
  23. package/lib/client/index.js +55 -0
  24. package/lib/client/index.js.map +1 -0
  25. package/lib/client/index.mjs +31 -0
  26. package/lib/client/index.mjs.map +1 -0
  27. package/lib/client/types.d.mts +17 -0
  28. package/lib/client/types.d.ts +17 -0
  29. package/lib/client/types.js +17 -0
  30. package/lib/client/types.js.map +1 -0
  31. package/lib/client/types.mjs +1 -0
  32. package/lib/client/types.mjs.map +1 -0
  33. package/lib/common.d.mts +3 -0
  34. package/lib/common.d.ts +3 -0
  35. package/lib/common.js +151 -0
  36. package/lib/common.js.map +1 -0
  37. package/lib/common.mjs +128 -0
  38. package/lib/common.mjs.map +1 -0
  39. package/lib/context.d.mts +1 -0
  40. package/lib/context.d.ts +1 -0
  41. package/lib/context.js +50 -0
  42. package/lib/context.js.map +1 -0
  43. package/lib/context.mjs +28 -0
  44. package/lib/context.mjs.map +1 -0
  45. package/lib/embedder.d.mts +1 -0
  46. package/lib/embedder.d.ts +1 -0
  47. package/lib/embedder.js +48 -0
  48. package/lib/embedder.js.map +1 -0
  49. package/lib/embedder.mjs +26 -0
  50. package/lib/embedder.mjs.map +1 -0
  51. package/lib/evaluator.d.mts +1 -0
  52. package/lib/evaluator.d.ts +1 -0
  53. package/lib/evaluator.js +60 -0
  54. package/lib/evaluator.js.map +1 -0
  55. package/lib/evaluator.mjs +38 -0
  56. package/lib/evaluator.mjs.map +1 -0
  57. package/lib/extract.d.mts +1 -0
  58. package/lib/extract.d.ts +1 -0
  59. package/lib/extract.js +48 -0
  60. package/lib/extract.js.map +1 -0
  61. package/lib/extract.mjs +23 -0
  62. package/lib/extract.mjs.map +1 -0
  63. package/lib/formats.d.mts +1 -0
  64. package/lib/formats.d.ts +1 -0
  65. package/lib/formats.js +34 -0
  66. package/lib/formats.js.map +1 -0
  67. package/lib/formats.mjs +18 -0
  68. package/lib/formats.mjs.map +1 -0
  69. package/lib/genkit-beta.d.mts +183 -0
  70. package/lib/genkit-beta.d.ts +183 -0
  71. package/lib/genkit-beta.js +178 -0
  72. package/lib/genkit-beta.js.map +1 -0
  73. package/lib/genkit-beta.mjs +159 -0
  74. package/lib/genkit-beta.mjs.map +1 -0
  75. package/lib/genkit.d.mts +10 -0
  76. package/lib/genkit.d.ts +10 -0
  77. package/lib/genkit.js +471 -0
  78. package/lib/genkit.js.map +1 -0
  79. package/lib/genkit.mjs +489 -0
  80. package/lib/genkit.mjs.map +1 -0
  81. package/lib/index-BSkuxdwl.d.mts +311 -0
  82. package/lib/index-BSkuxdwl.d.ts +311 -0
  83. package/lib/index.d.mts +10 -0
  84. package/lib/index.d.ts +10 -0
  85. package/lib/index.js +51 -0
  86. package/lib/index.js.map +1 -0
  87. package/lib/index.mjs +24 -0
  88. package/lib/index.mjs.map +1 -0
  89. package/lib/logging.d.mts +1 -0
  90. package/lib/logging.d.ts +1 -0
  91. package/lib/logging.js +46 -0
  92. package/lib/logging.js.map +1 -0
  93. package/lib/logging.mjs +22 -0
  94. package/lib/logging.mjs.map +1 -0
  95. package/lib/middleware.d.mts +1 -0
  96. package/lib/middleware.d.ts +1 -0
  97. package/lib/middleware.js +56 -0
  98. package/lib/middleware.js.map +1 -0
  99. package/lib/middleware.mjs +34 -0
  100. package/lib/middleware.mjs.map +1 -0
  101. package/lib/model.d.mts +1 -0
  102. package/lib/model.d.ts +1 -0
  103. package/lib/model.js +92 -0
  104. package/lib/model.js.map +1 -0
  105. package/lib/model.mjs +70 -0
  106. package/lib/model.mjs.map +1 -0
  107. package/lib/plugin.d.mts +10 -0
  108. package/lib/plugin.d.ts +10 -0
  109. package/lib/plugin.js +143 -0
  110. package/lib/plugin.js.map +1 -0
  111. package/lib/plugin.mjs +113 -0
  112. package/lib/plugin.mjs.map +1 -0
  113. package/lib/registry.d.mts +1 -0
  114. package/lib/registry.d.ts +1 -0
  115. package/lib/registry.js +46 -0
  116. package/lib/registry.js.map +1 -0
  117. package/lib/registry.mjs +24 -0
  118. package/lib/registry.mjs.map +1 -0
  119. package/lib/reranker.d.mts +1 -0
  120. package/lib/reranker.d.ts +1 -0
  121. package/lib/reranker.js +54 -0
  122. package/lib/reranker.js.map +1 -0
  123. package/lib/reranker.mjs +32 -0
  124. package/lib/reranker.mjs.map +1 -0
  125. package/lib/retriever.d.mts +1 -0
  126. package/lib/retriever.d.ts +1 -0
  127. package/lib/retriever.js +58 -0
  128. package/lib/retriever.js.map +1 -0
  129. package/lib/retriever.mjs +36 -0
  130. package/lib/retriever.mjs.map +1 -0
  131. package/lib/schema.d.mts +1 -0
  132. package/lib/schema.d.ts +1 -0
  133. package/lib/schema.js +54 -0
  134. package/lib/schema.js.map +1 -0
  135. package/lib/schema.mjs +32 -0
  136. package/lib/schema.mjs.map +1 -0
  137. package/lib/testing.d.mts +1 -0
  138. package/lib/testing.d.ts +1 -0
  139. package/lib/testing.js +50 -0
  140. package/lib/testing.js.map +1 -0
  141. package/lib/testing.mjs +28 -0
  142. package/lib/testing.mjs.map +1 -0
  143. package/lib/tool.d.mts +1 -0
  144. package/lib/tool.d.ts +1 -0
  145. package/lib/tool.js +54 -0
  146. package/lib/tool.js.map +1 -0
  147. package/lib/tool.mjs +32 -0
  148. package/lib/tool.mjs.map +1 -0
  149. package/lib/tracing.d.mts +1 -0
  150. package/lib/tracing.d.ts +1 -0
  151. package/lib/tracing.js +82 -0
  152. package/lib/tracing.js.map +1 -0
  153. package/lib/tracing.mjs +60 -0
  154. package/lib/tracing.mjs.map +1 -0
  155. package/package.json +233 -0
  156. package/src/beta.ts +85 -0
  157. package/src/client/agent.ts +160 -0
  158. package/src/client/client.ts +237 -0
  159. package/src/client/index.ts +72 -0
  160. package/src/client/types.ts +41 -0
  161. package/src/common.ts +175 -0
  162. package/src/context.ts +37 -0
  163. package/src/embedder.ts +41 -0
  164. package/src/evaluator.ts +50 -0
  165. package/src/extract.ts +30 -0
  166. package/src/formats.ts +30 -0
  167. package/src/genkit-beta.ts +275 -0
  168. package/src/genkit.ts +818 -0
  169. package/src/index.ts +29 -0
  170. package/src/logging.ts +30 -0
  171. package/src/middleware.ts +38 -0
  172. package/src/model.ts +87 -0
  173. package/src/plugin.ts +172 -0
  174. package/src/registry.ts +36 -0
  175. package/src/reranker.ts +42 -0
  176. package/src/retriever.ts +55 -0
  177. package/src/schema.ts +40 -0
  178. package/src/testing.ts +47 -0
  179. package/src/tool.ts +40 -0
  180. package/src/tracing.ts +55 -0
  181. package/tests/agent_client_test.ts +497 -0
  182. package/tests/embed_test.ts +142 -0
  183. package/tests/evaluate_test.ts +52 -0
  184. package/tests/flow_test.ts +94 -0
  185. package/tests/formats_test.ts +168 -0
  186. package/tests/generate_test.ts +1631 -0
  187. package/tests/genkit_test.ts +33 -0
  188. package/tests/helpers.ts +176 -0
  189. package/tests/mock-model_test.ts +490 -0
  190. package/tests/plugins_test.ts +259 -0
  191. package/tests/prompts/badSchemaRef.prompt +9 -0
  192. package/tests/prompts/chat_preamble.prompt +5 -0
  193. package/tests/prompts/kitchensink.prompt +25 -0
  194. package/tests/prompts/output.prompt +11 -0
  195. package/tests/prompts/schemaRef.prompt +9 -0
  196. package/tests/prompts/sub/test.prompt +5 -0
  197. package/tests/prompts/test.prompt +5 -0
  198. package/tests/prompts/test.variant.prompt +8 -0
  199. package/tests/prompts/toolPrompt.prompt +6 -0
  200. package/tests/prompts_test.ts +1442 -0
  201. package/tests/session_test.ts +17 -0
  202. package/typedoc.json +22 -0
@@ -0,0 +1,1442 @@
1
+ /**
2
+ * Copyright 2024 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 { modelRef, type ModelMiddleware } from '@genkit-ai/ai/model';
18
+ import { stripUndefinedProps } from '@genkit-ai/core';
19
+ import * as assert from 'assert';
20
+ import { beforeEach, describe, it } from 'node:test';
21
+ import { genkit, z, type GenkitBeta } from '../src/beta';
22
+ import type { PromptAction } from '../src/index';
23
+ import {
24
+ defineEchoModel,
25
+ defineProgrammableModel,
26
+ defineStaticResponseModel,
27
+ type ProgrammableModel,
28
+ } from './helpers';
29
+
30
+ const wrapRequest: ModelMiddleware = async (req, next) => {
31
+ return next({
32
+ ...req,
33
+ messages: [
34
+ {
35
+ role: 'user',
36
+ content: [
37
+ {
38
+ text:
39
+ '(' +
40
+ req.messages
41
+ .map((m) => m.content.map((c) => c.text).join())
42
+ .join() +
43
+ ')',
44
+ },
45
+ ],
46
+ },
47
+ ],
48
+ });
49
+ };
50
+ const wrapResponse: ModelMiddleware = async (req, next) => {
51
+ const res = await next(req);
52
+ return {
53
+ message: {
54
+ role: 'model',
55
+ content: [
56
+ {
57
+ text: '[' + res.message!.content.map((c) => c.text).join() + ']',
58
+ },
59
+ ],
60
+ },
61
+ finishReason: res.finishReason,
62
+ };
63
+ };
64
+
65
+ describe('definePrompt', () => {
66
+ let ai: GenkitBeta;
67
+
68
+ beforeEach(() => {
69
+ ai = genkit({
70
+ model: 'echoModel',
71
+ });
72
+ defineEchoModel(ai);
73
+ });
74
+
75
+ it('should define the prompt', async () => {
76
+ const prompt = ai.definePrompt({
77
+ name: 'hi',
78
+ metadata: { foo: 'bar' },
79
+ input: {
80
+ schema: z.object({
81
+ name: z.string(),
82
+ }),
83
+ },
84
+ messages: async (input) => {
85
+ return [
86
+ {
87
+ role: 'user',
88
+ content: [{ text: `hi ${input.name}` }],
89
+ },
90
+ ];
91
+ },
92
+ });
93
+
94
+ assert.deepStrictEqual(prompt.ref, {
95
+ name: 'hi',
96
+ metadata: { foo: 'bar' },
97
+ });
98
+
99
+ const lookedUpPrompt = ai.prompt('hi');
100
+ // This is a known limitation -- prompt lookup is async under the hood,
101
+ // so we can't actually get the metadata...
102
+ assert.deepStrictEqual(lookedUpPrompt.ref, { name: 'hi' }); // ideally metadata should be: { foo: 'bar' }
103
+ });
104
+
105
+ it('should apply middleware to a prompt call', async () => {
106
+ const prompt = ai.definePrompt({
107
+ name: 'hi',
108
+ input: {
109
+ schema: z.object({
110
+ name: z.string(),
111
+ }),
112
+ },
113
+ messages: async (input) => {
114
+ return [
115
+ {
116
+ role: 'user',
117
+ content: [{ text: `hi ${input.name}` }],
118
+ },
119
+ ];
120
+ },
121
+ });
122
+
123
+ const response = await prompt(
124
+ { name: 'Genkit' },
125
+ { use: [wrapRequest, wrapResponse] }
126
+ );
127
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
128
+ });
129
+
130
+ it('should apply middleware configured on a prompt', async () => {
131
+ const prompt = ai.definePrompt({
132
+ name: 'hi',
133
+ input: {
134
+ schema: z.object({
135
+ name: z.string(),
136
+ }),
137
+ },
138
+ use: [wrapRequest, wrapResponse],
139
+ messages: async (input) => {
140
+ return [
141
+ {
142
+ role: 'user',
143
+ content: [{ text: `hi ${input.name}` }],
144
+ },
145
+ ];
146
+ },
147
+ });
148
+
149
+ const response = await prompt({ name: 'Genkit' });
150
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
151
+ });
152
+
153
+ it('should apply middleware to a prompt call on a looked up prompt', async () => {
154
+ ai.definePrompt({
155
+ name: 'hi',
156
+ input: {
157
+ schema: z.object({
158
+ name: z.string(),
159
+ }),
160
+ },
161
+ use: [wrapRequest, wrapResponse],
162
+ messages: async (input) => {
163
+ return [
164
+ {
165
+ role: 'user',
166
+ content: [{ text: `hi ${input.name}` }],
167
+ },
168
+ ];
169
+ },
170
+ });
171
+
172
+ const prompt = ai.prompt('hi');
173
+
174
+ const response = await prompt({ name: 'Genkit' });
175
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
176
+ });
177
+
178
+ it('should apply middleware configured on a prompt on a looked up prompt', async () => {
179
+ ai.definePrompt({
180
+ name: 'hi',
181
+ input: {
182
+ schema: z.object({
183
+ name: z.string(),
184
+ }),
185
+ },
186
+ messages: async (input) => {
187
+ return [
188
+ {
189
+ role: 'user',
190
+ content: [{ text: `hi ${input.name}` }],
191
+ },
192
+ ];
193
+ },
194
+ });
195
+
196
+ const prompt = ai.prompt('hi');
197
+
198
+ const response = await prompt(
199
+ { name: 'Genkit' },
200
+ { use: [wrapRequest, wrapResponse] }
201
+ );
202
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
203
+ });
204
+ });
205
+
206
+ describe('definePrompt', () => {
207
+ describe('default model', () => {
208
+ let ai: GenkitBeta;
209
+
210
+ beforeEach(() => {
211
+ ai = genkit({
212
+ model: 'echoModel',
213
+ });
214
+ defineEchoModel(ai);
215
+ });
216
+
217
+ it('calls dotprompt with default model', async () => {
218
+ const hi = ai.definePrompt({
219
+ name: 'hi',
220
+ input: {
221
+ schema: z.object({
222
+ name: z.string(),
223
+ }),
224
+ },
225
+ prompt: 'hi {{ name }}',
226
+ });
227
+
228
+ const response = await hi({ name: 'Genkit' });
229
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
230
+ });
231
+
232
+ it('calls dotprompt with default model with config', async () => {
233
+ const hi = ai.definePrompt({
234
+ name: 'hi',
235
+ input: {
236
+ schema: z.object({
237
+ name: z.string(),
238
+ }),
239
+ },
240
+ config: {
241
+ temperature: 11,
242
+ },
243
+ prompt: 'hi {{ name }}',
244
+ });
245
+
246
+ const response = await hi({ name: 'Genkit' });
247
+ assert.strictEqual(
248
+ response.text,
249
+ 'Echo: hi Genkit; config: {"temperature":11}'
250
+ );
251
+ });
252
+
253
+ it('calls dotprompt with default model via retrieved prompt', async () => {
254
+ ai.definePrompt({
255
+ name: 'hi',
256
+ input: {
257
+ schema: z.object({
258
+ name: z.string(),
259
+ }),
260
+ },
261
+ prompt: 'hi {{ name }}',
262
+ });
263
+
264
+ const hi = ai.prompt('hi');
265
+
266
+ const response = await hi({ name: 'Genkit' });
267
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
268
+ });
269
+
270
+ it('should apply middleware to a prompt call', async () => {
271
+ const prompt = ai.definePrompt({
272
+ name: 'hi',
273
+ input: {
274
+ schema: z.object({
275
+ name: z.string(),
276
+ }),
277
+ },
278
+ prompt: 'hi {{ name }}',
279
+ });
280
+
281
+ const response = await prompt(
282
+ { name: 'Genkit' },
283
+ { use: [wrapRequest, wrapResponse] }
284
+ );
285
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
286
+ });
287
+
288
+ it('should apply middleware configured on a prompt', async () => {
289
+ const prompt = ai.definePrompt({
290
+ name: 'hi',
291
+ input: {
292
+ schema: z.object({
293
+ name: z.string(),
294
+ }),
295
+ },
296
+ use: [wrapRequest, wrapResponse],
297
+ prompt: 'hi {{ name }}',
298
+ });
299
+
300
+ const response = await prompt({ name: 'Genkit' });
301
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
302
+ });
303
+
304
+ it('should apply middleware to a prompt call on a looked up prompt', async () => {
305
+ ai.definePrompt({
306
+ name: 'hi',
307
+ input: {
308
+ schema: z.object({
309
+ name: z.string(),
310
+ }),
311
+ },
312
+ use: [wrapRequest, wrapResponse],
313
+ prompt: 'hi {{ name }}',
314
+ });
315
+
316
+ const prompt = ai.prompt('hi');
317
+
318
+ const response = await prompt({ name: 'Genkit' });
319
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
320
+ });
321
+
322
+ it('should apply middleware configured on a prompt on a looked up prompt', async () => {
323
+ ai.definePrompt({
324
+ name: 'hi',
325
+ input: {
326
+ schema: z.object({
327
+ name: z.string(),
328
+ }),
329
+ },
330
+ prompt: 'hi {{ name }}',
331
+ });
332
+
333
+ const prompt = ai.prompt('hi');
334
+
335
+ const response = await prompt(
336
+ { name: 'Genkit' },
337
+ { use: [wrapRequest, wrapResponse] }
338
+ );
339
+ assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]');
340
+ });
341
+ });
342
+
343
+ describe('default model ref', () => {
344
+ let ai: GenkitBeta;
345
+
346
+ beforeEach(() => {
347
+ ai = genkit({
348
+ model: modelRef({
349
+ name: 'echoModel',
350
+ }),
351
+ promptDir: './tests/prompts',
352
+ });
353
+ defineEchoModel(ai);
354
+ });
355
+
356
+ it('calls dotprompt with default model', async () => {
357
+ const hi = ai.definePrompt({
358
+ name: 'hi',
359
+ input: {
360
+ schema: z.object({
361
+ name: z.string(),
362
+ }),
363
+ },
364
+ prompt: 'hi {{ name }}',
365
+ });
366
+
367
+ const response = await hi({ name: 'Genkit' });
368
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
369
+ });
370
+
371
+ it('infers output schema', async () => {
372
+ const Foo = z.object({
373
+ bar: z.string(),
374
+ });
375
+ const model = defineStaticResponseModel(ai, {
376
+ role: 'model',
377
+ content: [
378
+ {
379
+ text: '```json\n{bar: "baz"}\n```',
380
+ },
381
+ ],
382
+ });
383
+ const hi = ai.definePrompt({
384
+ name: 'hi',
385
+ model,
386
+ input: {
387
+ schema: z.object({
388
+ name: z.string(),
389
+ }),
390
+ },
391
+ output: {
392
+ format: 'json',
393
+ schema: Foo,
394
+ },
395
+ prompt: 'hi {{ name }}',
396
+ });
397
+
398
+ const response = await hi({ name: 'Genkit' });
399
+ const foo = response.output;
400
+ assert.deepStrictEqual(foo, { bar: 'baz' });
401
+ });
402
+
403
+ it('defaults to json format', async () => {
404
+ const Foo = z.object({
405
+ bar: z.string(),
406
+ });
407
+ const model = defineStaticResponseModel(ai, {
408
+ role: 'model',
409
+ content: [
410
+ {
411
+ text: '```json\n{bar: "baz"}\n```',
412
+ },
413
+ ],
414
+ });
415
+ const hi = ai.definePrompt({
416
+ name: 'hi',
417
+ model,
418
+ input: {
419
+ schema: z.object({
420
+ name: z.string(),
421
+ }),
422
+ },
423
+ output: {
424
+ // no format specified
425
+ schema: Foo,
426
+ },
427
+ prompt: 'hi {{ name }}',
428
+ });
429
+
430
+ const response = await hi({ name: 'Genkit' });
431
+ const foo = response.output;
432
+ assert.deepStrictEqual(foo, { bar: 'baz' });
433
+ });
434
+
435
+ it('defaults to json format from a loaded prompt', async () => {
436
+ defineStaticResponseModel(ai, {
437
+ role: 'model',
438
+ content: [
439
+ {
440
+ text: '```json\n{bar: "baz"}\n```',
441
+ },
442
+ ],
443
+ });
444
+ const hi = ai.prompt('output');
445
+
446
+ const response = await hi({ name: 'Genkit' });
447
+ const foo = response.output;
448
+ assert.deepStrictEqual(stripUndefinedProps(response.request), {
449
+ config: {},
450
+ messages: [
451
+ {
452
+ content: [
453
+ {
454
+ text: 'Hi Genkit',
455
+ },
456
+ ],
457
+ role: 'user',
458
+ },
459
+ ],
460
+ output: {
461
+ constrained: true,
462
+ contentType: 'application/json',
463
+ format: 'json',
464
+ schema: {
465
+ additionalProperties: false,
466
+ properties: {
467
+ bar: {
468
+ type: 'string',
469
+ },
470
+ },
471
+ required: ['bar'],
472
+ type: 'object',
473
+ },
474
+ },
475
+ tools: [],
476
+ });
477
+
478
+ assert.deepStrictEqual(foo, { bar: 'baz' });
479
+ });
480
+
481
+ it('streams dotprompt with default model', async () => {
482
+ const hi = ai.definePrompt({
483
+ name: 'hi',
484
+ input: {
485
+ schema: z.object({
486
+ name: z.string(),
487
+ }),
488
+ },
489
+ config: {
490
+ temperature: 11,
491
+ },
492
+ prompt: 'hi {{ name }}',
493
+ });
494
+
495
+ const { response, stream } = hi.stream({ name: 'Genkit' });
496
+ const chunks: string[] = [];
497
+ for await (const chunk of stream) {
498
+ chunks.push(chunk.text);
499
+ }
500
+ const responseText = (await response).text;
501
+
502
+ assert.strictEqual(
503
+ responseText,
504
+ 'Echo: hi Genkit; config: {"temperature":11}'
505
+ );
506
+ assert.deepStrictEqual(chunks, ['3', '2', '1']);
507
+ });
508
+
509
+ it('calls dotprompt with default model via retrieved prompt', async () => {
510
+ ai.definePrompt({
511
+ name: 'hi',
512
+ input: {
513
+ schema: z.object({
514
+ name: z.string(),
515
+ }),
516
+ },
517
+ prompt: 'hi {{ name }}',
518
+ });
519
+
520
+ const hi = ai.prompt('hi');
521
+
522
+ const response = await hi({ name: 'Genkit' });
523
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
524
+
525
+ const { stream } = hi.stream({ name: 'Genkit' });
526
+ const chunks: string[] = [];
527
+ for await (const chunk of stream) {
528
+ chunks.push(chunk.text);
529
+ }
530
+ assert.deepStrictEqual(chunks, ['3', '2', '1']);
531
+ });
532
+ });
533
+
534
+ describe('explicit model', () => {
535
+ let ai: GenkitBeta;
536
+
537
+ beforeEach(() => {
538
+ ai = genkit({});
539
+ defineEchoModel(ai);
540
+ });
541
+
542
+ it('calls dotprompt with default model', async () => {
543
+ const hi = ai.definePrompt({
544
+ name: 'hi',
545
+ model: 'echoModel',
546
+ input: {
547
+ schema: z.object({
548
+ name: z.string(),
549
+ }),
550
+ },
551
+ prompt: 'hi {{ name }}',
552
+ });
553
+
554
+ const response = await hi({ name: 'Genkit' });
555
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
556
+ });
557
+
558
+ it('calls dotprompt with history and places it before user message', async () => {
559
+ const hi = ai.definePrompt({
560
+ name: 'hi',
561
+ model: 'echoModel',
562
+ input: {
563
+ schema: z.object({
564
+ name: z.string(),
565
+ }),
566
+ },
567
+ prompt: 'hi {{ name }}',
568
+ });
569
+
570
+ const response = await hi(
571
+ { name: 'Genkit' },
572
+ {
573
+ messages: [
574
+ { role: 'user', content: [{ text: 'hi' }] },
575
+ { role: 'model', content: [{ text: 'bye' }] },
576
+ ],
577
+ }
578
+ );
579
+ assert.deepStrictEqual(response.messages, [
580
+ {
581
+ role: 'user',
582
+ content: [{ text: 'hi' }],
583
+ },
584
+ {
585
+ role: 'model',
586
+ content: [{ text: 'bye' }],
587
+ },
588
+ {
589
+ role: 'user',
590
+ content: [{ text: 'hi Genkit' }],
591
+ },
592
+ {
593
+ role: 'model',
594
+ content: [
595
+ { text: 'Echo: hi,bye,hi Genkit' },
596
+ { text: '; config: {}' },
597
+ ],
598
+ },
599
+ ]);
600
+ });
601
+
602
+ it('streams dotprompt with history and places it before user message', async () => {
603
+ const hi = ai.definePrompt({
604
+ name: 'hi',
605
+ model: 'echoModel',
606
+ input: {
607
+ schema: z.object({
608
+ name: z.string(),
609
+ }),
610
+ },
611
+ prompt: 'hi {{ name }}',
612
+ });
613
+
614
+ const response = await hi.stream(
615
+ { name: 'Genkit' },
616
+ {
617
+ messages: [
618
+ { role: 'user', content: [{ text: 'hi' }] },
619
+ { role: 'model', content: [{ text: 'bye' }] },
620
+ ],
621
+ }
622
+ );
623
+ assert.deepStrictEqual((await response.response).messages, [
624
+ {
625
+ role: 'user',
626
+ content: [{ text: 'hi' }],
627
+ },
628
+ {
629
+ role: 'model',
630
+ content: [{ text: 'bye' }],
631
+ },
632
+ {
633
+ role: 'user',
634
+ content: [{ text: 'hi Genkit' }],
635
+ },
636
+ {
637
+ role: 'model',
638
+ content: [
639
+ { text: 'Echo: hi,bye,hi Genkit' },
640
+ { text: '; config: {}' },
641
+ ],
642
+ },
643
+ ]);
644
+ });
645
+
646
+ it('calls dotprompt with default model with config', async () => {
647
+ const hi = ai.definePrompt({
648
+ name: 'hi',
649
+ model: 'echoModel',
650
+ input: {
651
+ schema: z.object({
652
+ name: z.string(),
653
+ }),
654
+ },
655
+ config: {
656
+ temperature: 11,
657
+ },
658
+ prompt: 'hi {{ name }}',
659
+ });
660
+
661
+ const response = await hi({ name: 'Genkit' });
662
+ assert.strictEqual(
663
+ response.text,
664
+ 'Echo: hi Genkit; config: {"temperature":11}'
665
+ );
666
+ });
667
+
668
+ it('rejects on invalid model', async () => {
669
+ const hi = ai.definePrompt({
670
+ name: 'hi',
671
+ model: 'modelThatDoesNotExist',
672
+ input: {
673
+ schema: z.object({
674
+ name: z.string(),
675
+ }),
676
+ },
677
+ prompt: 'hi {{ name }}',
678
+ });
679
+
680
+ const response = hi({ name: 'Genkit' });
681
+ await assert.rejects(response, {
682
+ message: "NOT_FOUND: Model 'modelThatDoesNotExist' not found",
683
+ });
684
+ });
685
+ });
686
+
687
+ describe('render', () => {
688
+ let ai: GenkitBeta;
689
+
690
+ beforeEach(() => {
691
+ ai = genkit({});
692
+ defineEchoModel(ai);
693
+ });
694
+
695
+ it('renders dotprompt messages', async () => {
696
+ const hi = ai.definePrompt({
697
+ name: 'hi',
698
+ input: {
699
+ schema: z.object({
700
+ name: z.string(),
701
+ }),
702
+ },
703
+ prompt: 'hi {{ name }}',
704
+ });
705
+
706
+ const response = await hi.render({ name: 'Genkit' });
707
+ delete response.model; // ignore
708
+ assert.deepStrictEqual(response, {
709
+ messages: [{ content: [{ text: 'hi Genkit' }], role: 'user' }],
710
+ });
711
+ });
712
+ });
713
+ });
714
+
715
+ describe('definePrompt', () => {
716
+ describe('default model', () => {
717
+ let ai: GenkitBeta;
718
+
719
+ beforeEach(() => {
720
+ ai = genkit({
721
+ model: 'echoModel',
722
+ });
723
+ defineEchoModel(ai);
724
+ });
725
+
726
+ it('calls prompt with default model', async () => {
727
+ const hi = ai.definePrompt({
728
+ name: 'hi',
729
+ input: {
730
+ schema: z.object({
731
+ name: z.string(),
732
+ }),
733
+ },
734
+ messages: async (input) => {
735
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
736
+ },
737
+ });
738
+
739
+ const response = await hi({ name: 'Genkit' });
740
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
741
+ });
742
+
743
+ it('calls legacy prompt with default model', async () => {
744
+ const hi = ai.definePrompt(
745
+ {
746
+ name: 'hi',
747
+ input: {
748
+ schema: z.object({
749
+ name: z.string(),
750
+ }),
751
+ },
752
+ },
753
+ async (input) => {
754
+ return {
755
+ messages: [
756
+ { role: 'user', content: [{ text: `hi ${input.name}` }] },
757
+ ],
758
+ };
759
+ }
760
+ );
761
+
762
+ const response = await hi({ name: 'Genkit' });
763
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
764
+ });
765
+
766
+ it('calls legacy prompt with default model', async () => {
767
+ const hi = ai.definePrompt(
768
+ {
769
+ name: 'hi',
770
+ input: {
771
+ schema: z.object({
772
+ name: z.string(),
773
+ }),
774
+ },
775
+ },
776
+ 'hi {{ name }}'
777
+ );
778
+
779
+ const response = await hi({ name: 'Genkit' });
780
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
781
+ });
782
+
783
+ it('thrown on prompt with both legacy template and messages', async () => {
784
+ assert.throws(() =>
785
+ ai.definePrompt(
786
+ {
787
+ name: 'hi',
788
+ input: {
789
+ schema: z.object({
790
+ name: z.string(),
791
+ }),
792
+ },
793
+ messages: 'something',
794
+ },
795
+ 'hi {{ name }}'
796
+ )
797
+ );
798
+ });
799
+
800
+ it('calls prompt with default model with config', async () => {
801
+ const hi = ai.definePrompt({
802
+ name: 'hi',
803
+ input: {
804
+ schema: z.object({
805
+ name: z.string(),
806
+ }),
807
+ },
808
+ config: {
809
+ temperature: 11,
810
+ },
811
+ messages: async (input) => {
812
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
813
+ },
814
+ });
815
+
816
+ const response = await hi({ name: 'Genkit' });
817
+ assert.strictEqual(
818
+ response.text,
819
+ 'Echo: hi Genkit; config: {"temperature":11}'
820
+ );
821
+ });
822
+
823
+ it('calls prompt with default model via retrieved prompt', async () => {
824
+ ai.definePrompt({
825
+ name: 'hi',
826
+ input: {
827
+ schema: z.object({
828
+ name: z.string(),
829
+ }),
830
+ },
831
+ messages: async (input) => {
832
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
833
+ },
834
+ });
835
+
836
+ const hi = ai.prompt('hi');
837
+
838
+ const response = await hi({ name: 'Genkit' });
839
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
840
+ });
841
+ });
842
+
843
+ describe('default model ref', () => {
844
+ let ai: GenkitBeta;
845
+
846
+ beforeEach(() => {
847
+ ai = genkit({
848
+ model: modelRef({
849
+ name: 'echoModel',
850
+ }),
851
+ });
852
+ defineEchoModel(ai);
853
+ });
854
+
855
+ it('calls prompt with default model', async () => {
856
+ const hi = ai.definePrompt({
857
+ name: 'hi',
858
+ input: {
859
+ schema: z.object({
860
+ name: z.string(),
861
+ }),
862
+ },
863
+ messages: async (input) => {
864
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
865
+ },
866
+ });
867
+
868
+ const response = await hi({ name: 'Genkit' });
869
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
870
+ });
871
+
872
+ it('streams prompt with default model', async () => {
873
+ const hi = ai.definePrompt({
874
+ name: 'hi',
875
+ input: {
876
+ schema: z.object({
877
+ name: z.string(),
878
+ }),
879
+ },
880
+ config: {
881
+ temperature: 11,
882
+ },
883
+ messages: async (input) => {
884
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
885
+ },
886
+ });
887
+
888
+ const { response, stream } = await hi.stream({ name: 'Genkit' });
889
+ const chunks: string[] = [];
890
+ for await (const chunk of stream) {
891
+ chunks.push(chunk.text);
892
+ }
893
+ const responseText = (await response).text;
894
+
895
+ assert.strictEqual(
896
+ responseText,
897
+ 'Echo: hi Genkit; config: {"temperature":11}'
898
+ );
899
+ assert.deepStrictEqual(chunks, ['3', '2', '1']);
900
+ });
901
+ });
902
+
903
+ describe('explicit model', () => {
904
+ let ai: GenkitBeta;
905
+
906
+ beforeEach(() => {
907
+ ai = genkit({});
908
+ defineEchoModel(ai);
909
+ });
910
+
911
+ it('calls prompt with default model', async () => {
912
+ const hi = ai.definePrompt({
913
+ name: 'hi',
914
+ model: 'echoModel',
915
+ input: {
916
+ schema: z.object({
917
+ name: z.string(),
918
+ }),
919
+ },
920
+ messages: async (input) => {
921
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
922
+ },
923
+ });
924
+
925
+ const response = await hi({ name: 'Genkit' });
926
+ assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}');
927
+ });
928
+
929
+ it('calls prompt with default model with config', async () => {
930
+ const hi = ai.definePrompt({
931
+ name: 'hi',
932
+ model: 'echoModel',
933
+ input: {
934
+ schema: z.object({
935
+ name: z.string(),
936
+ }),
937
+ },
938
+ config: {
939
+ temperature: 11,
940
+ },
941
+ messages: async (input) => {
942
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
943
+ },
944
+ });
945
+
946
+ const response = await hi({ name: 'Genkit' });
947
+ assert.strictEqual(
948
+ response.text,
949
+ 'Echo: hi Genkit; config: {"temperature":11}'
950
+ );
951
+ });
952
+
953
+ it('calls prompt with default model with call site config', async () => {
954
+ const hi = ai.definePrompt({
955
+ name: 'hi',
956
+ model: 'echoModel',
957
+ input: {
958
+ schema: z.object({
959
+ name: z.string(),
960
+ }),
961
+ },
962
+ config: {
963
+ temperature: 11,
964
+ },
965
+ messages: async (input) => {
966
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
967
+ },
968
+ });
969
+
970
+ const response = await hi(
971
+ { name: 'Genkit' },
972
+ {
973
+ config: {
974
+ version: 'abc',
975
+ },
976
+ }
977
+ );
978
+ assert.strictEqual(
979
+ response.text,
980
+ 'Echo: hi Genkit; config: {"version":"abc","temperature":11}'
981
+ );
982
+ });
983
+ });
984
+
985
+ describe('render', () => {
986
+ let ai: GenkitBeta;
987
+
988
+ beforeEach(() => {
989
+ ai = genkit({});
990
+ defineEchoModel(ai);
991
+ });
992
+
993
+ it('renders prompt', async () => {
994
+ const hi = ai.definePrompt({
995
+ name: 'hi',
996
+ model: 'echoModel',
997
+ input: {
998
+ schema: z.object({
999
+ name: z.string(),
1000
+ }),
1001
+ },
1002
+ messages: async (input) => {
1003
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
1004
+ },
1005
+ });
1006
+
1007
+ const response = await hi.render({ name: 'Genkit' });
1008
+ delete response.model; // ignore
1009
+ assert.deepStrictEqual(response, {
1010
+ messages: [
1011
+ {
1012
+ content: [
1013
+ {
1014
+ text: 'hi Genkit',
1015
+ },
1016
+ ],
1017
+ role: 'user',
1018
+ },
1019
+ ],
1020
+ });
1021
+ });
1022
+ });
1023
+ });
1024
+
1025
+ describe('prompt', () => {
1026
+ let ai: GenkitBeta;
1027
+ let pm: ProgrammableModel;
1028
+
1029
+ beforeEach(() => {
1030
+ ai = genkit({
1031
+ model: 'echoModel',
1032
+ promptDir: './tests/prompts',
1033
+ });
1034
+ defineEchoModel(ai);
1035
+ pm = defineProgrammableModel(ai);
1036
+ ai.defineTool(
1037
+ {
1038
+ name: 'toolA',
1039
+ description: 'toolA it is',
1040
+ },
1041
+ async () => {}
1042
+ );
1043
+ ai.defineSchema('myInputSchema', z.object({ foo: z.string() }));
1044
+ ai.defineSchema('myOutputSchema', z.object({ output: z.string() }));
1045
+ });
1046
+
1047
+ it('loads from from the folder', async () => {
1048
+ const testPrompt = ai.prompt('test'); // see tests/prompts folder
1049
+
1050
+ const { text } = await testPrompt();
1051
+
1052
+ assert.strictEqual(
1053
+ text,
1054
+ 'Echo: Hello from the prompt file; config: {"temperature":11}'
1055
+ );
1056
+ assert.deepStrictEqual(await testPrompt.render({}), {
1057
+ config: {
1058
+ temperature: 11,
1059
+ },
1060
+ messages: [
1061
+ { content: [{ text: 'Hello from the prompt file' }], role: 'user' },
1062
+ ],
1063
+ output: {},
1064
+ });
1065
+ });
1066
+
1067
+ it('does not auto-load prompts when promptDir is null', async () => {
1068
+ const aiWithoutPromptDir = genkit({
1069
+ model: 'echoModel',
1070
+ promptDir: null,
1071
+ });
1072
+
1073
+ const prompt = aiWithoutPromptDir.prompt('test');
1074
+ const response = prompt();
1075
+ await assert.rejects(response, {
1076
+ message: 'NOT_FOUND: Prompt test not found',
1077
+ });
1078
+ });
1079
+
1080
+ it('loads from from the sub folder', async () => {
1081
+ const testPrompt = ai.prompt('sub/test'); // see tests/prompts/sub folder
1082
+
1083
+ const { text } = await testPrompt();
1084
+
1085
+ assert.strictEqual(
1086
+ text,
1087
+ 'Echo: Hello from the sub folder prompt file; config: {"temperature":12}'
1088
+ );
1089
+ assert.deepStrictEqual(await testPrompt.render({}), {
1090
+ config: {
1091
+ temperature: 12,
1092
+ },
1093
+ messages: [
1094
+ {
1095
+ content: [{ text: 'Hello from the sub folder prompt file' }],
1096
+ role: 'user',
1097
+ },
1098
+ ],
1099
+ output: {},
1100
+ });
1101
+ });
1102
+
1103
+ it('loads from from the folder with all the options', async () => {
1104
+ const testPrompt = ai.prompt('kitchensink'); // see tests/prompts folder
1105
+
1106
+ const request = await testPrompt.render({ subject: 'banana' });
1107
+
1108
+ assert.deepStrictEqual(request, {
1109
+ model: 'googleai/gemini-5.0-ultimate-pro-plus',
1110
+ config: {
1111
+ temperature: 11,
1112
+ },
1113
+ output: {
1114
+ format: 'csv',
1115
+ jsonSchema: {
1116
+ additionalProperties: false,
1117
+ properties: {
1118
+ arr: {
1119
+ description: 'array of objects',
1120
+ items: {
1121
+ additionalProperties: false,
1122
+ properties: {
1123
+ nest2: {
1124
+ type: ['boolean', 'null'],
1125
+ },
1126
+ },
1127
+ type: 'object',
1128
+ },
1129
+ type: 'array',
1130
+ },
1131
+ obj: {
1132
+ additionalProperties: false,
1133
+ description: 'a nested object',
1134
+ properties: {
1135
+ nest1: {
1136
+ type: ['string', 'null'],
1137
+ },
1138
+ },
1139
+ type: ['object', 'null'],
1140
+ },
1141
+ },
1142
+ required: ['arr'],
1143
+ type: 'object',
1144
+ },
1145
+ },
1146
+ maxTurns: 77,
1147
+ messages: [
1148
+ {
1149
+ content: [
1150
+ {
1151
+ text: ' Hello ',
1152
+ },
1153
+ ],
1154
+ role: 'system',
1155
+ },
1156
+ {
1157
+ content: [
1158
+ {
1159
+ text: ' from the prompt file banana',
1160
+ },
1161
+ ],
1162
+ role: 'model',
1163
+ },
1164
+ ],
1165
+ returnToolRequests: true,
1166
+ toolChoice: 'required',
1167
+ tools: ['toolA', 'toolB'],
1168
+ metadata: {
1169
+ prompt: {
1170
+ foo: 'bar',
1171
+ },
1172
+ },
1173
+ });
1174
+ });
1175
+
1176
+ it('renders loaded prompt via executable-prompt', async () => {
1177
+ ai.defineModel(
1178
+ { name: 'googleai/gemini-5.0-ultimate-pro-plus' },
1179
+ async () => ({})
1180
+ );
1181
+
1182
+ ai.defineTool(
1183
+ {
1184
+ name: 'toolA',
1185
+ description: 'toolA it is',
1186
+ },
1187
+ async () => {}
1188
+ );
1189
+
1190
+ ai.defineTool(
1191
+ {
1192
+ name: 'toolB',
1193
+ description: 'toolB it is',
1194
+ },
1195
+ async () => {}
1196
+ );
1197
+
1198
+ const generateActionOptions = await (
1199
+ await ai.registry.lookupAction('/executable-prompt/kitchensink')
1200
+ )({ subject: 'banana' });
1201
+
1202
+ assert.deepStrictEqual(stripUndefinedProps(generateActionOptions), {
1203
+ config: {
1204
+ temperature: 11,
1205
+ },
1206
+ model: 'googleai/gemini-5.0-ultimate-pro-plus',
1207
+ maxTurns: 77,
1208
+ messages: [
1209
+ { role: 'system', content: [{ text: ' Hello ' }] },
1210
+ { role: 'model', content: [{ text: ' from the prompt file banana' }] },
1211
+ ],
1212
+ output: {
1213
+ format: 'csv',
1214
+ jsonSchema: {
1215
+ additionalProperties: false,
1216
+ properties: {
1217
+ arr: {
1218
+ description: 'array of objects',
1219
+ items: {
1220
+ additionalProperties: false,
1221
+ properties: {
1222
+ nest2: {
1223
+ type: ['boolean', 'null'],
1224
+ },
1225
+ },
1226
+ type: 'object',
1227
+ },
1228
+ type: 'array',
1229
+ },
1230
+ obj: {
1231
+ additionalProperties: false,
1232
+ description: 'a nested object',
1233
+ properties: {
1234
+ nest1: {
1235
+ type: ['string', 'null'],
1236
+ },
1237
+ },
1238
+ type: ['object', 'null'],
1239
+ },
1240
+ },
1241
+ required: ['arr'],
1242
+ type: 'object',
1243
+ },
1244
+ },
1245
+ returnToolRequests: true,
1246
+ toolChoice: 'required',
1247
+ tools: ['/tool/toolA', '/tool/toolB'],
1248
+ });
1249
+ });
1250
+
1251
+ it('resolved schema refs', async () => {
1252
+ const prompt = ai.prompt('schemaRef');
1253
+
1254
+ const rendered = await prompt.render({ foo: 'bar' });
1255
+ assert.deepStrictEqual(rendered.output?.jsonSchema, {
1256
+ $schema: 'http://json-schema.org/draft-07/schema#',
1257
+ additionalProperties: true,
1258
+ properties: {
1259
+ output: {
1260
+ type: 'string',
1261
+ },
1262
+ },
1263
+ required: ['output'],
1264
+ type: 'object',
1265
+ });
1266
+
1267
+ assert.deepStrictEqual(
1268
+ (await (await prompt.asTool())({ foo: 'bar' })).messages,
1269
+ [
1270
+ {
1271
+ role: 'user',
1272
+ content: [{ text: 'Write a poem about bar.' }],
1273
+ },
1274
+ ]
1275
+ );
1276
+ });
1277
+
1278
+ it('lazily resolved schema refs', async () => {
1279
+ const prompt = ai.prompt('badSchemaRef');
1280
+
1281
+ await assert.rejects(prompt.render({ foo: 'bar' }), (e: Error) =>
1282
+ e.message.includes("NOT_FOUND: Schema 'badSchemaRef1' not found")
1283
+ );
1284
+ });
1285
+
1286
+ it('loads a variant from from the folder', async () => {
1287
+ const testPrompt = ai.prompt('test', { variant: 'variant' }); // see tests/prompts folder
1288
+
1289
+ const { text } = await testPrompt();
1290
+
1291
+ assert.strictEqual(
1292
+ text,
1293
+ 'Echo: Hello from a variant of the hello prompt; config: {"temperature":13}'
1294
+ );
1295
+ });
1296
+
1297
+ it('includes metadata expected by the dev ui', async () => {
1298
+ const testPrompt: PromptAction = await ai.registry.lookupAction(
1299
+ '/prompt/test.variant'
1300
+ );
1301
+
1302
+ assert.deepStrictEqual(testPrompt.__action.metadata, {
1303
+ prompt: {
1304
+ config: {
1305
+ temperature: 13,
1306
+ },
1307
+ description: 'a prompt variant in a file',
1308
+ ext: {},
1309
+ input: {
1310
+ schema: null,
1311
+ },
1312
+ metadata: {},
1313
+ model: undefined,
1314
+ name: 'test',
1315
+ variant: 'variant',
1316
+ template: 'Hello from a variant of the hello prompt',
1317
+ tools: ['toolA'],
1318
+ toolChoice: undefined,
1319
+ toolDefs: [],
1320
+ use: [{ name: 'myMiddleware' }],
1321
+ raw: {
1322
+ config: {
1323
+ temperature: 13,
1324
+ },
1325
+ description: 'a prompt variant in a file',
1326
+ tools: ['toolA'],
1327
+ use: ['myMiddleware'],
1328
+ },
1329
+ },
1330
+ type: 'prompt',
1331
+ });
1332
+ });
1333
+
1334
+ it('returns a ref to functional prompts', async () => {
1335
+ ai.definePrompt({
1336
+ name: 'hi',
1337
+ model: 'echoModel',
1338
+ input: {
1339
+ schema: z.object({
1340
+ name: z.string(),
1341
+ }),
1342
+ },
1343
+ config: {
1344
+ temperature: 11,
1345
+ },
1346
+ messages: async (input) => {
1347
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
1348
+ },
1349
+ });
1350
+ const testPrompt = ai.prompt('hi');
1351
+ const { text } = await testPrompt({ name: 'banana' });
1352
+
1353
+ assert.strictEqual(text, 'Echo: hi banana; config: {"temperature":11}');
1354
+ });
1355
+
1356
+ it('includes metadata for functional prompts', async () => {
1357
+ ai.definePrompt({
1358
+ name: 'hi',
1359
+ model: 'echoModel',
1360
+ input: {
1361
+ schema: z.object({
1362
+ name: z.string(),
1363
+ }),
1364
+ },
1365
+ config: {
1366
+ temperature: 0.13,
1367
+ },
1368
+ tools: ['toolA'],
1369
+ toolChoice: 'auto',
1370
+ use: ['myMiddleware'],
1371
+ messages: async (input) => [],
1372
+ });
1373
+ const testPrompt: PromptAction =
1374
+ await ai.registry.lookupAction('/prompt/hi');
1375
+
1376
+ assert.deepStrictEqual(testPrompt.__action.metadata, {
1377
+ type: 'prompt',
1378
+ prompt: {
1379
+ name: 'hi',
1380
+ model: 'echoModel',
1381
+ config: {
1382
+ temperature: 0.13,
1383
+ },
1384
+ tools: ['toolA'],
1385
+ toolChoice: 'auto',
1386
+ use: [{ name: 'myMiddleware' }],
1387
+ input: {
1388
+ schema: {
1389
+ type: 'object',
1390
+ properties: {
1391
+ name: {
1392
+ type: 'string',
1393
+ },
1394
+ },
1395
+ required: ['name'],
1396
+ additionalProperties: true,
1397
+ $schema: 'http://json-schema.org/draft-07/schema#',
1398
+ },
1399
+ },
1400
+ },
1401
+ });
1402
+ });
1403
+
1404
+ it('passes in output options to the model', async () => {
1405
+ const hi = ai.definePrompt({
1406
+ name: 'hi',
1407
+ model: 'programmableModel',
1408
+ input: {
1409
+ schema: z.object({
1410
+ name: z.string(),
1411
+ }),
1412
+ },
1413
+ output: {
1414
+ schema: z.object({
1415
+ message: z.string(),
1416
+ }),
1417
+ format: 'json',
1418
+ },
1419
+ config: {
1420
+ temperature: 11,
1421
+ },
1422
+ messages: async (input) => {
1423
+ return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }];
1424
+ },
1425
+ });
1426
+
1427
+ pm.handleResponse = async (req, sc) => {
1428
+ return {
1429
+ message: {
1430
+ role: 'model',
1431
+ content: [{ text: '```json\n{"message": "hello"}\n```' }],
1432
+ },
1433
+ };
1434
+ };
1435
+
1436
+ const { output } = await hi({
1437
+ name: 'Pavel',
1438
+ });
1439
+
1440
+ assert.deepStrictEqual(output, { message: 'hello' });
1441
+ });
1442
+ });