@api-client/core 0.19.7 → 0.19.8

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.
@@ -1,6 +1,7 @@
1
1
  import { AiMessageKind } from './kinds.js';
2
2
  import { nanoid } from '../nanoid.js';
3
3
  import { DataDomainDelta } from '../modeling/ai/DataDomainDelta.js';
4
+ import { detectReasoning } from '../modeling/ai/message_parser.js';
4
5
  /**
5
6
  * The base class for all AI messages.
6
7
  * It provides common functionality for all message types.
@@ -91,19 +92,32 @@ export class AiUserMessage extends AiMessageBase {
91
92
  };
92
93
  }
93
94
  }
95
+ /**
96
+ * Model messages have the `text` that most likely is a structured JSON
97
+ * with the `delta` and `reasoning` properties. When an `AiModelMessage` is instantiated,
98
+ * it will try to parse the `text` into the `delta` and `reasoning` properties, leaving the
99
+ * `text` property unchanged.
100
+ *
101
+ * The delta object will be different for different models and different use cases.
102
+ * It is up to the model to define the delta object. However, the delta object should
103
+ * be normalized by each child class.
104
+ *
105
+ * @template D The type of the delta.
106
+ */
94
107
  export class AiModelMessage extends AiMessageBase {
95
108
  state;
96
109
  applied;
97
110
  thoughts;
98
111
  /**
99
- * The delta of the message, if any.
112
+ * The extracted delta from the message.
100
113
  */
101
114
  delta;
102
115
  /**
103
- * The reasoning of the delta.
116
+ * The extracted reasoning from the message.
104
117
  * Format in markdown.
105
118
  */
106
119
  reasoning;
120
+ #reasoningEnded = false;
107
121
  static createSchema(input) {
108
122
  const base = AiMessageBase.createBaseSchema(input);
109
123
  return {
@@ -119,6 +133,7 @@ export class AiModelMessage extends AiMessageBase {
119
133
  this.state = input.state || 'loading';
120
134
  this.applied = input.applied;
121
135
  this.thoughts = input.thoughts || '';
136
+ this.processMessageSafe();
122
137
  }
123
138
  toJSON() {
124
139
  const result = {
@@ -143,6 +158,8 @@ export class AiModelMessage extends AiMessageBase {
143
158
  }
144
159
  /**
145
160
  * Adds a text chunk to the message. Useful when streaming.
161
+ *
162
+ * Note, this function progressively detects the reasoning on each chunk.
146
163
  * @param text The text chunk to add.
147
164
  */
148
165
  addText(text) {
@@ -150,6 +167,15 @@ export class AiModelMessage extends AiMessageBase {
150
167
  return;
151
168
  }
152
169
  this.text += text;
170
+ if (this.#reasoningEnded) {
171
+ return;
172
+ }
173
+ const before = this.reasoning;
174
+ const after = detectReasoning(this.text);
175
+ if (after && after === before) {
176
+ this.#reasoningEnded = true;
177
+ }
178
+ this.reasoning = after;
153
179
  }
154
180
  /**
155
181
  * Processes the message.
@@ -157,12 +183,13 @@ export class AiModelMessage extends AiMessageBase {
157
183
  * It sets the delta and reasoning fields, if found in the message.
158
184
  */
159
185
  processMessage() {
160
- if (!this.text) {
186
+ const { text } = this;
187
+ if (!text) {
161
188
  return;
162
189
  }
163
- const parsed = JSON.parse(this.text);
190
+ const parsed = JSON.parse(text);
164
191
  if (parsed.delta) {
165
- this.delta = DataDomainDelta.normalize(parsed.delta);
192
+ this.delta = this.normalizeDelta(parsed.delta);
166
193
  }
167
194
  if (parsed.reasoning) {
168
195
  this.reasoning = parsed.reasoning;
@@ -180,24 +207,24 @@ export class AiModelMessage extends AiMessageBase {
180
207
  // do nothing
181
208
  }
182
209
  }
210
+ }
211
+ /**
212
+ * A model message that contains a delta of type `AiDomainDelta`.
213
+ */
214
+ export class AiModelDataDomainMessage extends AiModelMessage {
183
215
  /**
184
216
  * Creates an empty model message. Useful when initializing LLM flow.
185
217
  * @param session The session key.
186
218
  * @returns An empty model message.
187
219
  */
188
220
  static createEmpty(session) {
189
- return new AiModelMessage({
221
+ return new AiModelDataDomainMessage({
190
222
  session,
191
223
  state: 'loading',
192
224
  });
193
225
  }
226
+ normalizeDelta(delta) {
227
+ return DataDomainDelta.normalize(delta);
228
+ }
194
229
  }
195
- export const AiMessageFactory = {
196
- fromSchema(schema) {
197
- if (schema.role === 'model') {
198
- return new AiModelMessage(schema);
199
- }
200
- return new AiUserMessage(schema);
201
- },
202
- };
203
230
  //# sourceMappingURL=AiMessage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiMessage.js","sourceRoot":"","sources":["../../../src/models/AiMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AA0GnE;;;GAGG;AACH,MAAM,OAAgB,aAAa;IAGjC;;OAEG;IACH,MAAM,KAAK,IAAI;QACb,OAAO,aAAa,CAAA;IACtB,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,aAAa,CAAA;IACtB,CAAC;IAED,GAAG,CAAQ;IACX,IAAI,CAAW;IACf,SAAS,CAAQ;IACjB,SAAS,CAAQ;IACjB,OAAO,CAAQ;IACf,IAAI,CAAQ;IACZ,OAAO,CAAS;IAChB,WAAW,CAAY;IAEb,MAAM,CAAC,gBAAgB,CAAC,KAAmC;QACnE,MAAM,EAAE,GAAG,GAAG,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,GAAG,KAAK,CAAA;QAClF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;QAChE,CAAC;QACD,OAAO;YACL,GAAG;YACH,IAAI,EAAE,aAAa;YACnB,OAAO;YACP,IAAI;YACJ,SAAS;YACT,SAAS;SACV,CAAA;IACH,CAAC;IAED,YAAY,QAAoB,EAAE,EAAE,IAAe;QACjD,MAAM,IAAI,GAAG,aAAa,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAClD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAA;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACrB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;QAC/B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;QAC/B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAA;QACrC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAA;IACtC,CAAC;IAED,MAAM;QACJ,MAAM,MAAM,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAA;QACD,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;gBACpB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,WAAW,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE;aACrC,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,MAAsB,CAAA;IAC/B,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAkC;IACnE,KAAK,CAAY;IAEjB,MAAM,CAAC,YAAY,CAAC,KAAmC;QACrD,MAAM,IAAI,GAAG,aAAa,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAClD,OAAO;YACL,GAAG,IAAI;YACP,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,UAAU;SAClB,CAAA;IACH,CAAC;IAED,YAAY,QAAsC,EAAE;QAClD,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACpB,IAAI,CAAC,KAAK,GAAG,UAAU,CAAA;IACzB,CAAC;IAEQ,MAAM;QACb,OAAO;YACL,GAAG,KAAK,CAAC,MAAM,EAAE;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAA;IACH,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,aAAmC;IACrE,KAAK,CAAgB;IACrB,OAAO,CAAU;IACjB,QAAQ,CAAQ;IAEhB;;OAEG;IACH,KAAK,CAAgB;IACrB;;;OAGG;IACH,SAAS,CAAS;IAElB,MAAM,CAAC,YAAY,CAAC,KAAoC;QACtD,MAAM,IAAI,GAAG,aAAa,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAClD,OAAO;YACL,GAAG,IAAI;YACP,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,SAAS;YAC/B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;SAC/B,CAAA;IACH,CAAC;IAED,YAAY,QAAuC,EAAE;QACnD,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,SAAS,CAAA;QACrC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAA;QAC5B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAA;IACtC,CAAC;IAEQ,MAAM;QACb,MAAM,MAAM,GAAyB;YACnC,GAAG,KAAK,CAAC,MAAM,EAAE;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAA;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAC/B,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,OAAgB;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAM;QACR,CAAC;QACD,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAA;IAC1B,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,IAAa;QACnB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAM;QACR,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAA;IACnB,CAAC;IAED;;;;OAIG;IACH,cAAc;QACZ,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACpC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACtD,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAA;QACnC,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,kBAAkB;QAChB,IAAI,CAAC;YACH,IAAI,CAAC,cAAc,EAAE,CAAA;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,OAAe;QAChC,OAAO,IAAI,cAAc,CAAC;YACxB,OAAO;YACP,KAAK,EAAE,SAAS;SACjB,CAAC,CAAA;IACJ,CAAC;CACF;AAOD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,UAAU,CAAC,MAAgC;QACzC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,OAAO,IAAI,cAAc,CAAC,MAAuC,CAAC,CAAA;QACpE,CAAC;QACD,OAAO,IAAI,aAAa,CAAC,MAAsC,CAAC,CAAA;IAClE,CAAC;CACF,CAAA","sourcesContent":["import type { IDeletion } from './store/Deletion.js'\nimport { AiMessageKind } from './kinds.js'\nimport { nanoid } from '../nanoid.js'\nimport type { AiDomainDelta } from '../modeling/ai/types.js'\nimport { DataDomainDelta } from '../modeling/ai/DataDomainDelta.js'\n\nexport type AiMessageRole = 'user' | 'model'\nexport type AiMessageState = 'loading' | 'complete' | 'error' | 'terminated'\n\n/**\n * The base interface representing a single message in the domain manipulation chat history.\n * It tracks metadata like timestamps, the associated session, and the raw text content.\n * See `AiUserMessage` and `AiModelMessage` for specific role implementations.\n */\nexport interface AiMessageSchemaBase {\n readonly kind: typeof AiMessageKind\n /**\n * The datastore key of the message.\n */\n key: string\n /**\n * The role of the message.\n */\n role: AiMessageRole\n /**\n * The timestamp of when the message was created.\n */\n createdAt: number\n /**\n * The timestamp of when the message was last updated.\n */\n updatedAt: number\n /**\n * The datastore key of the session.\n */\n session: string\n /**\n * The text of the message.\n */\n text: string\n /**\n * Whether the message is deleted.\n */\n deleted?: boolean\n /**\n * The information about the delete information.\n * Always set when the `deleted` is true.\n */\n deletedInfo?: IDeletion\n}\n\n/**\n * The schema for a user message.\n */\nexport interface AiUserMessageSchema extends AiMessageSchemaBase {\n /**\n * The role of the message.\n */\n role: 'user'\n /**\n * The state of the user message.\n * Can only be `complete`.\n */\n state: 'complete'\n /**\n * The user utterance.\n * Note, the actual message sent to the LLM may be different from the utterance\n * as it has additional instructions and the domain context. However, we are not storing\n * the full message in the datastore, only the utterance.\n */\n text: string\n}\n\n/**\n * The schema for a model message.\n */\nexport interface AiModelMessageSchema extends AiMessageSchemaBase {\n /**\n * The role of the message.\n */\n role: 'model'\n /**\n * The full text of the message returned by the LLM.\n * It is most likely a structured message that needs to be parsed\n * to the corresponding message object.\n */\n text: string\n /**\n * The state of the message.\n * - loading: the message is being generated\n * - complete: the message is complete\n * - error: the message generation failed\n * - terminated: the message generation was terminated by the user\n */\n state: AiMessageState\n /**\n * Whether the message has been applied.\n */\n applied?: boolean\n /**\n * The thoughts of the message.\n */\n thoughts?: string\n}\n\n/**\n * A union type representing any valid chat message in the AI session history.\n */\nexport type AiMessageSchema = AiUserMessageSchema | AiModelMessageSchema\n\n/**\n * The base class for all AI messages.\n * It provides common functionality for all message types.\n */\nexport abstract class AiMessageBase<\n T extends AiMessageSchemaBase = AiMessageSchemaBase,\n> implements AiMessageSchemaBase {\n /**\n * The kind of the schema.\n */\n static get Kind(): typeof AiMessageKind {\n return AiMessageKind\n }\n\n /**\n * The kind of the schema.\n */\n get kind(): typeof AiMessageKind {\n return AiMessageKind\n }\n\n key: string\n role: T['role']\n createdAt: number\n updatedAt: number\n session: string\n text: string\n deleted: boolean\n deletedInfo?: IDeletion\n\n protected static createBaseSchema(input: Partial<AiMessageSchemaBase>): Omit<AiMessageSchemaBase, 'role'> {\n const { key = nanoid(), session, text = '', createdAt = 0, updatedAt = 0 } = input\n if (!session) {\n throw new Error('Session is required to create an AiMessage.')\n }\n return {\n key,\n kind: AiMessageKind,\n session,\n text,\n createdAt,\n updatedAt,\n }\n }\n\n constructor(input: Partial<T> = {}, role: T['role']) {\n const base = AiMessageBase.createBaseSchema(input)\n this.key = base.key\n this.role = role\n this.session = base.session\n this.text = base.text\n this.createdAt = base.createdAt\n this.updatedAt = base.updatedAt\n this.deleted = input.deleted || false\n this.deletedInfo = input.deletedInfo\n }\n\n toJSON(): T {\n const result = {\n kind: this.kind,\n key: this.key,\n role: this.role,\n session: this.session,\n text: this.text,\n createdAt: this.createdAt,\n updatedAt: this.updatedAt,\n }\n if (this.deleted && this.deletedInfo) {\n Object.assign(result, {\n deleted: this.deleted,\n deletedInfo: { ...this.deletedInfo },\n })\n }\n return result as unknown as T\n }\n}\n\nexport class AiUserMessage extends AiMessageBase<AiUserMessageSchema> implements AiUserMessageSchema {\n state: 'complete'\n\n static createSchema(input: Partial<AiUserMessageSchema>): AiUserMessageSchema {\n const base = AiMessageBase.createBaseSchema(input)\n return {\n ...base,\n role: 'user',\n state: 'complete',\n }\n }\n\n constructor(input: Partial<AiUserMessageSchema> = {}) {\n super(input, 'user')\n this.state = 'complete'\n }\n\n override toJSON(): AiUserMessageSchema {\n return {\n ...super.toJSON(),\n state: this.state,\n }\n }\n}\n\nexport class AiModelMessage extends AiMessageBase<AiModelMessageSchema> implements AiModelMessageSchema {\n state: AiMessageState\n applied?: boolean\n thoughts: string\n\n /**\n * The delta of the message, if any.\n */\n delta?: AiDomainDelta\n /**\n * The reasoning of the delta.\n * Format in markdown.\n */\n reasoning?: string\n\n static createSchema(input: Partial<AiModelMessageSchema>): AiModelMessageSchema {\n const base = AiMessageBase.createBaseSchema(input)\n return {\n ...base,\n role: 'model',\n state: input.state || 'loading',\n applied: input.applied,\n thoughts: input.thoughts || '',\n }\n }\n\n constructor(input: Partial<AiModelMessageSchema> = {}) {\n super(input, 'model')\n this.state = input.state || 'loading'\n this.applied = input.applied\n this.thoughts = input.thoughts || ''\n }\n\n override toJSON(): AiModelMessageSchema {\n const result: AiModelMessageSchema = {\n ...super.toJSON(),\n state: this.state,\n thoughts: this.thoughts,\n }\n if (this.applied !== undefined) {\n result.applied = this.applied\n }\n return result\n }\n\n /**\n * Adds a thought to the message. Useful when streaming.\n * @param thought The thought to add.\n */\n addThought(thought?: string): void {\n if (!thought) {\n return\n }\n this.thoughts += thought\n }\n\n /**\n * Adds a text chunk to the message. Useful when streaming.\n * @param text The text chunk to add.\n */\n addText(text?: string): void {\n if (!text) {\n return\n }\n this.text += text\n }\n\n /**\n * Processes the message.\n * If the message is not valid JSON, it will throw an error.\n * It sets the delta and reasoning fields, if found in the message.\n */\n processMessage(): void {\n if (!this.text) {\n return\n }\n const parsed = JSON.parse(this.text)\n if (parsed.delta) {\n this.delta = DataDomainDelta.normalize(parsed.delta)\n }\n if (parsed.reasoning) {\n this.reasoning = parsed.reasoning\n }\n }\n\n /**\n * Processes the message safely.\n * If the message is not valid JSON, it will be ignored.\n */\n processMessageSafe(): void {\n try {\n this.processMessage()\n } catch {\n // do nothing\n }\n }\n\n /**\n * Creates an empty model message. Useful when initializing LLM flow.\n * @param session The session key.\n * @returns An empty model message.\n */\n static createEmpty(session: string): AiModelMessage {\n return new AiModelMessage({\n session,\n state: 'loading',\n })\n }\n}\n\n/**\n * A union type representing any valid chat message in the AI session history.\n */\nexport type AiMessage = AiUserMessage | AiModelMessage\n\nexport const AiMessageFactory = {\n fromSchema(schema: Partial<AiMessageSchema>): AiMessage {\n if (schema.role === 'model') {\n return new AiModelMessage(schema as Partial<AiModelMessageSchema>)\n }\n return new AiUserMessage(schema as Partial<AiUserMessageSchema>)\n },\n}\n"]}
1
+ {"version":3,"file":"AiMessage.js","sourceRoot":"","sources":["../../../src/models/AiMessage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IDeletion } from './store/Deletion.js'\nimport { AiMessageKind } from './kinds.js'\nimport { nanoid } from '../nanoid.js'\nimport type { AiDomainDelta } from '../modeling/ai/types.js'\nimport { DataDomainDelta } from '../modeling/ai/DataDomainDelta.js'\nimport { detectReasoning } from '../modeling/ai/message_parser.js'\n\nexport type AiMessageRole = 'user' | 'model'\nexport type AiMessageState = 'loading' | 'complete' | 'error' | 'terminated'\n\n/**\n * The base interface representing a single message in the domain manipulation chat history.\n * It tracks metadata like timestamps, the associated session, and the raw text content.\n * See `AiUserMessage` and `AiModelMessage` for specific role implementations.\n */\nexport interface AiMessageSchemaBase {\n readonly kind: typeof AiMessageKind\n /**\n * The datastore key of the message.\n */\n key: string\n /**\n * The role of the message.\n */\n role: AiMessageRole\n /**\n * The timestamp of when the message was created.\n */\n createdAt: number\n /**\n * The timestamp of when the message was last updated.\n */\n updatedAt: number\n /**\n * The datastore key of the session.\n */\n session: string\n /**\n * The text of the message.\n */\n text: string\n /**\n * Whether the message is deleted.\n */\n deleted?: boolean\n /**\n * The information about the delete information.\n * Always set when the `deleted` is true.\n */\n deletedInfo?: IDeletion\n}\n\n/**\n * The schema for a user message.\n */\nexport interface AiUserMessageSchema extends AiMessageSchemaBase {\n /**\n * The role of the message.\n */\n role: 'user'\n /**\n * The state of the user message.\n * Can only be `complete`.\n */\n state: 'complete'\n /**\n * The user utterance.\n * Note, the actual message sent to the LLM may be different from the utterance\n * as it has additional instructions and the domain context. However, we are not storing\n * the full message in the datastore, only the utterance.\n */\n text: string\n}\n\n/**\n * The schema for a model message.\n */\nexport interface AiModelMessageSchema extends AiMessageSchemaBase {\n /**\n * The role of the message.\n */\n role: 'model'\n /**\n * The full text of the message returned by the LLM.\n * It is most likely a structured message that needs to be parsed\n * to the corresponding message object.\n */\n text: string\n /**\n * The state of the message.\n * - loading: the message is being generated\n * - complete: the message is complete\n * - error: the message generation failed\n * - terminated: the message generation was terminated by the user\n */\n state: AiMessageState\n /**\n * Whether the message has been applied.\n */\n applied?: boolean\n /**\n * The thoughts of the message.\n */\n thoughts?: string\n}\n\n/**\n * A union type representing any valid chat message in the AI session history.\n */\nexport type AiMessageSchema = AiUserMessageSchema | AiModelMessageSchema\n\n/**\n * The base class for all AI messages.\n * It provides common functionality for all message types.\n */\nexport abstract class AiMessageBase<\n T extends AiMessageSchemaBase = AiMessageSchemaBase,\n> implements AiMessageSchemaBase {\n /**\n * The kind of the schema.\n */\n static get Kind(): typeof AiMessageKind {\n return AiMessageKind\n }\n\n /**\n * The kind of the schema.\n */\n get kind(): typeof AiMessageKind {\n return AiMessageKind\n }\n\n key: string\n role: T['role']\n createdAt: number\n updatedAt: number\n session: string\n text: string\n deleted: boolean\n deletedInfo?: IDeletion\n\n protected static createBaseSchema(input: Partial<AiMessageSchemaBase>): Omit<AiMessageSchemaBase, 'role'> {\n const { key = nanoid(), session, text = '', createdAt = 0, updatedAt = 0 } = input\n if (!session) {\n throw new Error('Session is required to create an AiMessage.')\n }\n return {\n key,\n kind: AiMessageKind,\n session,\n text,\n createdAt,\n updatedAt,\n }\n }\n\n constructor(input: Partial<T> = {}, role: T['role']) {\n const base = AiMessageBase.createBaseSchema(input)\n this.key = base.key\n this.role = role\n this.session = base.session\n this.text = base.text\n this.createdAt = base.createdAt\n this.updatedAt = base.updatedAt\n this.deleted = input.deleted || false\n this.deletedInfo = input.deletedInfo\n }\n\n toJSON(): T {\n const result = {\n kind: this.kind,\n key: this.key,\n role: this.role,\n session: this.session,\n text: this.text,\n createdAt: this.createdAt,\n updatedAt: this.updatedAt,\n }\n if (this.deleted && this.deletedInfo) {\n Object.assign(result, {\n deleted: this.deleted,\n deletedInfo: { ...this.deletedInfo },\n })\n }\n return result as unknown as T\n }\n}\n\nexport class AiUserMessage extends AiMessageBase<AiUserMessageSchema> implements AiUserMessageSchema {\n state: 'complete'\n\n static createSchema(input: Partial<AiUserMessageSchema>): AiUserMessageSchema {\n const base = AiMessageBase.createBaseSchema(input)\n return {\n ...base,\n role: 'user',\n state: 'complete',\n }\n }\n\n constructor(input: Partial<AiUserMessageSchema> = {}) {\n super(input, 'user')\n this.state = 'complete'\n }\n\n override toJSON(): AiUserMessageSchema {\n return {\n ...super.toJSON(),\n state: this.state,\n }\n }\n}\n\n/**\n * Model messages have the `text` that most likely is a structured JSON\n * with the `delta` and `reasoning` properties. When an `AiModelMessage` is instantiated,\n * it will try to parse the `text` into the `delta` and `reasoning` properties, leaving the\n * `text` property unchanged.\n *\n * The delta object will be different for different models and different use cases.\n * It is up to the model to define the delta object. However, the delta object should\n * be normalized by each child class.\n *\n * @template D The type of the delta.\n */\nexport abstract class AiModelMessage<D = unknown>\n extends AiMessageBase<AiModelMessageSchema>\n implements AiModelMessageSchema\n{\n state: AiMessageState\n applied?: boolean\n thoughts: string\n\n /**\n * The extracted delta from the message.\n */\n delta?: D\n\n /**\n * The extracted reasoning from the message.\n * Format in markdown.\n */\n reasoning?: string\n\n #reasoningEnded = false\n\n static createSchema(input: Partial<AiModelMessageSchema>): AiModelMessageSchema {\n const base = AiMessageBase.createBaseSchema(input)\n return {\n ...base,\n role: 'model',\n state: input.state || 'loading',\n applied: input.applied,\n thoughts: input.thoughts || '',\n }\n }\n\n constructor(input: Partial<AiModelMessageSchema> = {}) {\n super(input, 'model')\n this.state = input.state || 'loading'\n this.applied = input.applied\n this.thoughts = input.thoughts || ''\n this.processMessageSafe()\n }\n\n override toJSON(): AiModelMessageSchema {\n const result: AiModelMessageSchema = {\n ...super.toJSON(),\n state: this.state,\n thoughts: this.thoughts,\n }\n if (this.applied !== undefined) {\n result.applied = this.applied\n }\n return result\n }\n\n /**\n * Adds a thought to the message. Useful when streaming.\n * @param thought The thought to add.\n */\n addThought(thought?: string): void {\n if (!thought) {\n return\n }\n this.thoughts += thought\n }\n\n /**\n * Adds a text chunk to the message. Useful when streaming.\n *\n * Note, this function progressively detects the reasoning on each chunk.\n * @param text The text chunk to add.\n */\n addText(text?: string): void {\n if (!text) {\n return\n }\n this.text += text\n if (this.#reasoningEnded) {\n return\n }\n const before = this.reasoning\n const after = detectReasoning(this.text)\n if (after && after === before) {\n this.#reasoningEnded = true\n }\n this.reasoning = after\n }\n\n /**\n * Processes the message.\n * If the message is not valid JSON, it will throw an error.\n * It sets the delta and reasoning fields, if found in the message.\n */\n processMessage(): void {\n const { text } = this\n if (!text) {\n return\n }\n const parsed = JSON.parse(text)\n if (parsed.delta) {\n this.delta = this.normalizeDelta(parsed.delta)\n }\n if (parsed.reasoning) {\n this.reasoning = parsed.reasoning\n }\n }\n\n /**\n * Processes the message safely.\n * If the message is not valid JSON, it will be ignored.\n */\n processMessageSafe(): void {\n try {\n this.processMessage()\n } catch {\n // do nothing\n }\n }\n\n /**\n * Normalizes the delta to the final shape required by the system.\n *\n * Probabilistic models can produce deltas that are not always what the author expected.\n * For example, the DataDomain delta has `modifiedEntities` property that sometimes is\n * populated multiple times by the same entity. This should be normalized to a single\n * operation so that the UI can correctly render the changes, without an expensive lookup\n * operations. The author of the message model should predict these situations and handle\n * them accordingly.\n *\n * @param delta The delta to normalize.\n * @returns The normalized delta.\n */\n abstract normalizeDelta(delta: D): D\n}\n\n/**\n * A model message that contains a delta of type `AiDomainDelta`.\n */\nexport class AiModelDataDomainMessage extends AiModelMessage<AiDomainDelta> {\n /**\n * Creates an empty model message. Useful when initializing LLM flow.\n * @param session The session key.\n * @returns An empty model message.\n */\n static createEmpty(session: string): AiModelDataDomainMessage {\n return new AiModelDataDomainMessage({\n session,\n state: 'loading',\n })\n }\n\n override normalizeDelta(delta: AiDomainDelta): AiDomainDelta {\n return DataDomainDelta.normalize(delta)\n }\n}\n\n/**\n * A union type representing any valid chat message in the AI session history.\n */\nexport type AiMessage = AiUserMessage | AiModelMessage | AiModelDataDomainMessage\n"]}