@fluidframework/ai-collab 2.22.1 → 2.23.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 (47) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +70 -4
  3. package/api-report/ai-collab.alpha.api.md +170 -2
  4. package/dist/aiCollab.d.ts +0 -1
  5. package/dist/aiCollab.d.ts.map +1 -1
  6. package/dist/aiCollab.js +1 -2
  7. package/dist/aiCollab.js.map +1 -1
  8. package/dist/aiCollabApi.d.ts +50 -3
  9. package/dist/aiCollabApi.d.ts.map +1 -1
  10. package/dist/aiCollabApi.js.map +1 -1
  11. package/dist/alpha.d.ts +17 -0
  12. package/dist/explicit-strategy/debugEvents.d.ts +248 -0
  13. package/dist/explicit-strategy/debugEvents.d.ts.map +1 -0
  14. package/dist/explicit-strategy/debugEvents.js +36 -0
  15. package/dist/explicit-strategy/debugEvents.js.map +1 -0
  16. package/dist/explicit-strategy/index.d.ts +4 -4
  17. package/dist/explicit-strategy/index.d.ts.map +1 -1
  18. package/dist/explicit-strategy/index.js +176 -54
  19. package/dist/explicit-strategy/index.js.map +1 -1
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/lib/aiCollab.d.ts +0 -1
  24. package/lib/aiCollab.d.ts.map +1 -1
  25. package/lib/aiCollab.js +1 -2
  26. package/lib/aiCollab.js.map +1 -1
  27. package/lib/aiCollabApi.d.ts +50 -3
  28. package/lib/aiCollabApi.d.ts.map +1 -1
  29. package/lib/aiCollabApi.js.map +1 -1
  30. package/lib/alpha.d.ts +17 -0
  31. package/lib/explicit-strategy/debugEvents.d.ts +248 -0
  32. package/lib/explicit-strategy/debugEvents.d.ts.map +1 -0
  33. package/lib/explicit-strategy/debugEvents.js +32 -0
  34. package/lib/explicit-strategy/debugEvents.js.map +1 -0
  35. package/lib/explicit-strategy/index.d.ts +4 -4
  36. package/lib/explicit-strategy/index.d.ts.map +1 -1
  37. package/lib/explicit-strategy/index.js +174 -52
  38. package/lib/explicit-strategy/index.js.map +1 -1
  39. package/lib/index.d.ts +2 -1
  40. package/lib/index.d.ts.map +1 -1
  41. package/lib/index.js.map +1 -1
  42. package/package.json +13 -11
  43. package/src/aiCollab.ts +1 -2
  44. package/src/aiCollabApi.ts +54 -3
  45. package/src/explicit-strategy/debugEvents.ts +297 -0
  46. package/src/explicit-strategy/index.ts +269 -59
  47. package/src/index.ts +20 -0
@@ -2,16 +2,18 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
+ import { UsageError } from "@fluidframework/telemetry-utils/internal";
5
6
  import { getSimpleSchema, Tree, } from "@fluidframework/tree/internal";
6
7
  // eslint-disable-next-line import/no-internal-modules
7
8
  import { zodResponseFormat } from "openai/helpers/zod";
9
+ import { v4 as uuidv4 } from "uuid";
8
10
  import { z } from "zod";
9
11
  import { applyAgentEdit } from "./agentEditReducer.js";
12
+ import { generateDebugEvent, EventFlowDebugNames, } from "./debugEvents.js";
10
13
  import { IdGenerator } from "./idGenerator.js";
11
14
  import { getEditingSystemPrompt, getPlanningSystemPrompt, getReviewSystemPrompt, toDecoratedJson, } from "./promptGeneration.js";
12
15
  import { generateGenericEditTypes } from "./typeGeneration.js";
13
16
  import { fail } from "./utils.js";
14
- const DEBUG_LOG = [];
15
17
  /**
16
18
  * Prompts the provided LLM client to generate valid tree edits.
17
19
  * Applies those edits to the provided tree branch before returning.
@@ -29,57 +31,92 @@ export async function generateTreeEdits(options) {
29
31
  let sequentialErrorCount = 0;
30
32
  const simpleSchema = getSimpleSchema(Tree.schema(options.treeNode));
31
33
  const tokensUsed = { inputTokens: 0, outputTokens: 0 };
34
+ const debugLogTraceId = uuidv4();
35
+ const coreEventFlowTraceId = uuidv4();
36
+ options.debugEventLogHandler?.({
37
+ ...generateDebugEvent("CORE_EVENT_LOOP_STARTED", debugLogTraceId),
38
+ eventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,
39
+ eventFlowStatus: "STARTED",
40
+ eventFlowTraceId: coreEventFlowTraceId,
41
+ });
32
42
  try {
33
- for await (const edit of generateEdits(options, simpleSchema, idGenerator, editLog, options.limiters?.tokenLimits, tokensUsed)) {
43
+ for await (const generateEditResult of generateEdits(options, simpleSchema, idGenerator, editLog, options.limiters?.tokenLimits, tokensUsed, options.debugEventLogHandler && {
44
+ eventLogHandler: options.debugEventLogHandler,
45
+ traceId: debugLogTraceId,
46
+ })) {
34
47
  try {
35
- const result = applyAgentEdit(edit, idGenerator, simpleSchema.definitions, options.validator);
48
+ const result = applyAgentEdit(generateEditResult.edit, idGenerator, simpleSchema.definitions, options.validator);
36
49
  const explanation = result.explanation;
37
50
  editLog.push({ edit: { ...result, explanation } });
38
51
  sequentialErrorCount = 0;
52
+ options.debugEventLogHandler?.({
53
+ ...generateDebugEvent("APPLIED_EDIT_SUCCESS", debugLogTraceId),
54
+ eventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,
55
+ eventFlowStatus: "IN_PROGRESS",
56
+ eventFlowTraceId: generateEditResult.eventFlowTraceId,
57
+ edit: generateEditResult.edit,
58
+ });
39
59
  }
40
60
  catch (error) {
41
- if (error instanceof Error) {
61
+ options.debugEventLogHandler?.({
62
+ ...generateDebugEvent("APPLIED_EDIT_FAILURE", debugLogTraceId),
63
+ eventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,
64
+ eventFlowStatus: "IN_PROGRESS",
65
+ eventFlowTraceId: generateEditResult.eventFlowTraceId,
66
+ edit: generateEditResult.edit,
67
+ errorMessage: error?.message,
68
+ sequentialErrorCount,
69
+ });
70
+ if (error instanceof UsageError) {
42
71
  sequentialErrorCount += 1;
43
- editLog.push({ edit, error: error.message });
44
- DEBUG_LOG?.push(`Error: ${error.message}`);
72
+ editLog.push({ edit: generateEditResult.edit, error: error.message });
45
73
  }
46
74
  else {
47
75
  throw error;
48
76
  }
49
77
  }
50
- const responseStatus = editCount > 0 && sequentialErrorCount < editCount ? "partial-failure" : "failure";
78
+ let shouldExitEarly = false;
79
+ const completionResponse = {
80
+ status: editCount > 0 && sequentialErrorCount < editCount ? "partial-failure" : "failure",
81
+ errorMessage: "unexpectedError",
82
+ tokensUsed,
83
+ };
51
84
  if (options.limiters?.abortController?.signal.aborted === true) {
52
- return {
53
- status: responseStatus,
54
- errorMessage: "aborted",
55
- tokensUsed,
56
- };
85
+ completionResponse.errorMessage = "aborted";
86
+ shouldExitEarly = true;
57
87
  }
58
- if (sequentialErrorCount >
88
+ else if (sequentialErrorCount >
59
89
  (options.limiters?.maxSequentialErrors ?? Number.POSITIVE_INFINITY)) {
60
- return {
61
- status: responseStatus,
62
- errorMessage: "tooManyErrors",
63
- tokensUsed,
64
- };
90
+ completionResponse.errorMessage = "tooManyErrors";
91
+ shouldExitEarly = true;
92
+ }
93
+ else if (++editCount >= (options.limiters?.maxModelCalls ?? Number.POSITIVE_INFINITY)) {
94
+ completionResponse.errorMessage = "tooManyModelCalls";
95
+ shouldExitEarly = true;
65
96
  }
66
- if (++editCount >= (options.limiters?.maxModelCalls ?? Number.POSITIVE_INFINITY)) {
67
- return {
68
- status: responseStatus,
69
- errorMessage: "tooManyModelCalls",
70
- tokensUsed,
71
- };
97
+ if (shouldExitEarly) {
98
+ options.debugEventLogHandler?.({
99
+ ...generateDebugEvent("CORE_EVENT_LOOP_COMPLETED", debugLogTraceId),
100
+ eventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,
101
+ eventFlowStatus: "COMPLETED",
102
+ status: "failure",
103
+ failureReason: completionResponse.errorMessage,
104
+ eventFlowTraceId: coreEventFlowTraceId,
105
+ });
106
+ return completionResponse;
72
107
  }
73
108
  }
74
109
  }
75
110
  catch (error) {
76
- if (error instanceof Error) {
77
- DEBUG_LOG?.push(`Error: ${error.message}`);
78
- }
79
- if (options.dumpDebugLog ?? false) {
80
- console.log(DEBUG_LOG.join("\n\n"));
81
- DEBUG_LOG.length = 0;
82
- }
111
+ options.debugEventLogHandler?.({
112
+ ...generateDebugEvent("CORE_EVENT_LOOP_COMPLETED", debugLogTraceId),
113
+ eventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,
114
+ eventFlowStatus: "COMPLETED",
115
+ status: "failure",
116
+ eventFlowTraceId: coreEventFlowTraceId,
117
+ failureReason: error instanceof TokenLimitExceededError ? "tokenLimitExceeded" : "unexpectedError",
118
+ errorMessage: error?.message,
119
+ });
83
120
  if (error instanceof TokenLimitExceededError) {
84
121
  return {
85
122
  status: editCount > 0 && sequentialErrorCount < editCount ? "partial-failure" : "failure",
@@ -89,10 +126,13 @@ export async function generateTreeEdits(options) {
89
126
  }
90
127
  throw error;
91
128
  }
92
- if (options.dumpDebugLog ?? false) {
93
- console.log(DEBUG_LOG.join("\n\n"));
94
- DEBUG_LOG.length = 0;
95
- }
129
+ options.debugEventLogHandler?.({
130
+ ...generateDebugEvent("CORE_EVENT_LOOP_COMPLETED", debugLogTraceId),
131
+ eventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,
132
+ eventFlowStatus: "COMPLETED",
133
+ eventFlowTraceId: coreEventFlowTraceId,
134
+ status: "success",
135
+ });
96
136
  return {
97
137
  status: "success",
98
138
  tokensUsed,
@@ -107,14 +147,31 @@ export async function generateTreeEdits(options) {
107
147
  * Once the LLM believes it has completed the user's ask, it will no longer return an edit and as a result
108
148
  * this generator will no longer yield a next value.
109
149
  */
