@absolutejs/ai 0.0.1 → 0.0.2

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.
@@ -0,0 +1,777 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __name = (target, name) => {
5
+ Object.defineProperty(target, "name", {
6
+ value: name,
7
+ enumerable: false,
8
+ configurable: true
9
+ });
10
+ return target;
11
+ };
12
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
13
+ var __typeError = (msg) => {
14
+ throw TypeError(msg);
15
+ };
16
+ var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
18
+ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
19
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
20
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
21
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
22
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
23
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
24
+ var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
25
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({
26
+ kind: __decoratorStrings[kind],
27
+ name,
28
+ metadata,
29
+ addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
30
+ });
31
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
32
+ var __runInitializers = (array, flags, self, value) => {
33
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
34
+ flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
35
+ return value;
36
+ };
37
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
38
+ var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
39
+ var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
40
+ var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
41
+ var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
42
+ get [name]() {
43
+ return __privateGet(this, extra);
44
+ },
45
+ set [name](x) {
46
+ __privateSet(this, extra, x);
47
+ }
48
+ }, name));
49
+ k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
50
+ for (var i = decorators.length - 1;i >= 0; i--) {
51
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
52
+ if (k) {
53
+ ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
54
+ if (k ^ 3)
55
+ access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
56
+ if (k > 2)
57
+ access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
58
+ }
59
+ it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
60
+ done._ = 1;
61
+ if (k ^ 4 || it === undefined)
62
+ __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
63
+ else if (typeof it !== "object" || it === null)
64
+ __typeError("Object expected");
65
+ else
66
+ __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
67
+ }
68
+ return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
69
+ };
70
+
71
+ // src/angular/ai/ai-stream.service.ts
72
+ import { computed, Injectable, signal } from "@angular/core";
73
+
74
+ // src/ai/client/actions.ts
75
+ var serverMessageToAction = (message) => {
76
+ switch (message.type) {
77
+ case "chunk":
78
+ return {
79
+ content: message.content,
80
+ conversationId: message.conversationId,
81
+ messageId: message.messageId,
82
+ type: "chunk"
83
+ };
84
+ case "thinking":
85
+ return {
86
+ content: message.content,
87
+ conversationId: message.conversationId,
88
+ messageId: message.messageId,
89
+ type: "thinking"
90
+ };
91
+ case "tool_status":
92
+ return {
93
+ conversationId: message.conversationId,
94
+ input: message.input,
95
+ messageId: message.messageId,
96
+ name: message.name,
97
+ result: message.result,
98
+ status: message.status,
99
+ type: "tool_status"
100
+ };
101
+ case "image":
102
+ return {
103
+ conversationId: message.conversationId,
104
+ data: message.data,
105
+ format: message.format,
106
+ imageId: message.imageId,
107
+ isPartial: message.isPartial,
108
+ messageId: message.messageId,
109
+ revisedPrompt: message.revisedPrompt,
110
+ type: "image"
111
+ };
112
+ case "complete":
113
+ return {
114
+ conversationId: message.conversationId,
115
+ durationMs: message.durationMs,
116
+ messageId: message.messageId,
117
+ model: message.model,
118
+ sources: message.sources,
119
+ type: "complete",
120
+ usage: message.usage
121
+ };
122
+ case "rag_retrieving":
123
+ return {
124
+ conversationId: message.conversationId,
125
+ messageId: message.messageId,
126
+ retrievalStartedAt: message.retrievalStartedAt,
127
+ type: "rag_retrieving"
128
+ };
129
+ case "rag_retrieved":
130
+ return {
131
+ conversationId: message.conversationId,
132
+ messageId: message.messageId,
133
+ retrievalDurationMs: message.retrievalDurationMs,
134
+ retrievalStartedAt: message.retrievalStartedAt,
135
+ retrievedAt: message.retrievedAt,
136
+ sources: message.sources,
137
+ trace: message.trace,
138
+ type: "rag_retrieved"
139
+ };
140
+ case "error":
141
+ return { message: message.message, type: "error" };
142
+ default:
143
+ return null;
144
+ }
145
+ };
146
+
147
+ // types/typeGuards.ts
148
+ var isValidAIServerMessage = (data) => {
149
+ if (!data || typeof data !== "object") {
150
+ return false;
151
+ }
152
+ if (!("type" in data) || typeof data.type !== "string") {
153
+ return false;
154
+ }
155
+ switch (data.type) {
156
+ case "chunk":
157
+ case "thinking":
158
+ return "content" in data && typeof data.content === "string" && "messageId" in data && "conversationId" in data;
159
+ case "tool_status":
160
+ return "name" in data && "status" in data && "messageId" in data && "conversationId" in data;
161
+ case "image":
162
+ return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "isPartial" in data && typeof data.isPartial === "boolean" && "messageId" in data && "conversationId" in data;
163
+ case "complete":
164
+ return "messageId" in data && "conversationId" in data;
165
+ case "rag_retrieved":
166
+ return "conversationId" in data && "messageId" in data && "sources" in data && Array.isArray(data.sources);
167
+ case "error":
168
+ return "message" in data && typeof data.message === "string";
169
+ default:
170
+ return false;
171
+ }
172
+ };
173
+
174
+ // src/ai/client/connection.ts
175
+ var WS_OPEN = 1;
176
+ var WS_NORMAL_CLOSURE = 1000;
177
+ var WS_CLOSED = 3;
178
+ var DEFAULT_PING_INTERVAL = 30000;
179
+ var RECONNECT_INITIAL_DELAY = 500;
180
+ var RECONNECT_POLL_INTERVAL = 300;
181
+ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 60;
182
+ var noop = () => {};
183
+ var noopUnsubscribe = () => noop;
184
+ var NOOP_CONNECTION = {
185
+ close: noop,
186
+ send: noop,
187
+ subscribe: noopUnsubscribe,
188
+ getReadyState: () => WS_CLOSED
189
+ };
190
+ var buildWsUrl = (path) => {
191
+ const { hostname, port, protocol } = window.location;
192
+ const wsProtocol = protocol === "https:" ? "wss:" : "ws:";
193
+ const portSuffix = port ? `:${port}` : "";
194
+ return `${wsProtocol}//${hostname}${portSuffix}${path}`;
195
+ };
196
+ var parseServerMessage = (event) => {
197
+ let data;
198
+ try {
199
+ data = JSON.parse(String(event.data));
200
+ } catch {
201
+ return null;
202
+ }
203
+ if (data && typeof data === "object" && "type" in data && data.type === "pong") {
204
+ return null;
205
+ }
206
+ if (!isValidAIServerMessage(data)) {
207
+ return null;
208
+ }
209
+ return data;
210
+ };
211
+ var createAIConnection = (path, options = {}) => {
212
+ if (typeof window === "undefined") {
213
+ return NOOP_CONNECTION;
214
+ }
215
+ const shouldReconnect = options.reconnect !== false;
216
+ const pingInterval = options.pingInterval ?? DEFAULT_PING_INTERVAL;
217
+ const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
218
+ const listeners = new Set;
219
+ const connState = {
220
+ isConnected: false,
221
+ pendingMessages: [],
222
+ pingInterval: null,
223
+ reconnectAttempts: 0,
224
+ reconnectTimeout: null,
225
+ ws: null
226
+ };
227
+ const flushPendingMessages = () => {
228
+ if (connState.ws?.readyState !== WS_OPEN) {
229
+ return;
230
+ }
231
+ while (connState.pendingMessages.length > 0) {
232
+ const next = connState.pendingMessages.shift();
233
+ if (typeof next === "string") {
234
+ connState.ws.send(next);
235
+ }
236
+ }
237
+ };
238
+ const clearTimers = () => {
239
+ if (connState.pingInterval) {
240
+ clearInterval(connState.pingInterval);
241
+ connState.pingInterval = null;
242
+ }
243
+ if (connState.reconnectTimeout) {
244
+ clearTimeout(connState.reconnectTimeout);
245
+ connState.reconnectTimeout = null;
246
+ }
247
+ };
248
+ const scheduleReconnect = () => {
249
+ connState.reconnectAttempts++;
250
+ const delay = connState.reconnectAttempts === 1 ? RECONNECT_INITIAL_DELAY : RECONNECT_POLL_INTERVAL;
251
+ connState.reconnectTimeout = setTimeout(() => {
252
+ if (connState.reconnectAttempts > maxReconnectAttempts) {
253
+ return;
254
+ }
255
+ connect();
256
+ }, delay);
257
+ };
258
+ const connect = () => {
259
+ const url = buildWsUrl(path);
260
+ const wsInstance = new WebSocket(url, options.protocols);
261
+ wsInstance.onopen = () => {
262
+ connState.isConnected = true;
263
+ connState.reconnectAttempts = 0;
264
+ flushPendingMessages();
265
+ connState.pingInterval = setInterval(() => {
266
+ if (wsInstance.readyState === WS_OPEN && connState.isConnected) {
267
+ wsInstance.send(JSON.stringify({ type: "ping" }));
268
+ }
269
+ }, pingInterval);
270
+ };
271
+ wsInstance.onmessage = (event) => {
272
+ const message = parseServerMessage(event);
273
+ if (!message) {
274
+ return;
275
+ }
276
+ listeners.forEach((listener) => listener(message));
277
+ };
278
+ wsInstance.onclose = (event) => {
279
+ connState.isConnected = false;
280
+ clearTimers();
281
+ const shouldAttemptReconnect = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && connState.reconnectAttempts < maxReconnectAttempts;
282
+ if (shouldAttemptReconnect) {
283
+ scheduleReconnect();
284
+ }
285
+ };
286
+ wsInstance.onerror = () => {};
287
+ connState.ws = wsInstance;
288
+ };
289
+ const send = (msg) => {
290
+ const serialized = JSON.stringify(msg);
291
+ if (connState.ws?.readyState === WS_OPEN) {
292
+ connState.ws.send(serialized);
293
+ return;
294
+ }
295
+ connState.pendingMessages.push(serialized);
296
+ };
297
+ const subscribe = (callback) => {
298
+ listeners.add(callback);
299
+ return () => {
300
+ listeners.delete(callback);
301
+ };
302
+ };
303
+ const close = () => {
304
+ clearTimers();
305
+ if (connState.ws) {
306
+ connState.ws.close(WS_NORMAL_CLOSURE);
307
+ connState.ws = null;
308
+ }
309
+ connState.isConnected = false;
310
+ listeners.clear();
311
+ };
312
+ const getReadyState = () => connState.ws?.readyState ?? WS_CLOSED;
313
+ connect();
314
+ return { close, getReadyState, send, subscribe };
315
+ };
316
+
317
+ // src/ai/client/messageStore.ts
318
+ var EMPTY_STATE = {
319
+ activeConversationId: null,
320
+ conversations: new Map,
321
+ error: null,
322
+ isStreaming: false
323
+ };
324
+ var initialActiveConversationId = null;
325
+ var initialError = null;
326
+ var freshState = () => ({
327
+ activeConversationId: initialActiveConversationId,
328
+ conversations: new Map,
329
+ error: initialError,
330
+ isStreaming: false
331
+ });
332
+ var findAssistantMessage = (conversation, messageId) => conversation.messages.find((msg) => msg.id === messageId && msg.role === "assistant");
333
+ var getOrCreate = (state, conversationId) => {
334
+ let conversation = state.conversations.get(conversationId);
335
+ if (!conversation) {
336
+ conversation = {
337
+ createdAt: Date.now(),
338
+ id: conversationId,
339
+ messages: []
340
+ };
341
+ state.conversations.set(conversationId, conversation);
342
+ }
343
+ return conversation;
344
+ };
345
+ var handleSend = (state, action) => {
346
+ const conversation = getOrCreate(state, action.conversationId);
347
+ const message = {
348
+ attachments: action.attachments,
349
+ content: action.content,
350
+ conversationId: action.conversationId,
351
+ id: action.messageId,
352
+ role: "user",
353
+ timestamp: Date.now()
354
+ };
355
+ conversation.messages = [...conversation.messages, message];
356
+ state.activeConversationId = action.conversationId;
357
+ state.error = null;
358
+ state.isStreaming = true;
359
+ };
360
+ var handleChunk = (state, action) => {
361
+ const conversation = getOrCreate(state, action.conversationId);
362
+ const existingIdx = conversation.messages.findIndex((msg) => msg.id === action.messageId && msg.role === "assistant");
363
+ if (existingIdx >= 0) {
364
+ const prevContent = conversation.messages[existingIdx]?.content ?? "";
365
+ conversation.messages = conversation.messages.map((msg, idx) => idx === existingIdx ? { ...msg, content: prevContent + action.content } : msg);
366
+ return;
367
+ }
368
+ const message = {
369
+ content: action.content,
370
+ conversationId: action.conversationId,
371
+ id: action.messageId,
372
+ isStreaming: true,
373
+ role: "assistant",
374
+ timestamp: Date.now()
375
+ };
376
+ conversation.messages = [...conversation.messages, message];
377
+ };
378
+ var handleThinking = (state, action) => {
379
+ const conversation = getOrCreate(state, action.conversationId);
380
+ const existingIdx = conversation.messages.findIndex((msg) => msg.id === action.messageId && msg.role === "assistant");
381
+ if (existingIdx >= 0) {
382
+ const prevThinking = conversation.messages[existingIdx]?.thinking ?? "";
383
+ conversation.messages = conversation.messages.map((msg, idx) => idx === existingIdx ? { ...msg, thinking: prevThinking + action.content } : msg);
384
+ return;
385
+ }
386
+ const message = {
387
+ content: "",
388
+ conversationId: action.conversationId,
389
+ id: action.messageId,
390
+ isStreaming: true,
391
+ role: "assistant",
392
+ thinking: action.content,
393
+ timestamp: Date.now()
394
+ };
395
+ conversation.messages = [...conversation.messages, message];
396
+ };
397
+ var upsertToolCall = (message, toolCall) => {
398
+ if (!message.toolCalls) {
399
+ message.toolCalls = [toolCall];
400
+ return;
401
+ }
402
+ const existingIdx = message.toolCalls.findIndex((existing) => existing.name === toolCall.name);
403
+ if (existingIdx >= 0) {
404
+ message.toolCalls[existingIdx] = toolCall;
405
+ } else {
406
+ message.toolCalls = [...message.toolCalls, toolCall];
407
+ }
408
+ };
409
+ var getOrCreateAssistantMessage = (conversation, messageId, conversationId) => {
410
+ const existing = findAssistantMessage(conversation, messageId);
411
+ if (existing) {
412
+ return existing;
413
+ }
414
+ const message = {
415
+ content: "",
416
+ conversationId,
417
+ id: messageId,
418
+ isStreaming: true,
419
+ role: "assistant",
420
+ timestamp: Date.now()
421
+ };
422
+ conversation.messages = [...conversation.messages, message];
423
+ return message;
424
+ };
425
+ var handleRAGRetrieved = (state, action) => {
426
+ const conversation = getOrCreate(state, action.conversationId);
427
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
428
+ message.sources = action.sources;
429
+ message.retrievalStartedAt = action.retrievalStartedAt ?? message.retrievalStartedAt;
430
+ message.retrievedAt = action.retrievedAt;
431
+ message.retrievalDurationMs = action.retrievalDurationMs;
432
+ message.retrievalTrace = action.trace;
433
+ conversation.messages = [...conversation.messages];
434
+ };
435
+ var handleRAGRetrieving = (state, action) => {
436
+ const conversation = getOrCreate(state, action.conversationId);
437
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
438
+ message.retrievalStartedAt = action.retrievalStartedAt;
439
+ conversation.messages = [...conversation.messages];
440
+ };
441
+ var handleToolStatus = (state, action) => {
442
+ const conversation = getOrCreate(state, action.conversationId);
443
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
444
+ const toolCall = {
445
+ id: action.messageId,
446
+ input: action.input,
447
+ name: action.name,
448
+ result: action.status === "complete" ? action.result ?? "" : undefined
449
+ };
450
+ upsertToolCall(message, toolCall);
451
+ conversation.messages = [...conversation.messages];
452
+ };
453
+ var markMessageComplete = (conversation, messageId, usage, durationMs, model, sources) => {
454
+ conversation.messages = conversation.messages.map((msg) => msg.id === messageId && msg.role === "assistant" ? {
455
+ ...msg,
456
+ durationMs,
457
+ isStreaming: false,
458
+ model,
459
+ sources: sources ?? msg.sources,
460
+ usage
461
+ } : msg);
462
+ };
463
+ var NOT_FOUND = -1;
464
+ var findExistingImageById = (images, imageId) => {
465
+ if (!imageId)
466
+ return NOT_FOUND;
467
+ return images.findIndex((img) => img.imageId === imageId);
468
+ };
469
+ var findReplaceablePartialIndex = (images) => {
470
+ const lastIdx = images.length - 1;
471
+ if (lastIdx >= 0 && images[lastIdx]?.isPartial) {
472
+ return lastIdx;
473
+ }
474
+ return NOT_FOUND;
475
+ };
476
+ var upsertImage = (message, imageData) => {
477
+ if (!message.images) {
478
+ message.images = [imageData];
479
+ return;
480
+ }
481
+ const existingIdx = findExistingImageById(message.images, imageData.imageId);
482
+ if (existingIdx >= 0) {
483
+ message.images[existingIdx] = imageData;
484
+ return;
485
+ }
486
+ const replaceableIdx = findReplaceablePartialIndex(message.images);
487
+ if (replaceableIdx >= 0) {
488
+ message.images[replaceableIdx] = imageData;
489
+ return;
490
+ }
491
+ message.images = [...message.images, imageData];
492
+ };
493
+ var handleImage = (state, action) => {
494
+ const conversation = getOrCreate(state, action.conversationId);
495
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
496
+ upsertImage(message, {
497
+ data: action.data,
498
+ format: action.format,
499
+ imageId: action.imageId,
500
+ isPartial: action.isPartial,
501
+ revisedPrompt: action.revisedPrompt
502
+ });
503
+ conversation.messages = [...conversation.messages];
504
+ };
505
+ var handleComplete = (state, action) => {
506
+ const conversation = state.conversations.get(action.conversationId);
507
+ if (conversation) {
508
+ markMessageComplete(conversation, action.messageId, action.usage, action.durationMs, action.model, action.sources);
509
+ }
510
+ state.isStreaming = false;
511
+ };
512
+ var markConversationStreamsComplete = (conversation) => {
513
+ const streamingMessages = conversation.messages.filter((msg) => msg.isStreaming);
514
+ if (streamingMessages.length === 0) {
515
+ return;
516
+ }
517
+ for (const msg of streamingMessages) {
518
+ msg.isStreaming = false;
519
+ }
520
+ conversation.messages = [...conversation.messages];
521
+ };
522
+ var markAllStreamsComplete = (state) => {
523
+ for (const [, conversation] of state.conversations) {
524
+ markConversationStreamsComplete(conversation);
525
+ }
526
+ };
527
+ var handleBranch = (state, action) => {
528
+ const source = state.conversations.get(action.oldConversationId);
529
+ if (!source) {
530
+ return;
531
+ }
532
+ const cutoffIndex = source.messages.findIndex((msg) => msg.id === action.fromMessageId);
533
+ if (cutoffIndex < 0) {
534
+ return;
535
+ }
536
+ const branchedMessages = source.messages.slice(0, cutoffIndex + 1).map((msg) => ({ ...msg, conversationId: action.newConversationId }));
537
+ const newConversation = {
538
+ createdAt: Date.now(),
539
+ id: action.newConversationId,
540
+ messages: branchedMessages
541
+ };
542
+ state.conversations.set(action.newConversationId, newConversation);
543
+ state.activeConversationId = action.newConversationId;
544
+ };
545
+ var applyAction = (state, action) => {
546
+ switch (action.type) {
547
+ case "send":
548
+ handleSend(state, action);
549
+ break;
550
+ case "chunk":
551
+ handleChunk(state, action);
552
+ break;
553
+ case "thinking":
554
+ handleThinking(state, action);
555
+ break;
556
+ case "tool_status":
557
+ handleToolStatus(state, action);
558
+ break;
559
+ case "image":
560
+ handleImage(state, action);
561
+ break;
562
+ case "complete":
563
+ handleComplete(state, action);
564
+ break;
565
+ case "error":
566
+ state.error = action.message;
567
+ state.isStreaming = false;
568
+ break;
569
+ case "rag_retrieving":
570
+ handleRAGRetrieving(state, action);
571
+ break;
572
+ case "rag_retrieved":
573
+ handleRAGRetrieved(state, action);
574
+ break;
575
+ case "cancel":
576
+ state.isStreaming = false;
577
+ markAllStreamsComplete(state);
578
+ break;
579
+ case "branch":
580
+ handleBranch(state, action);
581
+ break;
582
+ case "set_conversation":
583
+ state.activeConversationId = action.conversationId;
584
+ break;
585
+ }
586
+ };
587
+ var createAIMessageStore = () => {
588
+ let state = freshState();
589
+ const subscribers = new Set;
590
+ return {
591
+ dispatch: (action) => {
592
+ applyAction(state, action);
593
+ state = { ...state, conversations: new Map(state.conversations) };
594
+ subscribers.forEach((callback) => callback());
595
+ },
596
+ getServerSnapshot: () => EMPTY_STATE,
597
+ getSnapshot: () => state,
598
+ subscribe: (callback) => {
599
+ subscribers.add(callback);
600
+ return () => {
601
+ subscribers.delete(callback);
602
+ };
603
+ }
604
+ };
605
+ };
606
+
607
+ // src/ai/protocol.ts
608
+ var generateId = () => crypto.randomUUID();
609
+
610
+ // src/angular/ai/ai-stream.service.ts
611
+ var _dec = [
612
+ Injectable({ providedIn: "root" })
613
+ ];
614
+ var _init = __decoratorStart(undefined);
615
+
616
+ class AIStreamService {
617
+ connections = new Map;
618
+ connect(path, conversationId) {
619
+ const existing = this.connections.get(path);
620
+ if (existing) {
621
+ return this.createHandle(existing, conversationId);
622
+ }
623
+ const connection = createAIConnection(path);
624
+ const store = createAIMessageStore();
625
+ const messagesSignal = signal([]);
626
+ const isStreamingSignal = signal(false);
627
+ const errorSignal = signal(null);
628
+ const activeConversationIdSignal = signal(conversationId ?? null);
629
+ const syncState = () => {
630
+ const snapshot = store.getSnapshot();
631
+ const convId = activeConversationIdSignal() ?? snapshot.activeConversationId;
632
+ const conversation = convId ? snapshot.conversations.get(convId) : undefined;
633
+ messagesSignal.set(conversation?.messages ?? []);
634
+ isStreamingSignal.set(snapshot.isStreaming);
635
+ errorSignal.set(snapshot.error);
636
+ if (convId) {
637
+ activeConversationIdSignal.set(convId);
638
+ }
639
+ };
640
+ const unsubscribeStore = store.subscribe(syncState);
641
+ const unsubscribeConnection = connection.subscribe((message) => {
642
+ const action = serverMessageToAction(message);
643
+ if (action) {
644
+ store.dispatch(action);
645
+ }
646
+ });
647
+ const entry = {
648
+ connection,
649
+ store,
650
+ unsubscribeConnection,
651
+ unsubscribeStore
652
+ };
653
+ this.connections.set(path, entry);
654
+ const send = (content, attachments) => {
655
+ const convId = activeConversationIdSignal() ?? generateId();
656
+ const msgId = generateId();
657
+ store.dispatch({
658
+ attachments,
659
+ content,
660
+ conversationId: convId,
661
+ messageId: msgId,
662
+ type: "send"
663
+ });
664
+ connection.send({
665
+ attachments,
666
+ content,
667
+ conversationId: convId,
668
+ type: "message"
669
+ });
670
+ };
671
+ const cancel = () => {
672
+ const convId = activeConversationIdSignal();
673
+ if (convId) {
674
+ store.dispatch({ type: "cancel" });
675
+ connection.send({ conversationId: convId, type: "cancel" });
676
+ }
677
+ };
678
+ const branch = (messageId, content) => {
679
+ const convId = activeConversationIdSignal();
680
+ if (convId) {
681
+ connection.send({
682
+ content,
683
+ conversationId: convId,
684
+ messageId,
685
+ type: "branch"
686
+ });
687
+ }
688
+ };
689
+ return {
690
+ branch,
691
+ cancel,
692
+ error: computed(() => errorSignal()),
693
+ isStreaming: computed(() => isStreamingSignal()),
694
+ messages: computed(() => messagesSignal()),
695
+ send
696
+ };
697
+ }
698
+ createHandle(entry, conversationId) {
699
+ const { connection, store } = entry;
700
+ const messagesSignal = signal([]);
701
+ const isStreamingSignal = signal(false);
702
+ const errorSignal = signal(null);
703
+ const activeConversationIdSignal = signal(conversationId ?? null);
704
+ store.subscribe(() => {
705
+ const snapshot = store.getSnapshot();
706
+ const convId = activeConversationIdSignal() ?? snapshot.activeConversationId;
707
+ const conversation = convId ? snapshot.conversations.get(convId) : undefined;
708
+ messagesSignal.set(conversation?.messages ?? []);
709
+ isStreamingSignal.set(snapshot.isStreaming);
710
+ errorSignal.set(snapshot.error);
711
+ if (convId) {
712
+ activeConversationIdSignal.set(convId);
713
+ }
714
+ });
715
+ const send = (content, attachments) => {
716
+ const convId = activeConversationIdSignal() ?? generateId();
717
+ const msgId = generateId();
718
+ store.dispatch({
719
+ attachments,
720
+ content,
721
+ conversationId: convId,
722
+ messageId: msgId,
723
+ type: "send"
724
+ });
725
+ connection.send({
726
+ attachments,
727
+ content,
728
+ conversationId: convId,
729
+ type: "message"
730
+ });
731
+ };
732
+ const cancel = () => {
733
+ const convId = activeConversationIdSignal();
734
+ if (convId) {
735
+ store.dispatch({ type: "cancel" });
736
+ connection.send({ conversationId: convId, type: "cancel" });
737
+ }
738
+ };
739
+ const branch = (messageId, content) => {
740
+ const convId = activeConversationIdSignal();
741
+ if (convId) {
742
+ connection.send({
743
+ content,
744
+ conversationId: convId,
745
+ messageId,
746
+ type: "branch"
747
+ });
748
+ }
749
+ };
750
+ return {
751
+ branch,
752
+ cancel,
753
+ error: computed(() => errorSignal()),
754
+ isStreaming: computed(() => isStreamingSignal()),
755
+ messages: computed(() => messagesSignal()),
756
+ send
757
+ };
758
+ }
759
+ ngOnDestroy() {
760
+ for (const [, entry] of this.connections) {
761
+ entry.unsubscribeConnection();
762
+ entry.unsubscribeStore();
763
+ entry.connection.close();
764
+ }
765
+ this.connections.clear();
766
+ }
767
+ }
768
+ AIStreamService = __decorateElement(_init, 0, "AIStreamService", _dec, AIStreamService);
769
+ __runInitializers(_init, 1, AIStreamService);
770
+ __decoratorMetadata(_init, AIStreamService);
771
+ let _AIStreamService = AIStreamService;
772
+ export {
773
+ AIStreamService
774
+ };
775
+
776
+ //# debugId=78D09F9C53697ECB64756E2164756E21
777
+ //# sourceMappingURL=index.js.map