110
- async function* generateEdits(options, simpleSchema, idGenerator, editLog, tokenLimits, tokensUsed) {
150
+ async function* generateEdits(options, simpleSchema, idGenerator, editLog, tokenLimits, tokensUsed, debugOptions) {
111
151
  const [types, rootTypeName] = generateGenericEditTypes(simpleSchema, true);
112
152
  let plan;
113
153
  if (options.planningStep !== undefined) {
114
- const planningPromt = getPlanningSystemPrompt(options.treeNode, options.prompt.userAsk, options.prompt.systemRoleContext);
115
- DEBUG_LOG?.push(planningPromt);
116
- plan = await getStringFromLlm(planningPromt, options.openAI, tokensUsed);
117
- DEBUG_LOG?.push(`AI Generated the following plan: ${planningPromt}`);
154
+ const planningPrompt = getPlanningSystemPrompt(options.treeNode, options.prompt.userAsk, options.prompt.systemRoleContext);
155
+ const generatePlanningPromptEventFlowId = uuidv4();
156
+ debugOptions?.eventLogHandler?.({
157
+ ...generateDebugEvent("GENERATE_PLANNING_PROMPT_STARTED", debugOptions.traceId),
158
+ eventFlowName: EventFlowDebugNames.GENERATE_PLANNING_PROMPT,
159
+ eventFlowTraceId: generatePlanningPromptEventFlowId,
160
+ eventFlowStatus: "STARTED",
161
+ });
162
+ plan = await getStringFromLlm(planningPrompt, options.openAI, tokensUsed, debugOptions && {
163
+ ...debugOptions,
164
+ triggeringEventFlowName: EventFlowDebugNames.GENERATE_PLANNING_PROMPT,
165
+ eventFlowTraceId: generatePlanningPromptEventFlowId,
166
+ });
167
+ debugOptions?.eventLogHandler?.({
168
+ ...generateDebugEvent("GENERATE_PLANNING_PROMPT_COMPLETED", debugOptions.traceId),
169
+ eventFlowName: EventFlowDebugNames.GENERATE_PLANNING_PROMPT,
170
+ eventFlowStatus: "COMPLETED",
171
+ eventFlowTraceId: generatePlanningPromptEventFlowId,
172
+ isLlmResponseValid: plan !== undefined,
173
+ llmGeneratedPlan: plan,
174
+ });
118
175
  }
119
176
  const originalDecoratedJson = (options.finalReviewStep ?? false)
120
177
  ? toDecoratedJson(idGenerator, options.treeNode)
@@ -123,21 +180,35 @@ async function* generateEdits(options, simpleSchema, idGenerator, editLog, token
123
180
  let hasReviewed = (options.finalReviewStep ?? false) ? false : true;
124
181
  async function getNextEdit() {
125
182
  const systemPrompt = getEditingSystemPrompt(options.prompt.userAsk, idGenerator, options.treeNode, editLog, options.prompt.systemRoleContext, plan);
126
- DEBUG_LOG?.push(systemPrompt);
127
183
  const schema = types[rootTypeName] ?? fail("Root type not found.");
128
- const wrapper = await getStructuredOutputFromLlm(systemPrompt, options.openAI, schema, "A JSON object that represents an edit to a JSON tree.", tokensUsed);
129
- // eslint-disable-next-line unicorn/no-null
130
- DEBUG_LOG?.push(JSON.stringify(wrapper, null, 2));
184
+ const generateTreeEditEventFlowId = uuidv4();
185
+ debugOptions?.eventLogHandler?.({
186
+ ...generateDebugEvent("GENERATE_TREE_EDIT_STARTED", debugOptions.traceId),
187
+ eventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,
188
+ eventFlowStatus: "STARTED",
189
+ eventFlowTraceId: generateTreeEditEventFlowId,
190
+ llmPrompt: systemPrompt,
191
+ });
192
+ const wrapper = await getStructuredOutputFromLlm(systemPrompt, options.openAI, schema, "A JSON object that represents an edit to a JSON tree.", tokensUsed, debugOptions && {
193
+ ...debugOptions,
194
+ triggeringEventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,
195
+ eventFlowTraceId: generateTreeEditEventFlowId,
196
+ });
197
+ debugOptions?.eventLogHandler?.({
198
+ ...generateDebugEvent("GENERATE_TREE_EDIT_COMPLETED", debugOptions.traceId),
199
+ eventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,
200
+ eventFlowStatus: "COMPLETED",
201
+ eventFlowTraceId: generateTreeEditEventFlowId,
202
+ isLlmResponseValid: wrapper?.edit !== undefined,
203
+ llmGeneratedEdit: wrapper?.edit,
204
+ });
131
205
  if (wrapper === undefined) {
132
- DEBUG_LOG?.push("Failed to get response");
133
206
  return undefined;
134
207
  }
135
208
  if (wrapper.edit === null) {
136
- DEBUG_LOG?.push("No more edits.");
137
209
  if ((options.finalReviewStep ?? false) && !hasReviewed) {
138
210
  const reviewResult = await reviewGoal();
139
211
  if (reviewResult === undefined) {
140
- DEBUG_LOG?.push("Failed to get review response");
141
212
  return undefined;
142
213
  }
143
214
  // eslint-disable-next-line require-atomic-updates
@@ -153,18 +224,40 @@ async function* generateEdits(options, simpleSchema, idGenerator, editLog, token
153
224
  }
154
225
  }
155
226
  else {
156
- return wrapper.edit;
227
+ return { edit: wrapper.edit, eventFlowTraceId: generateTreeEditEventFlowId };
157
228
  }
158
229
  }
159
230
  async function reviewGoal() {
160
231
  const systemPrompt = getReviewSystemPrompt(options.prompt.userAsk, idGenerator, options.treeNode, originalDecoratedJson ?? fail("Original decorated tree not provided."), options.prompt.systemRoleContext);
161
- DEBUG_LOG?.push(systemPrompt);
162
232
  const schema = z.object({
163
233
  goalAccomplished: z
164
234
  .enum(["yes", "no"])
165
235
  .describe('Whether the user\'s goal was met in the "after" tree.'),
166
236
  });
167
- return getStructuredOutputFromLlm(systemPrompt, options.openAI, schema);
237
+ const finalReviewEventFlowTraceId = uuidv4();
238
+ debugOptions?.eventLogHandler?.({
239
+ ...generateDebugEvent("FINAL_REVIEW_STARTED", debugOptions.traceId),
240
+ eventFlowName: EventFlowDebugNames.FINAL_REVIEW,
241
+ eventFlowStatus: "STARTED",
242
+ eventFlowTraceId: finalReviewEventFlowTraceId,
243
+ llmPrompt: systemPrompt,
244
+ });
245
+ // TODO: In the future, when using structured output isn't guarenteed, we will
246
+ // need to add a custom type guard to ensure that output is in the right shape.
247
+ const output = await getStructuredOutputFromLlm(systemPrompt, options.openAI, schema, undefined, tokensUsed, debugOptions && {
248
+ ...debugOptions,
249
+ triggeringEventFlowName: EventFlowDebugNames.FINAL_REVIEW,
250
+ eventFlowTraceId: finalReviewEventFlowTraceId,
251
+ });
252
+ debugOptions?.eventLogHandler?.({
253
+ ...generateDebugEvent("FINAL_REVIEW_COMPLETED", debugOptions.traceId),
254
+ eventFlowName: EventFlowDebugNames.FINAL_REVIEW,
255
+ eventFlowStatus: "COMPLETED",
256
+ eventFlowTraceId: finalReviewEventFlowTraceId,
257
+ isLlmResponseValid: output !== undefined,
258
+ didLlmAccomplishGoal: output?.goalAccomplished,
259
+ });
260
+ return output;
168
261
  }
169
262
  let edit = await getNextEdit();
170
263
  while (edit !== undefined) {
@@ -181,7 +274,7 @@ async function* generateEdits(options, simpleSchema, idGenerator, editLog, token
181
274
  /**
182
275
  * Calls the LLM to generate a structured output response based on the provided prompt.
183
276
  */
184
- async function getStructuredOutputFromLlm(prompt, openAi, structuredOutputSchema, description, tokensUsed) {
277
+ async function getStructuredOutputFromLlm(prompt, openAi, structuredOutputSchema, description, tokensUsed, debugOptions) {
185
278
  const response_format = zodResponseFormat(structuredOutputSchema, "SharedTreeAI", {
186
279
  description,
187
280
  });
@@ -191,23 +284,52 @@ async function getStructuredOutputFromLlm(prompt, openAi, structuredOutputSchema
191
284
  response_format,
192
285
  };
193
286
  const result = await openAi.client.beta.chat.completions.parse(body);
287
+ debugOptions?.eventLogHandler?.({
288
+ ...generateDebugEvent("LLM_API_CALL", debugOptions.traceId),
289
+ triggeringEventFlowName: debugOptions.triggeringEventFlowName,
290
+ eventFlowTraceId: debugOptions.eventFlowTraceId,
291
+ modelName: openAi.modelName ?? "gpt-4o",
292
+ requestParams: body,
293
+ response: { ...result },
294
+ ...(result.usage && {
295
+ tokenUsage: {
296
+ promptTokens: result.usage.prompt_tokens,
297
+ completionTokens: result.usage.completion_tokens,
298
+ },
299
+ }),
300
+ });
194
301
  if (result.usage !== undefined && tokensUsed !== undefined) {
195
302
  tokensUsed.inputTokens += result.usage?.prompt_tokens;
196
303
  tokensUsed.outputTokens += result.usage?.completion_tokens;
197
304
  }
198
305
  // TODO: fix types so this isn't null and doesn't need a cast
199
306
  // The type should be derived from the zod schema
307
+ // TODO: Determine why this value would be undefined.
200
308
  return result.choices[0]?.message.parsed;
201
309
  }
202
310
  /**
203
311
  * Calls the LLM to generate a response based on the provided prompt.
204
312
  */
205
- async function getStringFromLlm(prompt, openAi, tokensUsed) {
313
+ async function getStringFromLlm(prompt, openAi, tokensUsed, debugOptions) {
206
314
  const body = {
207
315
  messages: [{ role: "system", content: prompt }],
208
316
  model: openAi.modelName ?? "gpt-4o",
209
317
  };
210
318
  const result = await openAi.client.chat.completions.create(body);
319
+ debugOptions?.eventLogHandler?.({
320
+ ...generateDebugEvent("LLM_API_CALL", debugOptions.traceId),
321
+ triggeringEventFlowName: debugOptions.triggeringEventFlowName,
322
+ eventFlowTraceId: debugOptions.eventFlowTraceId,
323
+ modelName: openAi.modelName ?? "gpt-4o",
324
+ requestParams: body,
325
+ response: { ...result },
326
+ ...(result.usage && {
327
+ tokenUsage: {
328
+ promptTokens: result.usage.prompt_tokens,
329
+ completionTokens: result.usage.completion_tokens,
330
+ },
331
+ }),
332
+ });
211
333
  if (result.usage !== undefined && tokensUsed !== undefined) {
212
334
  tokensUsed.inputTokens += result.usage?.prompt_tokens;
213
335
  tokensUsed.outputTokens += result.usage?.completion_tokens;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/explicit-strategy/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,eAAe,EACf,IAAI,GAGJ,MAAM,+BAA+B,CAAC;AACvC,sDAAsD;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAKvD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACN,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,GAEf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,MAAM,SAAS,GAAa,EAAE,CAAC;AAqC/B;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,OAAiC;IAEjC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;IACtC,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAE7B,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEpE,MAAM,UAAU,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IAEvD,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,aAAa,CACrC,OAAO,EACP,YAAY,EACZ,WAAW,EACX,OAAO,EACP,OAAO,CAAC,QAAQ,EAAE,WAAW,EAC7B,UAAU,CACV,EAAE,CAAC;YACH,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,cAAc,CAC5B,IAAI,EACJ,WAAW,EACX,YAAY,CAAC,WAAW,EACxB,OAAO,CAAC,SAAS,CACjB,CAAC;gBACF,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;gBACvC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;gBACnD,oBAAoB,GAAG,CAAC,CAAC;YAC1B,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACzB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;oBAC5B,oBAAoB,IAAI,CAAC,CAAC;oBAC1B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;oBAC7C,SAAS,EAAE,IAAI,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC5C,CAAC;qBAAM,CAAC;oBACP,MAAM,KAAK,CAAC;gBACb,CAAC;YACF,CAAC;YAED,MAAM,cAAc,GACnB,SAAS,GAAG,CAAC,IAAI,oBAAoB,GAAG,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;YAEnF,IAAI,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBAChE,OAAO;oBACN,MAAM,EAAE,cAAc;oBACtB,YAAY,EAAE,SAAS;oBACvB,UAAU;iBACV,CAAC;YACH,CAAC;YAED,IACC,oBAAoB;gBACpB,CAAC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,IAAI,MAAM,CAAC,iBAAiB,CAAC,EAClE,CAAC;gBACF,OAAO;oBACN,MAAM,EAAE,cAAc;oBACtB,YAAY,EAAE,eAAe;oBAC7B,UAAU;iBACV,CAAC;YACH,CAAC;YAED,IAAI,EAAE,SAAS,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,IAAI,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAClF,OAAO;oBACN,MAAM,EAAE,cAAc;oBACtB,YAAY,EAAE,mBAAmB;oBACjC,UAAU;iBACV,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC5B,SAAS,EAAE,IAAI,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,OAAO,CAAC,YAAY,IAAI,KAAK,EAAE,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACpC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QACtB,CAAC;QAED,IAAI,KAAK,YAAY,uBAAuB,EAAE,CAAC;YAC9C,OAAO;gBACN,MAAM,EACL,SAAS,GAAG,CAAC,IAAI,oBAAoB,GAAG,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;gBAClF,YAAY,EAAE,oBAAoB;gBAClC,UAAU;aACV,CAAC;QACH,CAAC;QACD,MAAM,KAAK,CAAC;IACb,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,IAAI,KAAK,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACpC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,OAAO;QACN,MAAM,EAAE,SAAS;QACjB,UAAU;KACV,CAAC;AACH,CAAC;AAMD;;;;;;;;GAQG;AACH,KAAK,SAAS,CAAC,CAAC,aAAa,CAC5B,OAAiC,EACjC,YAA8B,EAC9B,WAAwB,EACxB,OAAgB,EAChB,WAAoC,EACpC,UAAsB;IAEtB,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,GAAG,wBAAwB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAE3E,IAAI,IAAwB,CAAC;IAC7B,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACxC,MAAM,aAAa,GAAG,uBAAuB,CAC5C,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,MAAM,CAAC,OAAO,EACtB,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAChC,CAAC;QACF,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAC/B,IAAI,GAAG,MAAM,gBAAgB,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACzE,SAAS,EAAE,IAAI,CAAC,oCAAoC,aAAa,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,qBAAqB,GAC1B,CAAC,OAAO,CAAC,eAAe,IAAI,KAAK,CAAC;QACjC,CAAC,CAAC,eAAe,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC;QAChD,CAAC,CAAC,SAAS,CAAC;IACd,0DAA0D;IAC1D,IAAI,WAAW,GAAG,CAAC,OAAO,CAAC,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,KAAK,UAAU,WAAW;QACzB,MAAM,YAAY,GAAG,sBAAsB,CAC1C,OAAO,CAAC,MAAM,CAAC,OAAO,EACtB,WAAW,EACX,OAAO,CAAC,QAAQ,EAChB,OAAO,EACP,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAChC,IAAI,CACJ,CAAC;QAEF,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,MAAM,0BAA0B,CAC/C,YAAY,EACZ,OAAO,CAAC,MAAM,EACd,MAAM,EACN,uDAAuD,EACvD,UAAU,CACV,CAAC;QAEF,2CAA2C;QAC3C,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAClD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,SAAS,EAAE,IAAI,CAAC,wBAAwB,CAAC,CAAC;YAC1C,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YAC3B,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACxD,MAAM,YAAY,GAAG,MAAM,UAAU,EAAE,CAAC;gBACxC,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;oBAChC,SAAS,EAAE,IAAI,CAAC,+BAA+B,CAAC,CAAC;oBACjD,OAAO,SAAS,CAAC;gBAClB,CAAC;gBACD,kDAAkD;gBAClD,WAAW,GAAG,IAAI,CAAC;gBACnB,IAAI,YAAY,CAAC,gBAAgB,KAAK,KAAK,EAAE,CAAC;oBAC7C,OAAO,SAAS,CAAC;gBAClB,CAAC;qBAAM,CAAC;oBACP,kDAAkD;oBAClD,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;oBACnB,OAAO,WAAW,EAAE,CAAC;gBACtB,CAAC;YACF,CAAC;QACF,CAAC;aAAM,CAAC;YACP,OAAO,OAAO,CAAC,IAAI,CAAC;QACrB,CAAC;IACF,CAAC;IAED,KAAK,UAAU,UAAU;QACxB,MAAM,YAAY,GAAG,qBAAqB,CACzC,OAAO,CAAC,MAAM,CAAC,OAAO,EACtB,WAAW,EACX,OAAO,CAAC,QAAQ,EAChB,qBAAqB,IAAI,IAAI,CAAC,uCAAuC,CAAC,EACtE,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAChC,CAAC;QAEF,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;YACvB,gBAAgB,EAAE,CAAC;iBACjB,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;iBACnB,QAAQ,CAAC,uDAAuD,CAAC;SACnE,CAAC,CAAC;QACH,OAAO,0BAA0B,CAAe,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;IAC/B,OAAO,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,CAAC;QACX,IAAI,UAAU,CAAC,WAAW,GAAG,CAAC,WAAW,EAAE,WAAW,IAAI,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACrF,MAAM,IAAI,uBAAuB,CAAC,6BAA6B,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,UAAU,CAAC,YAAY,GAAG,CAAC,WAAW,EAAE,YAAY,IAAI,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACvF,MAAM,IAAI,uBAAuB,CAAC,8BAA8B,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;IAC5B,CAAC;AACF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,0BAA0B,CACxC,MAAc,EACd,MAA2B,EAC3B,sBAAsC,EACtC,WAAoB,EACpB,UAAuB;IAEvB,MAAM,eAAe,GAAG,iBAAiB,CAAC,sBAAsB,EAAE,cAAc,EAAE;QACjF,WAAW;KACX,CAAC,CAAC;IAEH,MAAM,IAAI,GAA+B;QACxC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC/C,KAAK,EAAE,MAAM,CAAC,SAAS,IAAI,QAAQ;QACnC,eAAe;KACf,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAErE,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC5D,UAAU,CAAC,WAAW,IAAI,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC;QACtD,UAAU,CAAC,YAAY,IAAI,MAAM,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAC5D,CAAC;IAED,6DAA6D;IAC7D,iDAAiD;IACjD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,MAAuB,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,gBAAgB,CAC9B,MAAc,EACd,MAA2B,EAC3B,UAAuB;IAEvB,MAAM,IAAI,GAA+B;QACxC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC/C,KAAK,EAAE,MAAM,CAAC,SAAS,IAAI,QAAQ;KACnC,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEjE,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC5D,UAAU,CAAC,WAAW,IAAI,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC;QACtD,UAAU,CAAC,YAAY,IAAI,MAAM,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAC5D,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;AACxD,CAAC;AAED,MAAM,uBAAwB,SAAQ,KAAK;CAAG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tgetSimpleSchema,\n\tTree,\n\ttype SimpleTreeSchema,\n\ttype TreeNode,\n} from \"@fluidframework/tree/internal\";\n// eslint-disable-next-line import/no-internal-modules\nimport { zodResponseFormat } from \"openai/helpers/zod\";\nimport type {\n\tChatCompletionCreateParams,\n\t// eslint-disable-next-line import/no-internal-modules\n} from \"openai/resources/index.mjs\";\nimport { z } from \"zod\";\n\nimport type { OpenAiClientOptions, TokenLimits, TokenUsage } from \"../aiCollabApi.js\";\n\nimport { applyAgentEdit } from \"./agentEditReducer.js\";\nimport type { EditWrapper, TreeEdit } from \"./agentEditTypes.js\";\nimport { IdGenerator } from \"./idGenerator.js\";\nimport {\n\tgetEditingSystemPrompt,\n\tgetPlanningSystemPrompt,\n\tgetReviewSystemPrompt,\n\ttoDecoratedJson,\n\ttype EditLog,\n} from \"./promptGeneration.js\";\nimport { generateGenericEditTypes } from \"./typeGeneration.js\";\nimport { fail } from \"./utils.js\";\n\nconst DEBUG_LOG: string[] = [];\n\n/**\n * {@link generateTreeEdits} options.\n *\n * @internal\n */\nexport interface GenerateTreeEditsOptions {\n\topenAI: OpenAiClientOptions;\n\ttreeNode: TreeNode;\n\tprompt: {\n\t\tsystemRoleContext: string;\n\t\tuserAsk: string;\n\t};\n\tlimiters?: {\n\t\tabortController?: AbortController;\n\t\tmaxSequentialErrors?: number;\n\t\tmaxModelCalls?: number;\n\t\ttokenLimits?: TokenLimits;\n\t};\n\tfinalReviewStep?: boolean;\n\tvalidator?: (newContent: TreeNode) => void;\n\tdumpDebugLog?: boolean;\n\tplanningStep?: boolean;\n}\n\ninterface GenerateTreeEditsSuccessResponse {\n\tstatus: \"success\";\n\ttokensUsed: TokenUsage;\n}\n\ninterface GenerateTreeEditsErrorResponse {\n\tstatus: \"failure\" | \"partial-failure\";\n\terrorMessage: \"tokenLimitExceeded\" | \"tooManyErrors\" | \"tooManyModelCalls\" | \"aborted\";\n\ttokensUsed: TokenUsage;\n}\n\n/**\n * Prompts the provided LLM client to generate valid tree edits.\n * Applies those edits to the provided tree branch before returning.\n *\n * @remarks\n * - Optional root nodes are not supported\n * - Primitive root nodes are not supported\n *\n * @internal\n */\nexport async function generateTreeEdits(\n\toptions: GenerateTreeEditsOptions,\n): Promise<GenerateTreeEditsSuccessResponse | GenerateTreeEditsErrorResponse> {\n\tconst idGenerator = new IdGenerator();\n\tconst editLog: EditLog = [];\n\tlet editCount = 0;\n\tlet sequentialErrorCount = 0;\n\n\tconst simpleSchema = getSimpleSchema(Tree.schema(options.treeNode));\n\n\tconst tokensUsed = { inputTokens: 0, outputTokens: 0 };\n\n\ttry {\n\t\tfor await (const edit of generateEdits(\n\t\t\toptions,\n\t\t\tsimpleSchema,\n\t\t\tidGenerator,\n\t\t\teditLog,\n\t\t\toptions.limiters?.tokenLimits,\n\t\t\ttokensUsed,\n\t\t)) {\n\t\t\ttry {\n\t\t\t\tconst result = applyAgentEdit(\n\t\t\t\t\tedit,\n\t\t\t\t\tidGenerator,\n\t\t\t\t\tsimpleSchema.definitions,\n\t\t\t\t\toptions.validator,\n\t\t\t\t);\n\t\t\t\tconst explanation = result.explanation;\n\t\t\t\teditLog.push({ edit: { ...result, explanation } });\n\t\t\t\tsequentialErrorCount = 0;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tsequentialErrorCount += 1;\n\t\t\t\t\teditLog.push({ edit, error: error.message });\n\t\t\t\t\tDEBUG_LOG?.push(`Error: ${error.message}`);\n\t\t\t\t} else {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst responseStatus =\n\t\t\t\teditCount > 0 && sequentialErrorCount < editCount ? \"partial-failure\" : \"failure\";\n\n\t\t\tif (options.limiters?.abortController?.signal.aborted === true) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: responseStatus,\n\t\t\t\t\terrorMessage: \"aborted\",\n\t\t\t\t\ttokensUsed,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tsequentialErrorCount >\n\t\t\t\t(options.limiters?.maxSequentialErrors ?? Number.POSITIVE_INFINITY)\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: responseStatus,\n\t\t\t\t\terrorMessage: \"tooManyErrors\",\n\t\t\t\t\ttokensUsed,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (++editCount >= (options.limiters?.maxModelCalls ?? Number.POSITIVE_INFINITY)) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: responseStatus,\n\t\t\t\t\terrorMessage: \"tooManyModelCalls\",\n\t\t\t\t\ttokensUsed,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t} catch (error: unknown) {\n\t\tif (error instanceof Error) {\n\t\t\tDEBUG_LOG?.push(`Error: ${error.message}`);\n\t\t}\n\n\t\tif (options.dumpDebugLog ?? false) {\n\t\t\tconsole.log(DEBUG_LOG.join(\"\\n\\n\"));\n\t\t\tDEBUG_LOG.length = 0;\n\t\t}\n\n\t\tif (error instanceof TokenLimitExceededError) {\n\t\t\treturn {\n\t\t\t\tstatus:\n\t\t\t\t\teditCount > 0 && sequentialErrorCount < editCount ? \"partial-failure\" : \"failure\",\n\t\t\t\terrorMessage: \"tokenLimitExceeded\",\n\t\t\t\ttokensUsed,\n\t\t\t};\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (options.dumpDebugLog ?? false) {\n\t\tconsole.log(DEBUG_LOG.join(\"\\n\\n\"));\n\t\tDEBUG_LOG.length = 0;\n\t}\n\n\treturn {\n\t\tstatus: \"success\",\n\t\ttokensUsed,\n\t};\n}\n\ninterface ReviewResult {\n\tgoalAccomplished: \"yes\" | \"no\";\n}\n\n/**\n * Generates a single {@link TreeEdit} from an LLM.\n *\n * @remarks\n * The design of this async generator function is such that which each iteration of this functions values,\n * an LLM will be prompted to generate the next value (a {@link TreeEdit}) based on the users ask.\n * Once the LLM believes it has completed the user's ask, it will no longer return an edit and as a result\n * this generator will no longer yield a next value.\n */\nasync function* generateEdits(\n\toptions: GenerateTreeEditsOptions,\n\tsimpleSchema: SimpleTreeSchema,\n\tidGenerator: IdGenerator,\n\teditLog: EditLog,\n\ttokenLimits: TokenLimits | undefined,\n\ttokensUsed: TokenUsage,\n): AsyncGenerator<TreeEdit> {\n\tconst [types, rootTypeName] = generateGenericEditTypes(simpleSchema, true);\n\n\tlet plan: string | undefined;\n\tif (options.planningStep !== undefined) {\n\t\tconst planningPromt = getPlanningSystemPrompt(\n\t\t\toptions.treeNode,\n\t\t\toptions.prompt.userAsk,\n\t\t\toptions.prompt.systemRoleContext,\n\t\t);\n\t\tDEBUG_LOG?.push(planningPromt);\n\t\tplan = await getStringFromLlm(planningPromt, options.openAI, tokensUsed);\n\t\tDEBUG_LOG?.push(`AI Generated the following plan: ${planningPromt}`);\n\t}\n\n\tconst originalDecoratedJson =\n\t\t(options.finalReviewStep ?? false)\n\t\t\t? toDecoratedJson(idGenerator, options.treeNode)\n\t\t\t: undefined;\n\t// reviewed is implicitly true if finalReviewStep is false\n\tlet hasReviewed = (options.finalReviewStep ?? false) ? false : true;\n\tasync function getNextEdit(): Promise<TreeEdit | undefined> {\n\t\tconst systemPrompt = getEditingSystemPrompt(\n\t\t\toptions.prompt.userAsk,\n\t\t\tidGenerator,\n\t\t\toptions.treeNode,\n\t\t\teditLog,\n\t\t\toptions.prompt.systemRoleContext,\n\t\t\tplan,\n\t\t);\n\n\t\tDEBUG_LOG?.push(systemPrompt);\n\n\t\tconst schema = types[rootTypeName] ?? fail(\"Root type not found.\");\n\t\tconst wrapper = await getStructuredOutputFromLlm<EditWrapper>(\n\t\t\tsystemPrompt,\n\t\t\toptions.openAI,\n\t\t\tschema,\n\t\t\t\"A JSON object that represents an edit to a JSON tree.\",\n\t\t\ttokensUsed,\n\t\t);\n\n\t\t// eslint-disable-next-line unicorn/no-null\n\t\tDEBUG_LOG?.push(JSON.stringify(wrapper, null, 2));\n\t\tif (wrapper === undefined) {\n\t\t\tDEBUG_LOG?.push(\"Failed to get response\");\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (wrapper.edit === null) {\n\t\t\tDEBUG_LOG?.push(\"No more edits.\");\n\t\t\tif ((options.finalReviewStep ?? false) && !hasReviewed) {\n\t\t\t\tconst reviewResult = await reviewGoal();\n\t\t\t\tif (reviewResult === undefined) {\n\t\t\t\t\tDEBUG_LOG?.push(\"Failed to get review response\");\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\t// eslint-disable-next-line require-atomic-updates\n\t\t\t\thasReviewed = true;\n\t\t\t\tif (reviewResult.goalAccomplished === \"yes\") {\n\t\t\t\t\treturn undefined;\n\t\t\t\t} else {\n\t\t\t\t\t// eslint-disable-next-line require-atomic-updates\n\t\t\t\t\teditLog.length = 0;\n\t\t\t\t\treturn getNextEdit();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn wrapper.edit;\n\t\t}\n\t}\n\n\tasync function reviewGoal(): Promise<ReviewResult | undefined> {\n\t\tconst systemPrompt = getReviewSystemPrompt(\n\t\t\toptions.prompt.userAsk,\n\t\t\tidGenerator,\n\t\t\toptions.treeNode,\n\t\t\toriginalDecoratedJson ?? fail(\"Original decorated tree not provided.\"),\n\t\t\toptions.prompt.systemRoleContext,\n\t\t);\n\n\t\tDEBUG_LOG?.push(systemPrompt);\n\n\t\tconst schema = z.object({\n\t\t\tgoalAccomplished: z\n\t\t\t\t.enum([\"yes\", \"no\"])\n\t\t\t\t.describe('Whether the user\\'s goal was met in the \"after\" tree.'),\n\t\t});\n\t\treturn getStructuredOutputFromLlm<ReviewResult>(systemPrompt, options.openAI, schema);\n\t}\n\n\tlet edit = await getNextEdit();\n\twhile (edit !== undefined) {\n\t\tyield edit;\n\t\tif (tokensUsed.inputTokens > (tokenLimits?.inputTokens ?? Number.POSITIVE_INFINITY)) {\n\t\t\tthrow new TokenLimitExceededError(\"Input token limit exceeded.\");\n\t\t}\n\t\tif (tokensUsed.outputTokens > (tokenLimits?.outputTokens ?? Number.POSITIVE_INFINITY)) {\n\t\t\tthrow new TokenLimitExceededError(\"Output token limit exceeded.\");\n\t\t}\n\t\tedit = await getNextEdit();\n\t}\n}\n\n/**\n * Calls the LLM to generate a structured output response based on the provided prompt.\n */\nasync function getStructuredOutputFromLlm<T>(\n\tprompt: string,\n\topenAi: OpenAiClientOptions,\n\tstructuredOutputSchema: Zod.ZodTypeAny,\n\tdescription?: string,\n\ttokensUsed?: TokenUsage,\n): Promise<T | undefined> {\n\tconst response_format = zodResponseFormat(structuredOutputSchema, \"SharedTreeAI\", {\n\t\tdescription,\n\t});\n\n\tconst body: ChatCompletionCreateParams = {\n\t\tmessages: [{ role: \"system\", content: prompt }],\n\t\tmodel: openAi.modelName ?? \"gpt-4o\",\n\t\tresponse_format,\n\t};\n\n\tconst result = await openAi.client.beta.chat.completions.parse(body);\n\n\tif (result.usage !== undefined && tokensUsed !== undefined) {\n\t\ttokensUsed.inputTokens += result.usage?.prompt_tokens;\n\t\ttokensUsed.outputTokens += result.usage?.completion_tokens;\n\t}\n\n\t// TODO: fix types so this isn't null and doesn't need a cast\n\t// The type should be derived from the zod schema\n\treturn result.choices[0]?.message.parsed as T | undefined;\n}\n\n/**\n * Calls the LLM to generate a response based on the provided prompt.\n */\nasync function getStringFromLlm(\n\tprompt: string,\n\topenAi: OpenAiClientOptions,\n\ttokensUsed?: TokenUsage,\n): Promise<string | undefined> {\n\tconst body: ChatCompletionCreateParams = {\n\t\tmessages: [{ role: \"system\", content: prompt }],\n\t\tmodel: openAi.modelName ?? \"gpt-4o\",\n\t};\n\n\tconst result = await openAi.client.chat.completions.create(body);\n\n\tif (result.usage !== undefined && tokensUsed !== undefined) {\n\t\ttokensUsed.inputTokens += result.usage?.prompt_tokens;\n\t\ttokensUsed.outputTokens += result.usage?.completion_tokens;\n\t}\n\n\treturn result.choices[0]?.message.content ?? undefined;\n}\n\nclass TokenLimitExceededError extends Error {}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/explicit-strategy/index.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { UsageError } from \"@fluidframework/telemetry-utils/internal\";\nimport {\n\tgetSimpleSchema,\n\tTree,\n\ttype SimpleTreeSchema,\n\ttype TreeNode,\n} from \"@fluidframework/tree/internal\";\n// eslint-disable-next-line import/no-internal-modules\nimport { zodResponseFormat } from \"openai/helpers/zod\";\nimport type {\n\tChatCompletionCreateParams,\n\t// eslint-disable-next-line import/no-internal-modules\n} from \"openai/resources/index.mjs\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { z } from \"zod\";\n\nimport type {\n\tDebugEventLogHandler,\n\tOpenAiClientOptions,\n\tTokenLimits,\n\tTokenUsage,\n} from \"../aiCollabApi.js\";\n\nimport { applyAgentEdit } from \"./agentEditReducer.js\";\nimport type { EditWrapper, TreeEdit } from \"./agentEditTypes.js\";\nimport {\n\ttype ApplyEditFailure,\n\ttype ApplyEditSuccess,\n\ttype GenerateTreeEditCompleted,\n\ttype GenerateTreeEditStarted,\n\ttype FinalReviewCompleted,\n\ttype FinalReviewStarted,\n\ttype LlmApiCallDebugEvent,\n\ttype PlanningPromptCompleted,\n\ttype CoreEventLoopStarted,\n\ttype CoreEventLoopCompleted,\n\tgenerateDebugEvent,\n\ttype PlanningPromptStarted,\n\ttype EventFlowDebugName,\n\tEventFlowDebugNames,\n} from \"./debugEvents.js\";\nimport { IdGenerator } from \"./idGenerator.js\";\nimport {\n\tgetEditingSystemPrompt,\n\tgetPlanningSystemPrompt,\n\tgetReviewSystemPrompt,\n\ttoDecoratedJson,\n\ttype EditLog,\n} from \"./promptGeneration.js\";\nimport { generateGenericEditTypes } from \"./typeGeneration.js\";\nimport { fail } from \"./utils.js\";\n\n// TODO: Create a proper index file and move the logic of this file to a new location\nexport type {\n\tApplyEditFailure,\n\tApplyEditSuccess,\n\tCoreEventLoopCompleted,\n\tCoreEventLoopStarted,\n\tFinalReviewCompleted,\n\tFinalReviewStarted,\n\tGenerateTreeEditCompleted,\n\tGenerateTreeEditStarted,\n\tLlmApiCallDebugEvent,\n\tPlanningPromptCompleted,\n\tPlanningPromptStarted,\n\tLlmTreeEdit,\n\tEventFlowDebugName,\n\tEventFlowDebugNames,\n} from \"./debugEvents.js\";\n\n/**\n * {@link generateTreeEdits} options.\n *\n * @internal\n */\nexport interface GenerateTreeEditsOptions {\n\topenAI: OpenAiClientOptions;\n\ttreeNode: TreeNode;\n\tprompt: {\n\t\tsystemRoleContext: string;\n\t\tuserAsk: string;\n\t};\n\tlimiters?: {\n\t\tabortController?: AbortController;\n\t\tmaxSequentialErrors?: number;\n\t\tmaxModelCalls?: number;\n\t\ttokenLimits?: TokenLimits;\n\t};\n\tfinalReviewStep?: boolean;\n\tvalidator?: (newContent: TreeNode) => void;\n\tdebugEventLogHandler?: DebugEventLogHandler;\n\tplanningStep?: boolean;\n}\n\ninterface GenerateTreeEditsSuccessResponse {\n\tstatus: \"success\";\n\ttokensUsed: TokenUsage;\n}\n\ninterface GenerateTreeEditsErrorResponse {\n\tstatus: \"failure\" | \"partial-failure\";\n\terrorMessage:\n\t\t| \"tokenLimitExceeded\"\n\t\t| \"tooManyErrors\"\n\t\t| \"tooManyModelCalls\"\n\t\t| \"aborted\"\n\t\t| \"unexpectedError\";\n\ttokensUsed: TokenUsage;\n}\n\n/**\n * Prompts the provided LLM client to generate valid tree edits.\n * Applies those edits to the provided tree branch before returning.\n *\n * @remarks\n * - Optional root nodes are not supported\n * - Primitive root nodes are not supported\n *\n * @internal\n */\nexport async function generateTreeEdits(\n\toptions: GenerateTreeEditsOptions,\n): Promise<GenerateTreeEditsSuccessResponse | GenerateTreeEditsErrorResponse> {\n\tconst idGenerator = new IdGenerator();\n\tconst editLog: EditLog = [];\n\tlet editCount = 0;\n\tlet sequentialErrorCount = 0;\n\n\tconst simpleSchema = getSimpleSchema(Tree.schema(options.treeNode));\n\n\tconst tokensUsed = { inputTokens: 0, outputTokens: 0 };\n\n\tconst debugLogTraceId = uuidv4();\n\n\tconst coreEventFlowTraceId = uuidv4();\n\toptions.debugEventLogHandler?.({\n\t\t...generateDebugEvent(\"CORE_EVENT_LOOP_STARTED\", debugLogTraceId),\n\t\teventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,\n\t\teventFlowStatus: \"STARTED\",\n\t\teventFlowTraceId: coreEventFlowTraceId,\n\t} satisfies CoreEventLoopStarted);\n\n\ttry {\n\t\tfor await (const generateEditResult of generateEdits(\n\t\t\toptions,\n\t\t\tsimpleSchema,\n\t\t\tidGenerator,\n\t\t\teditLog,\n\t\t\toptions.limiters?.tokenLimits,\n\t\t\ttokensUsed,\n\t\t\toptions.debugEventLogHandler && {\n\t\t\t\teventLogHandler: options.debugEventLogHandler,\n\t\t\t\ttraceId: debugLogTraceId,\n\t\t\t},\n\t\t)) {\n\t\t\ttry {\n\t\t\t\tconst result = applyAgentEdit(\n\t\t\t\t\tgenerateEditResult.edit,\n\t\t\t\t\tidGenerator,\n\t\t\t\t\tsimpleSchema.definitions,\n\t\t\t\t\toptions.validator,\n\t\t\t\t);\n\t\t\t\tconst explanation = result.explanation;\n\t\t\t\teditLog.push({ edit: { ...result, explanation } });\n\t\t\t\tsequentialErrorCount = 0;\n\n\t\t\t\toptions.debugEventLogHandler?.({\n\t\t\t\t\t...generateDebugEvent(\"APPLIED_EDIT_SUCCESS\", debugLogTraceId),\n\t\t\t\t\teventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,\n\t\t\t\t\teventFlowStatus: \"IN_PROGRESS\",\n\t\t\t\t\teventFlowTraceId: generateEditResult.eventFlowTraceId,\n\t\t\t\t\tedit: generateEditResult.edit as unknown as Record<string, unknown>,\n\t\t\t\t} satisfies ApplyEditSuccess);\n\t\t\t} catch (error: unknown) {\n\t\t\t\toptions.debugEventLogHandler?.({\n\t\t\t\t\t...generateDebugEvent(\"APPLIED_EDIT_FAILURE\", debugLogTraceId),\n\t\t\t\t\teventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,\n\t\t\t\t\teventFlowStatus: \"IN_PROGRESS\",\n\t\t\t\t\teventFlowTraceId: generateEditResult.eventFlowTraceId,\n\t\t\t\t\tedit: generateEditResult.edit as unknown as Record<string, unknown>,\n\t\t\t\t\terrorMessage: (error as Error)?.message,\n\t\t\t\t\tsequentialErrorCount,\n\t\t\t\t} satisfies ApplyEditFailure);\n\n\t\t\t\tif (error instanceof UsageError) {\n\t\t\t\t\tsequentialErrorCount += 1;\n\t\t\t\t\teditLog.push({ edit: generateEditResult.edit, error: error.message });\n\t\t\t\t} else {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet shouldExitEarly = false;\n\t\t\tconst completionResponse: GenerateTreeEditsErrorResponse = {\n\t\t\t\tstatus:\n\t\t\t\t\teditCount > 0 && sequentialErrorCount < editCount ? \"partial-failure\" : \"failure\",\n\t\t\t\terrorMessage: \"unexpectedError\",\n\t\t\t\ttokensUsed,\n\t\t\t};\n\n\t\t\tif (options.limiters?.abortController?.signal.aborted === true) {\n\t\t\t\tcompletionResponse.errorMessage = \"aborted\";\n\t\t\t\tshouldExitEarly = true;\n\t\t\t} else if (\n\t\t\t\tsequentialErrorCount >\n\t\t\t\t(options.limiters?.maxSequentialErrors ?? Number.POSITIVE_INFINITY)\n\t\t\t) {\n\t\t\t\tcompletionResponse.errorMessage = \"tooManyErrors\";\n\t\t\t\tshouldExitEarly = true;\n\t\t\t} else if (\n\t\t\t\t++editCount >= (options.limiters?.maxModelCalls ?? Number.POSITIVE_INFINITY)\n\t\t\t) {\n\t\t\t\tcompletionResponse.errorMessage = \"tooManyModelCalls\";\n\t\t\t\tshouldExitEarly = true;\n\t\t\t}\n\n\t\t\tif (shouldExitEarly) {\n\t\t\t\toptions.debugEventLogHandler?.({\n\t\t\t\t\t...generateDebugEvent(\"CORE_EVENT_LOOP_COMPLETED\", debugLogTraceId),\n\t\t\t\t\teventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,\n\t\t\t\t\teventFlowStatus: \"COMPLETED\",\n\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\tfailureReason: completionResponse.errorMessage,\n\t\t\t\t\teventFlowTraceId: coreEventFlowTraceId,\n\t\t\t\t} satisfies CoreEventLoopCompleted);\n\n\t\t\t\treturn completionResponse;\n\t\t\t}\n\t\t}\n\t} catch (error: unknown) {\n\t\toptions.debugEventLogHandler?.({\n\t\t\t...generateDebugEvent(\"CORE_EVENT_LOOP_COMPLETED\", debugLogTraceId),\n\t\t\teventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,\n\t\t\teventFlowStatus: \"COMPLETED\",\n\t\t\tstatus: \"failure\",\n\t\t\teventFlowTraceId: coreEventFlowTraceId,\n\t\t\tfailureReason:\n\t\t\t\terror instanceof TokenLimitExceededError ? \"tokenLimitExceeded\" : \"unexpectedError\",\n\t\t\terrorMessage: (error as Error)?.message,\n\t\t} satisfies CoreEventLoopCompleted);\n\n\t\tif (error instanceof TokenLimitExceededError) {\n\t\t\treturn {\n\t\t\t\tstatus:\n\t\t\t\t\teditCount > 0 && sequentialErrorCount < editCount ? \"partial-failure\" : \"failure\",\n\t\t\t\terrorMessage: \"tokenLimitExceeded\",\n\t\t\t\ttokensUsed,\n\t\t\t};\n\t\t}\n\t\tthrow error;\n\t}\n\n\toptions.debugEventLogHandler?.({\n\t\t...generateDebugEvent(\"CORE_EVENT_LOOP_COMPLETED\", debugLogTraceId),\n\t\teventFlowName: EventFlowDebugNames.CORE_EVENT_LOOP,\n\t\teventFlowStatus: \"COMPLETED\",\n\t\teventFlowTraceId: coreEventFlowTraceId,\n\t\tstatus: \"success\",\n\t} satisfies CoreEventLoopCompleted);\n\n\treturn {\n\t\tstatus: \"success\",\n\t\ttokensUsed,\n\t};\n}\n\ninterface ReviewResult {\n\tgoalAccomplished: \"yes\" | \"no\";\n}\n\n/**\n * Generates a single {@link TreeEdit} from an LLM.\n *\n * @remarks\n * The design of this async generator function is such that which each iteration of this functions values,\n * an LLM will be prompted to generate the next value (a {@link TreeEdit}) based on the users ask.\n * Once the LLM believes it has completed the user's ask, it will no longer return an edit and as a result\n * this generator will no longer yield a next value.\n */\nasync function* generateEdits(\n\toptions: GenerateTreeEditsOptions,\n\tsimpleSchema: SimpleTreeSchema,\n\tidGenerator: IdGenerator,\n\teditLog: EditLog,\n\ttokenLimits: TokenLimits | undefined,\n\ttokensUsed: TokenUsage,\n\tdebugOptions?: {\n\t\teventLogHandler: DebugEventLogHandler;\n\t\ttraceId: string;\n\t},\n): AsyncGenerator<{ edit: TreeEdit; eventFlowTraceId: string }> {\n\tconst [types, rootTypeName] = generateGenericEditTypes(simpleSchema, true);\n\n\tlet plan: string | undefined;\n\tif (options.planningStep !== undefined) {\n\t\tconst planningPrompt = getPlanningSystemPrompt(\n\t\t\toptions.treeNode,\n\t\t\toptions.prompt.userAsk,\n\t\t\toptions.prompt.systemRoleContext,\n\t\t);\n\n\t\tconst generatePlanningPromptEventFlowId = uuidv4();\n\t\tdebugOptions?.eventLogHandler?.({\n\t\t\t...generateDebugEvent(\"GENERATE_PLANNING_PROMPT_STARTED\", debugOptions.traceId),\n\t\t\teventFlowName: EventFlowDebugNames.GENERATE_PLANNING_PROMPT,\n\t\t\teventFlowTraceId: generatePlanningPromptEventFlowId,\n\t\t\teventFlowStatus: \"STARTED\",\n\t\t} satisfies PlanningPromptStarted);\n\n\t\tplan = await getStringFromLlm(\n\t\t\tplanningPrompt,\n\t\t\toptions.openAI,\n\t\t\ttokensUsed,\n\t\t\tdebugOptions && {\n\t\t\t\t...debugOptions,\n\t\t\t\ttriggeringEventFlowName: EventFlowDebugNames.GENERATE_PLANNING_PROMPT,\n\t\t\t\teventFlowTraceId: generatePlanningPromptEventFlowId,\n\t\t\t},\n\t\t);\n\n\t\tdebugOptions?.eventLogHandler?.({\n\t\t\t...generateDebugEvent(\"GENERATE_PLANNING_PROMPT_COMPLETED\", debugOptions.traceId),\n\t\t\teventFlowName: EventFlowDebugNames.GENERATE_PLANNING_PROMPT,\n\t\t\teventFlowStatus: \"COMPLETED\",\n\t\t\teventFlowTraceId: generatePlanningPromptEventFlowId,\n\t\t\tisLlmResponseValid: plan !== undefined,\n\t\t\tllmGeneratedPlan: plan,\n\t\t} satisfies PlanningPromptCompleted);\n\t}\n\n\tconst originalDecoratedJson =\n\t\t(options.finalReviewStep ?? false)\n\t\t\t? toDecoratedJson(idGenerator, options.treeNode)\n\t\t\t: undefined;\n\t// reviewed is implicitly true if finalReviewStep is false\n\tlet hasReviewed = (options.finalReviewStep ?? false) ? false : true;\n\tasync function getNextEdit(): Promise<\n\t\t{ edit: TreeEdit; eventFlowTraceId: string } | undefined\n\t> {\n\t\tconst systemPrompt = getEditingSystemPrompt(\n\t\t\toptions.prompt.userAsk,\n\t\t\tidGenerator,\n\t\t\toptions.treeNode,\n\t\t\teditLog,\n\t\t\toptions.prompt.systemRoleContext,\n\t\t\tplan,\n\t\t);\n\n\t\tconst schema = types[rootTypeName] ?? fail(\"Root type not found.\");\n\n\t\tconst generateTreeEditEventFlowId = uuidv4();\n\t\tdebugOptions?.eventLogHandler?.({\n\t\t\t...generateDebugEvent(\"GENERATE_TREE_EDIT_STARTED\", debugOptions.traceId),\n\t\t\teventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,\n\t\t\teventFlowStatus: \"STARTED\",\n\t\t\teventFlowTraceId: generateTreeEditEventFlowId,\n\t\t\tllmPrompt: systemPrompt,\n\t\t} satisfies GenerateTreeEditStarted);\n\n\t\tconst wrapper = await getStructuredOutputFromLlm<EditWrapper>(\n\t\t\tsystemPrompt,\n\t\t\toptions.openAI,\n\t\t\tschema,\n\t\t\t\"A JSON object that represents an edit to a JSON tree.\",\n\t\t\ttokensUsed,\n\t\t\tdebugOptions && {\n\t\t\t\t...debugOptions,\n\t\t\t\ttriggeringEventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,\n\t\t\t\teventFlowTraceId: generateTreeEditEventFlowId,\n\t\t\t},\n\t\t);\n\n\t\tdebugOptions?.eventLogHandler?.({\n\t\t\t...generateDebugEvent(\"GENERATE_TREE_EDIT_COMPLETED\", debugOptions.traceId),\n\t\t\teventFlowName: EventFlowDebugNames.GENERATE_AND_APPLY_TREE_EDIT,\n\t\t\teventFlowStatus: \"COMPLETED\",\n\t\t\teventFlowTraceId: generateTreeEditEventFlowId,\n\t\t\tisLlmResponseValid: wrapper?.edit !== undefined,\n\t\t\tllmGeneratedEdit: wrapper?.edit as Record<string, unknown> | null,\n\t\t} satisfies GenerateTreeEditCompleted);\n\n\t\tif (wrapper === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (wrapper.edit === null) {\n\t\t\tif ((options.finalReviewStep ?? false) && !hasReviewed) {\n\t\t\t\tconst reviewResult = await reviewGoal();\n\t\t\t\tif (reviewResult === undefined) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\t// eslint-disable-next-line require-atomic-updates\n\t\t\t\thasReviewed = true;\n\t\t\t\tif (reviewResult.goalAccomplished === \"yes\") {\n\t\t\t\t\treturn undefined;\n\t\t\t\t} else {\n\t\t\t\t\t// eslint-disable-next-line require-atomic-updates\n\t\t\t\t\teditLog.length = 0;\n\t\t\t\t\treturn getNextEdit();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn { edit: wrapper.edit, eventFlowTraceId: generateTreeEditEventFlowId };\n\t\t}\n\t}\n\n\tasync function reviewGoal(): Promise<ReviewResult | undefined> {\n\t\tconst systemPrompt = getReviewSystemPrompt(\n\t\t\toptions.prompt.userAsk,\n\t\t\tidGenerator,\n\t\t\toptions.treeNode,\n\t\t\toriginalDecoratedJson ?? fail(\"Original decorated tree not provided.\"),\n\t\t\toptions.prompt.systemRoleContext,\n\t\t);\n\n\t\tconst schema = z.object({\n\t\t\tgoalAccomplished: z\n\t\t\t\t.enum([\"yes\", \"no\"])\n\t\t\t\t.describe('Whether the user\\'s goal was met in the \"after\" tree.'),\n\t\t});\n\n\t\tconst finalReviewEventFlowTraceId = uuidv4();\n\t\tdebugOptions?.eventLogHandler?.({\n\t\t\t...generateDebugEvent(\"FINAL_REVIEW_STARTED\", debugOptions.traceId),\n\t\t\teventFlowName: EventFlowDebugNames.FINAL_REVIEW,\n\t\t\teventFlowStatus: \"STARTED\",\n\t\t\teventFlowTraceId: finalReviewEventFlowTraceId,\n\t\t\tllmPrompt: systemPrompt,\n\t\t} satisfies FinalReviewStarted);\n\n\t\t// TODO: In the future, when using structured output isn't guarenteed, we will\n\t\t// need to add a custom type guard to ensure that output is in the right shape.\n\t\tconst output = await getStructuredOutputFromLlm<ReviewResult>(\n\t\t\tsystemPrompt,\n\t\t\toptions.openAI,\n\t\t\tschema,\n\t\t\tundefined,\n\t\t\ttokensUsed,\n\t\t\tdebugOptions && {\n\t\t\t\t...debugOptions,\n\t\t\t\ttriggeringEventFlowName: EventFlowDebugNames.FINAL_REVIEW,\n\t\t\t\teventFlowTraceId: finalReviewEventFlowTraceId,\n\t\t\t},\n\t\t);\n\n\t\tdebugOptions?.eventLogHandler?.({\n\t\t\t...generateDebugEvent(\"FINAL_REVIEW_COMPLETED\", debugOptions.traceId),\n\t\t\teventFlowName: EventFlowDebugNames.FINAL_REVIEW,\n\t\t\teventFlowStatus: \"COMPLETED\",\n\t\t\teventFlowTraceId: finalReviewEventFlowTraceId,\n\t\t\tisLlmResponseValid: output !== undefined,\n\t\t\tdidLlmAccomplishGoal: output?.goalAccomplished,\n\t\t} satisfies FinalReviewCompleted);\n\n\t\treturn output;\n\t}\n\n\tlet edit = await getNextEdit();\n\twhile (edit !== undefined) {\n\t\tyield edit;\n\t\tif (tokensUsed.inputTokens > (tokenLimits?.inputTokens ?? Number.POSITIVE_INFINITY)) {\n\t\t\tthrow new TokenLimitExceededError(\"Input token limit exceeded.\");\n\t\t}\n\t\tif (tokensUsed.outputTokens > (tokenLimits?.outputTokens ?? Number.POSITIVE_INFINITY)) {\n\t\t\tthrow new TokenLimitExceededError(\"Output token limit exceeded.\");\n\t\t}\n\t\tedit = await getNextEdit();\n\t}\n}\n\n/**\n * Calls the LLM to generate a structured output response based on the provided prompt.\n */\nasync function getStructuredOutputFromLlm<T>(\n\tprompt: string,\n\topenAi: OpenAiClientOptions,\n\tstructuredOutputSchema: Zod.ZodTypeAny,\n\tdescription?: string,\n\ttokensUsed?: TokenUsage,\n\tdebugOptions?: {\n\t\teventLogHandler: DebugEventLogHandler;\n\t\ttraceId: string;\n\t\ttriggeringEventFlowName: EventFlowDebugName;\n\t\teventFlowTraceId: string;\n\t},\n): Promise<T | undefined> {\n\tconst response_format = zodResponseFormat(structuredOutputSchema, \"SharedTreeAI\", {\n\t\tdescription,\n\t});\n\n\tconst body: ChatCompletionCreateParams = {\n\t\tmessages: [{ role: \"system\", content: prompt }],\n\t\tmodel: openAi.modelName ?? \"gpt-4o\",\n\t\tresponse_format,\n\t};\n\n\tconst result = await openAi.client.beta.chat.completions.parse(body);\n\n\tdebugOptions?.eventLogHandler?.({\n\t\t...generateDebugEvent(\"LLM_API_CALL\", debugOptions.traceId),\n\t\ttriggeringEventFlowName: debugOptions.triggeringEventFlowName,\n\t\teventFlowTraceId: debugOptions.eventFlowTraceId,\n\t\tmodelName: openAi.modelName ?? \"gpt-4o\",\n\t\trequestParams: body,\n\t\tresponse: { ...result },\n\t\t...(result.usage && {\n\t\t\ttokenUsage: {\n\t\t\t\tpromptTokens: result.usage.prompt_tokens,\n\t\t\t\tcompletionTokens: result.usage.completion_tokens,\n\t\t\t},\n\t\t}),\n\t} satisfies LlmApiCallDebugEvent);\n\n\tif (result.usage !== undefined && tokensUsed !== undefined) {\n\t\ttokensUsed.inputTokens += result.usage?.prompt_tokens;\n\t\ttokensUsed.outputTokens += result.usage?.completion_tokens;\n\t}\n\n\t// TODO: fix types so this isn't null and doesn't need a cast\n\t// The type should be derived from the zod schema\n\t// TODO: Determine why this value would be undefined.\n\treturn result.choices[0]?.message.parsed as T | undefined;\n}\n\n/**\n * Calls the LLM to generate a response based on the provided prompt.\n */\nasync function getStringFromLlm(\n\tprompt: string,\n\topenAi: OpenAiClientOptions,\n\ttokensUsed?: TokenUsage,\n\tdebugOptions?: {\n\t\teventLogHandler: DebugEventLogHandler;\n\t\ttraceId: string;\n\t\ttriggeringEventFlowName: EventFlowDebugName;\n\t\teventFlowTraceId: string;\n\t},\n): Promise<string | undefined> {\n\tconst body: ChatCompletionCreateParams = {\n\t\tmessages: [{ role: \"system\", content: prompt }],\n\t\tmodel: openAi.modelName ?? \"gpt-4o\",\n\t};\n\n\tconst result = await openAi.client.chat.completions.create(body);\n\n\tdebugOptions?.eventLogHandler?.({\n\t\t...generateDebugEvent(\"LLM_API_CALL\", debugOptions.traceId),\n\t\ttriggeringEventFlowName: debugOptions.triggeringEventFlowName,\n\t\teventFlowTraceId: debugOptions.eventFlowTraceId,\n\t\tmodelName: openAi.modelName ?? \"gpt-4o\",\n\t\trequestParams: body,\n\t\tresponse: { ...result },\n\t\t...(result.usage && {\n\t\t\ttokenUsage: {\n\t\t\t\tpromptTokens: result.usage.prompt_tokens,\n\t\t\t\tcompletionTokens: result.usage.completion_tokens,\n\t\t\t},\n\t\t}),\n\t} satisfies LlmApiCallDebugEvent);\n\n\tif (result.usage !== undefined && tokensUsed !== undefined) {\n\t\ttokensUsed.inputTokens += result.usage?.prompt_tokens;\n\t\ttokensUsed.outputTokens += result.usage?.completion_tokens;\n\t}\n\n\treturn result.choices[0]?.message.content ?? undefined;\n}\n\nclass TokenLimitExceededError extends Error {}\n"]}
package/lib/index.d.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  * @packageDocumentation
12
12
  */
13
13
  export { type DifferenceCreate, type DifferenceChange, type DifferenceMove, type DifferenceRemove, type Difference, type ObjectPath, type Options, sharedTreeDiff, createMergableIdDiffSeries, createMergableDiffSeries, SharedTreeBranchManager, sharedTreeTraverse, } from "./implicit-strategy/index.js";
14
- export { type AiCollabOptions, type AiCollabSuccessResponse, type AiCollabErrorResponse, type TokenUsage, type TokenLimits, type OpenAiClientOptions, } from "./aiCollabApi.js";
14
+ export type { ApplyEditFailure, ApplyEditSuccess, CoreEventLoopCompleted, CoreEventLoopStarted, FinalReviewCompleted, FinalReviewStarted, GenerateTreeEditCompleted, GenerateTreeEditStarted, LlmApiCallDebugEvent, PlanningPromptCompleted, PlanningPromptStarted, LlmTreeEdit, EventFlowDebugName, EventFlowDebugNames, } from "./explicit-strategy/index.js";
15
+ export { type AiCollabOptions, type AiCollabSuccessResponse, type AiCollabErrorResponse, type TokenUsage, type TokenLimits, type OpenAiClientOptions, type DebugEvent, type DebugEventLogHandler, type EventFlowDebugEvent, } from "./aiCollabApi.js";
15
16
  export { aiCollab } from "./aiCollab.js";
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,cAAc,EACd,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,cAAc,EACd,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACX,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,mBAAmB,GACnB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,OAAO,EAQN,cAAc,EACd,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AAWtC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Experimental package for utilities that enable/simplify interaction with LLMs for apps based on SharedTree.\n *\n * See {@link https://github.com/microsoft/FluidFramework/tree/main/packages/framework/ai-collab#readme | README.md }\n * for an overview of the package.\n *\n * @packageDocumentation\n */\n\nexport {\n\ttype DifferenceCreate,\n\ttype DifferenceChange,\n\ttype DifferenceMove,\n\ttype DifferenceRemove,\n\ttype Difference,\n\ttype ObjectPath,\n\ttype Options,\n\tsharedTreeDiff,\n\tcreateMergableIdDiffSeries,\n\tcreateMergableDiffSeries,\n\tSharedTreeBranchManager,\n\tsharedTreeTraverse,\n} from \"./implicit-strategy/index.js\";\n\nexport {\n\ttype AiCollabOptions,\n\ttype AiCollabSuccessResponse,\n\ttype AiCollabErrorResponse,\n\ttype TokenUsage,\n\ttype TokenLimits,\n\ttype OpenAiClientOptions,\n} from \"./aiCollabApi.js\";\n\nexport { aiCollab } from \"./aiCollab.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,OAAO,EAQN,cAAc,EACd,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AA+BtC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Experimental package for utilities that enable/simplify interaction with LLMs for apps based on SharedTree.\n *\n * See {@link https://github.com/microsoft/FluidFramework/tree/main/packages/framework/ai-collab#readme | README.md }\n * for an overview of the package.\n *\n * @packageDocumentation\n */\n\nexport {\n\ttype DifferenceCreate,\n\ttype DifferenceChange,\n\ttype DifferenceMove,\n\ttype DifferenceRemove,\n\ttype Difference,\n\ttype ObjectPath,\n\ttype Options,\n\tsharedTreeDiff,\n\tcreateMergableIdDiffSeries,\n\tcreateMergableDiffSeries,\n\tSharedTreeBranchManager,\n\tsharedTreeTraverse,\n} from \"./implicit-strategy/index.js\";\n\nexport type {\n\tApplyEditFailure,\n\tApplyEditSuccess,\n\tCoreEventLoopCompleted,\n\tCoreEventLoopStarted,\n\tFinalReviewCompleted,\n\tFinalReviewStarted,\n\tGenerateTreeEditCompleted,\n\tGenerateTreeEditStarted,\n\tLlmApiCallDebugEvent,\n\tPlanningPromptCompleted,\n\tPlanningPromptStarted,\n\tLlmTreeEdit,\n\tEventFlowDebugName,\n\tEventFlowDebugNames,\n} from \"./explicit-strategy/index.js\";\n\nexport {\n\ttype AiCollabOptions,\n\ttype AiCollabSuccessResponse,\n\ttype AiCollabErrorResponse,\n\ttype TokenUsage,\n\ttype TokenLimits,\n\ttype OpenAiClientOptions,\n\ttype DebugEvent,\n\ttype DebugEventLogHandler,\n\ttype EventFlowDebugEvent,\n} from \"./aiCollabApi.js\";\n\nexport { aiCollab } from \"./aiCollab.js\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/ai-collab",
3
- "version": "2.22.1",
3
+ "version": "2.23.0",
4
4
  "description": "Experimental package to simplify integrating AI into Fluid-based applications",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -69,28 +69,30 @@
69
69
  "temp-directory": "nyc/.nyc_output"
70
70
  },
71
71
  "dependencies": {
72
- "@fluidframework/core-utils": "~2.22.1",
73
- "@fluidframework/runtime-utils": "~2.22.1",
74
- "@fluidframework/telemetry-utils": "~2.22.1",
75
- "@fluidframework/tree": "~2.22.1",
72
+ "@fluidframework/core-utils": "~2.23.0",
73
+ "@fluidframework/runtime-utils": "~2.23.0",
74
+ "@fluidframework/telemetry-utils": "~2.23.0",
75
+ "@fluidframework/tree": "~2.23.0",
76
76
  "openai": "^4.67.3",
77
77
  "typechat": "^0.1.1",
78
+ "uuid": "^9.0.0",
78
79
  "zod": "^3.23.8"
79
80
  },
80
81
  "devDependencies": {
81
82
  "@arethetypeswrong/cli": "^0.17.1",
82
83
  "@biomejs/biome": "~1.9.3",
83
- "@fluid-internal/mocha-test-setup": "~2.22.1",
84
- "@fluid-tools/build-cli": "^0.51.0",
84
+ "@fluid-internal/mocha-test-setup": "~2.23.0",
85
+ "@fluid-tools/build-cli": "^0.54.0",
85
86
  "@fluidframework/build-common": "^2.0.3",
86
- "@fluidframework/build-tools": "^0.51.0",
87
+ "@fluidframework/build-tools": "^0.54.0",
87
88
  "@fluidframework/eslint-config-fluid": "^5.7.3",
88
- "@fluidframework/id-compressor": "~2.22.1",
89
- "@fluidframework/runtime-utils": "~2.22.1",
90
- "@fluidframework/test-runtime-utils": "~2.22.1",
89
+ "@fluidframework/id-compressor": "~2.23.0",
90
+ "@fluidframework/runtime-utils": "~2.23.0",
91
+ "@fluidframework/test-runtime-utils": "~2.23.0",
91
92
  "@microsoft/api-extractor": "7.47.8",
92
93
  "@types/mocha": "^10.0.10",
93
94
  "@types/node": "^18.19.0",
95
+ "@types/uuid": "^9.0.2",
94
96
  "c8": "^8.0.1",
95
97
  "concurrently": "^8.2.1",
96
98
  "copyfiles": "^2.4.1",
package/src/aiCollab.ts CHANGED
@@ -53,7 +53,6 @@ import { generateTreeEdits } from "./explicit-strategy/index.js";
53
53
  * },
54
54
  * planningStep: true,
55
55
  * finalReviewStep: true,
56
- * dumpDebugLog: true,
57
56
  * });
58
57
  * ```
59
58
  *
@@ -77,9 +76,9 @@ export async function aiCollab(
77
76
  openAI: options.openAI,
78
77
  prompt: options.prompt,
79
78
  limiters: options.limiters,
80
- dumpDebugLog: options.dumpDebugLog,
81
79
  planningStep: options.planningStep,
82
80
  finalReviewStep: options.finalReviewStep,
81
+ debugEventLogHandler: options.debugEventLogHandler,
83
82
  });
84
83
 
85
84
  return response;