@dxos/assistant 0.8.2-staging.7ac8446 → 0.8.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.
Files changed (277) hide show
  1. package/dist/lib/browser/index.mjs +1830 -228
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +1825 -229
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +1831 -228
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/{src/bin → bin}/chat.d.ts.map +1 -1
  11. package/dist/types/src/blueprint/blueprint.d.ts +79 -0
  12. package/dist/types/src/blueprint/blueprint.d.ts.map +1 -0
  13. package/dist/types/src/blueprint/blueprint.test.d.ts +2 -0
  14. package/dist/types/src/blueprint/blueprint.test.d.ts.map +1 -0
  15. package/dist/types/src/blueprint/index.d.ts +4 -0
  16. package/dist/types/src/blueprint/index.d.ts.map +1 -0
  17. package/dist/types/src/blueprint/logger.d.ts +3 -0
  18. package/dist/types/src/blueprint/logger.d.ts.map +1 -0
  19. package/dist/types/src/blueprint/machine.d.ts +113 -0
  20. package/dist/types/src/blueprint/machine.d.ts.map +1 -0
  21. package/dist/types/src/blueprint/test-data.d.ts +3 -0
  22. package/dist/types/src/blueprint/test-data.d.ts.map +1 -0
  23. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +4 -0
  24. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +1 -0
  25. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +2 -0
  26. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +1 -0
  27. package/dist/types/src/extraction/extraction-llm-function/index.d.ts +2 -0
  28. package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +1 -0
  29. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +210 -0
  30. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +1 -0
  31. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +2 -0
  32. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +1 -0
  33. package/dist/types/src/extraction/extraction-ner-function/index.d.ts +3 -0
  34. package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -0
  35. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts +14 -0
  36. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts.map +1 -0
  37. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.test.d.ts +2 -0
  38. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.test.d.ts.map +1 -0
  39. package/dist/types/src/extraction/extraction.d.ts +242 -0
  40. package/dist/types/src/extraction/extraction.d.ts.map +1 -0
  41. package/dist/types/src/extraction/index.d.ts +5 -0
  42. package/dist/types/src/extraction/index.d.ts.map +1 -0
  43. package/dist/types/src/extraction/quotes.d.ts +15 -0
  44. package/dist/types/src/extraction/quotes.d.ts.map +1 -0
  45. package/dist/types/src/extraction/quotes.test.d.ts +2 -0
  46. package/dist/types/src/extraction/quotes.test.d.ts.map +1 -0
  47. package/dist/types/src/index.d.ts +4 -3
  48. package/dist/types/src/index.d.ts.map +1 -1
  49. package/dist/types/src/research/exa.d.ts +7 -0
  50. package/dist/types/src/research/exa.d.ts.map +1 -0
  51. package/dist/types/src/research/graph.d.ts +39 -0
  52. package/dist/types/src/research/graph.d.ts.map +1 -0
  53. package/dist/types/src/research/graph.test.d.ts +2 -0
  54. package/dist/types/src/research/graph.test.d.ts.map +1 -0
  55. package/dist/types/src/research/index.d.ts +4 -0
  56. package/dist/types/src/research/index.d.ts.map +1 -0
  57. package/dist/types/src/research/research.d.ts +10 -0
  58. package/dist/types/src/research/research.d.ts.map +1 -0
  59. package/dist/types/src/research/research.test.d.ts +2 -0
  60. package/dist/types/src/research/research.test.d.ts.map +1 -0
  61. package/dist/types/src/session/index.d.ts +1 -0
  62. package/dist/types/src/session/index.d.ts.map +1 -1
  63. package/dist/types/src/session/session.d.ts +80 -35
  64. package/dist/types/src/session/session.d.ts.map +1 -1
  65. package/dist/types/src/session/version-pin.d.ts +21 -0
  66. package/dist/types/src/session/version-pin.d.ts.map +1 -0
  67. package/dist/types/src/session/version-pin.test.d.ts +2 -0
  68. package/dist/types/src/session/version-pin.test.d.ts.map +1 -0
  69. package/dist/types/src/summarization/summarization.test.d.ts +2 -0
  70. package/dist/types/src/summarization/summarization.test.d.ts.map +1 -0
  71. package/dist/types/src/testing/exa-search-1748337321991.d.ts +38 -0
  72. package/dist/types/src/testing/exa-search-1748337321991.d.ts.map +1 -0
  73. package/dist/types/src/testing/exa-search-1748337331526.d.ts +37 -0
  74. package/dist/types/src/testing/exa-search-1748337331526.d.ts.map +1 -0
  75. package/dist/types/src/testing/exa-search-1748337344119.d.ts +58 -0
  76. package/dist/types/src/testing/exa-search-1748337344119.d.ts.map +1 -0
  77. package/dist/types/src/testing/index.d.ts +2 -6
  78. package/dist/types/src/testing/index.d.ts.map +1 -1
  79. package/dist/types/src/types.d.ts +3 -0
  80. package/dist/types/src/types.d.ts.map +1 -0
  81. package/dist/types/tsconfig.tsbuildinfo +1 -1
  82. package/package.json +25 -35
  83. package/src/blueprint/blueprint.test.ts +142 -0
  84. package/src/blueprint/blueprint.ts +95 -0
  85. package/src/blueprint/index.ts +7 -0
  86. package/src/blueprint/logger.ts +66 -0
  87. package/src/blueprint/machine.ts +208 -0
  88. package/src/blueprint/test-data.ts +55 -0
  89. package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +95 -0
  90. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +64 -0
  91. package/src/extraction/extraction-llm-function/index.ts +5 -0
  92. package/src/extraction/extraction-llm-function/instructions.tpl +28 -0
  93. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +115 -0
  94. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +54 -0
  95. package/src/extraction/extraction-ner-function/index.ts +6 -0
  96. package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +94 -0
  97. package/src/extraction/extraction-ner-function/named-entity-recognition.ts +97 -0
  98. package/src/extraction/extraction.ts +75 -0
  99. package/src/extraction/index.ts +8 -0
  100. package/src/extraction/quotes.test.ts +27 -0
  101. package/src/extraction/quotes.ts +57 -0
  102. package/src/index.ts +4 -3
  103. package/src/research/exa.ts +100 -0
  104. package/src/research/graph.test.ts +15 -0
  105. package/src/research/graph.ts +291 -0
  106. package/src/research/index.ts +7 -0
  107. package/src/research/instructions-research.tpl +97 -0
  108. package/src/research/research.test.ts +189 -0
  109. package/src/research/research.ts +89 -0
  110. package/src/session/index.ts +1 -0
  111. package/src/session/session.test.ts +39 -67
  112. package/src/session/session.ts +328 -155
  113. package/src/session/version-pin.test.ts +26 -0
  114. package/src/session/version-pin.ts +48 -0
  115. package/src/summarization/summarization.test.ts +216 -0
  116. package/src/testing/exa-search-1748337321991.ts +131 -0
  117. package/src/testing/exa-search-1748337331526.ts +144 -0
  118. package/src/testing/exa-search-1748337344119.ts +133 -0
  119. package/src/testing/index.ts +8 -7
  120. package/src/typedefs.d.ts +8 -0
  121. package/src/types.ts +8 -0
  122. package/src/typings.d.ts +8 -0
  123. package/dist/lib/browser/chunk-PND5VR6D.mjs +0 -2315
  124. package/dist/lib/browser/chunk-PND5VR6D.mjs.map +0 -7
  125. package/dist/lib/browser/testing/index.mjs +0 -621
  126. package/dist/lib/browser/testing/index.mjs.map +0 -7
  127. package/dist/lib/node/chunk-IBOBRBDF.cjs +0 -2326
  128. package/dist/lib/node/chunk-IBOBRBDF.cjs.map +0 -7
  129. package/dist/lib/node/testing/index.cjs +0 -637
  130. package/dist/lib/node/testing/index.cjs.map +0 -7
  131. package/dist/lib/node-esm/chunk-DCQJOLSR.mjs +0 -2317
  132. package/dist/lib/node-esm/chunk-DCQJOLSR.mjs.map +0 -7
  133. package/dist/lib/node-esm/testing/index.mjs +0 -622
  134. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  135. package/dist/types/src/ai-service/client.test.d.ts +0 -2
  136. package/dist/types/src/ai-service/client.test.d.ts.map +0 -1
  137. package/dist/types/src/ai-service/defs.d.ts +0 -9
  138. package/dist/types/src/ai-service/defs.d.ts.map +0 -1
  139. package/dist/types/src/ai-service/edge-client.d.ts +0 -19
  140. package/dist/types/src/ai-service/edge-client.d.ts.map +0 -1
  141. package/dist/types/src/ai-service/index.d.ts +0 -10
  142. package/dist/types/src/ai-service/index.d.ts.map +0 -1
  143. package/dist/types/src/ai-service/message-collector.d.ts +0 -88
  144. package/dist/types/src/ai-service/message-collector.d.ts.map +0 -1
  145. package/dist/types/src/ai-service/ollama-client.d.ts +0 -39
  146. package/dist/types/src/ai-service/ollama-client.d.ts.map +0 -1
  147. package/dist/types/src/ai-service/parser.d.ts +0 -138
  148. package/dist/types/src/ai-service/parser.d.ts.map +0 -1
  149. package/dist/types/src/ai-service/service.d.ts +0 -9
  150. package/dist/types/src/ai-service/service.d.ts.map +0 -1
  151. package/dist/types/src/ai-service/stream.d.ts +0 -40
  152. package/dist/types/src/ai-service/stream.d.ts.map +0 -1
  153. package/dist/types/src/ai-service/stream.test.d.ts +0 -2
  154. package/dist/types/src/ai-service/stream.test.d.ts.map +0 -1
  155. package/dist/types/src/ai-service/testing/index.d.ts +0 -3
  156. package/dist/types/src/ai-service/testing/index.d.ts.map +0 -1
  157. package/dist/types/src/ai-service/testing/mock.d.ts +0 -7
  158. package/dist/types/src/ai-service/testing/mock.d.ts.map +0 -1
  159. package/dist/types/src/ai-service/testing/test-stream.d.ts +0 -7
  160. package/dist/types/src/ai-service/testing/test-stream.d.ts.map +0 -1
  161. package/dist/types/src/ai-service/transform.d.ts +0 -27
  162. package/dist/types/src/ai-service/transform.d.ts.map +0 -1
  163. package/dist/types/src/ai-service/types.d.ts +0 -167
  164. package/dist/types/src/ai-service/types.d.ts.map +0 -1
  165. package/dist/types/src/ai-service/util/index.d.ts +0 -2
  166. package/dist/types/src/ai-service/util/index.d.ts.map +0 -1
  167. package/dist/types/src/ai-service/util/sse.d.ts +0 -39
  168. package/dist/types/src/ai-service/util/sse.d.ts.map +0 -1
  169. package/dist/types/src/conversation/conversation.d.ts +0 -80
  170. package/dist/types/src/conversation/conversation.d.ts.map +0 -1
  171. package/dist/types/src/conversation/conversation.test.d.ts +0 -2
  172. package/dist/types/src/conversation/conversation.test.d.ts.map +0 -1
  173. package/dist/types/src/conversation/index.d.ts +0 -2
  174. package/dist/types/src/conversation/index.d.ts.map +0 -1
  175. package/dist/types/src/conversation/types.d.ts +0 -79
  176. package/dist/types/src/conversation/types.d.ts.map +0 -1
  177. package/dist/types/src/cypher/ast.d.ts +0 -56
  178. package/dist/types/src/cypher/ast.d.ts.map +0 -1
  179. package/dist/types/src/cypher/combinators.d.ts +0 -14
  180. package/dist/types/src/cypher/combinators.d.ts.map +0 -1
  181. package/dist/types/src/cypher/combinators.test.d.ts +0 -2
  182. package/dist/types/src/cypher/combinators.test.d.ts.map +0 -1
  183. package/dist/types/src/cypher/echo-data-source.d.ts +0 -16
  184. package/dist/types/src/cypher/echo-data-source.d.ts.map +0 -1
  185. package/dist/types/src/cypher/execution-plan.d.ts +0 -49
  186. package/dist/types/src/cypher/execution-plan.d.ts.map +0 -1
  187. package/dist/types/src/cypher/execution-plan.test.d.ts +0 -2
  188. package/dist/types/src/cypher/execution-plan.test.d.ts.map +0 -1
  189. package/dist/types/src/cypher/index.d.ts +0 -5
  190. package/dist/types/src/cypher/index.d.ts.map +0 -1
  191. package/dist/types/src/cypher/parjs.d.ts +0 -4
  192. package/dist/types/src/cypher/parjs.d.ts.map +0 -1
  193. package/dist/types/src/cypher/parser.d.ts +0 -6
  194. package/dist/types/src/cypher/parser.d.ts.map +0 -1
  195. package/dist/types/src/cypher/query-executor.d.ts +0 -26
  196. package/dist/types/src/cypher/query-executor.d.ts.map +0 -1
  197. package/dist/types/src/cypher/query-executor.test.d.ts +0 -2
  198. package/dist/types/src/cypher/query-executor.test.d.ts.map +0 -1
  199. package/dist/types/src/cypher/schema.d.ts +0 -12
  200. package/dist/types/src/cypher/schema.d.ts.map +0 -1
  201. package/dist/types/src/cypher/schema.test.d.ts +0 -2
  202. package/dist/types/src/cypher/schema.test.d.ts.map +0 -1
  203. package/dist/types/src/testing/artifacts.test.d.ts +0 -1
  204. package/dist/types/src/testing/artifacts.test.d.ts.map +0 -1
  205. package/dist/types/src/testing/data.d.ts +0 -2
  206. package/dist/types/src/testing/data.d.ts.map +0 -1
  207. package/dist/types/src/testing/defs.d.ts +0 -5
  208. package/dist/types/src/testing/defs.d.ts.map +0 -1
  209. package/dist/types/src/testing/logger.d.ts +0 -12
  210. package/dist/types/src/testing/logger.d.ts.map +0 -1
  211. package/dist/types/src/testing/object-extraction.test.d.ts +0 -1
  212. package/dist/types/src/testing/object-extraction.test.d.ts.map +0 -1
  213. package/dist/types/src/testing/ollama.d.ts +0 -6
  214. package/dist/types/src/testing/ollama.d.ts.map +0 -1
  215. package/dist/types/src/testing/query-promts.d.ts +0 -5
  216. package/dist/types/src/testing/query-promts.d.ts.map +0 -1
  217. package/dist/types/src/testing/query.test.d.ts +0 -2
  218. package/dist/types/src/testing/query.test.d.ts.map +0 -1
  219. package/dist/types/src/testing/test-data.d.ts +0 -29
  220. package/dist/types/src/testing/test-data.d.ts.map +0 -1
  221. package/dist/types/src/testing/test-schema.d.ts +0 -50
  222. package/dist/types/src/testing/test-schema.d.ts.map +0 -1
  223. package/dist/types/src/util/index.d.ts +0 -2
  224. package/dist/types/src/util/index.d.ts.map +0 -1
  225. package/dist/types/src/util/trim.d.ts +0 -5
  226. package/dist/types/src/util/trim.d.ts.map +0 -1
  227. package/src/ai-service/client.test.ts +0 -265
  228. package/src/ai-service/defs.ts +0 -39
  229. package/src/ai-service/edge-client.ts +0 -67
  230. package/src/ai-service/index.ts +0 -13
  231. package/src/ai-service/message-collector.ts +0 -84
  232. package/src/ai-service/ollama-client.ts +0 -499
  233. package/src/ai-service/parser.ts +0 -358
  234. package/src/ai-service/service.ts +0 -15
  235. package/src/ai-service/stream.test.ts +0 -125
  236. package/src/ai-service/stream.ts +0 -118
  237. package/src/ai-service/testing/index.ts +0 -6
  238. package/src/ai-service/testing/mock.ts +0 -22
  239. package/src/ai-service/testing/test-stream.ts +0 -93
  240. package/src/ai-service/transform.ts +0 -121
  241. package/src/ai-service/types.ts +0 -103
  242. package/src/ai-service/util/index.ts +0 -5
  243. package/src/ai-service/util/sse.ts +0 -341
  244. package/src/bin/chat.ts +0 -79
  245. package/src/conversation/conversation.test.ts +0 -73
  246. package/src/conversation/conversation.ts +0 -257
  247. package/src/conversation/index.ts +0 -5
  248. package/src/conversation/types.ts +0 -129
  249. package/src/cypher/ast.ts +0 -75
  250. package/src/cypher/combinators.test.ts +0 -137
  251. package/src/cypher/combinators.ts +0 -148
  252. package/src/cypher/echo-data-source.ts +0 -105
  253. package/src/cypher/execution-plan.test.ts +0 -44
  254. package/src/cypher/execution-plan.ts +0 -208
  255. package/src/cypher/index.ts +0 -8
  256. package/src/cypher/parjs-shims.d.ts +0 -10
  257. package/src/cypher/parjs.ts +0 -10
  258. package/src/cypher/parser.ts +0 -19
  259. package/src/cypher/query-executor.test.ts +0 -47
  260. package/src/cypher/query-executor.ts +0 -145
  261. package/src/cypher/schema.test.ts +0 -17
  262. package/src/cypher/schema.ts +0 -86
  263. package/src/testing/artifacts.test.ts +0 -182
  264. package/src/testing/data.ts +0 -13
  265. package/src/testing/defs.ts +0 -9
  266. package/src/testing/logger.ts +0 -129
  267. package/src/testing/object-extraction.test.ts +0 -71
  268. package/src/testing/ollama.ts +0 -15
  269. package/src/testing/query-promts.ts +0 -75
  270. package/src/testing/query.test.ts +0 -91
  271. package/src/testing/test-data.ts +0 -372
  272. package/src/testing/test-schema.ts +0 -80
  273. package/src/util/index.ts +0 -5
  274. package/src/util/trim.ts +0 -15
  275. package/vendor/parjs-browser.mjs +0 -4
  276. package/vendor/parjs-default.mjs +0 -8
  277. /package/dist/types/{src/bin → bin}/chat.d.ts +0 -0
@@ -1,358 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Message, type MessageContentBlock } from '@dxos/artifact';
6
- import { Event } from '@dxos/async';
7
- import { createStatic } from '@dxos/echo-schema';
8
- import { invariant } from '@dxos/invariant';
9
- import { log } from '@dxos/log';
10
- import { isNotFalsy, safeParseJson } from '@dxos/util';
11
-
12
- import { type GenerationStream } from './service';
13
- import { StreamTransform, type StreamBlock } from './transform';
14
- import type { GenerationStreamEvent } from './types';
15
-
16
- /**
17
- * Parse mixed content of plain text, XML fragments, and JSON blocks.
18
- */
19
- export class MixedStreamParser {
20
- /**
21
- * New message.
22
- */
23
- public message = new Event<Message>();
24
-
25
- /**
26
- * Complete block added to Message.
27
- */
28
- public block = new Event<MessageContentBlock>();
29
-
30
- /**
31
- * Update partial block (while streaming).
32
- */
33
- public update = new Event<MessageContentBlock>();
34
-
35
- public streamEvent = new Event<GenerationStreamEvent>();
36
-
37
- /**
38
- * Current message.
39
- */
40
- private _message?: Message | undefined;
41
-
42
- private _emitBlock(contentBlock: MessageContentBlock, streamBlock?: StreamBlock) {
43
- const messageBlock = streamBlock ? mergeMessageBlock(contentBlock, streamBlock) : contentBlock;
44
- if (messageBlock) {
45
- if (messageBlock.type === 'text' && messageBlock.text.length === 0) {
46
- return;
47
- }
48
-
49
- invariant(this._message);
50
- this._message.content.push(messageBlock);
51
- this.block.emit(messageBlock);
52
- }
53
- }
54
-
55
- private _emitUpdate(contentBlock: MessageContentBlock, streamBlock: StreamBlock) {
56
- const messageBlock = mergeMessageBlock(contentBlock, streamBlock);
57
- if (messageBlock) {
58
- messageBlock.pending = true;
59
- this.update.emit(messageBlock);
60
- }
61
- }
62
-
63
- /**
64
- * Parse stream until end.
65
- */
66
- async parse(stream: GenerationStream): Promise<Message[]> {
67
- const transformer = new StreamTransform();
68
-
69
- /**
70
- * Current content message block.
71
- */
72
- let contentBlock: MessageContentBlock | undefined;
73
-
74
- /**
75
- * Current partial block used to accumulate content.
76
- */
77
- let streamBlock: StreamBlock | undefined;
78
- const stack: StreamBlock[] = [];
79
-
80
- const messagesCollected: Message[] = [];
81
-
82
- for await (const event of stream) {
83
- log('streamEvent', { event });
84
- this.streamEvent.emit(event);
85
-
86
- // log('event', { type: event.type, event });
87
- switch (event.type) {
88
- //
89
- // Messages.
90
- //
91
-
92
- case 'message_start': {
93
- if (this._message) {
94
- log.warn('unexpected message_start');
95
- }
96
-
97
- this._message = createStatic(Message, { role: event.message.role, content: [...event.message.content] });
98
- this.message.emit(this._message);
99
- break;
100
- }
101
-
102
- case 'message_delta': {
103
- if (!this._message) {
104
- log.warn('unexpected message_delta');
105
- }
106
-
107
- break;
108
- }
109
-
110
- case 'message_stop': {
111
- if (!this._message) {
112
- log.warn('unexpected message_stop');
113
- } else {
114
- messagesCollected.push(this._message);
115
- this._message = undefined;
116
- }
117
- break;
118
- }
119
-
120
- //
121
- // Content blocks.
122
- //
123
-
124
- case 'content_block_start': {
125
- if (contentBlock) {
126
- log.warn('unexpected content_block_start', { content: contentBlock });
127
- }
128
-
129
- // TODO(burdon): Parse initial content (factor out from content_block_delta).
130
- contentBlock = event.content;
131
- break;
132
- }
133
-
134
- case 'content_block_delta': {
135
- if (!contentBlock) {
136
- log.warn('unexpected content_block_delta');
137
- break;
138
- }
139
-
140
- switch (event.delta.type) {
141
- //
142
- // Text
143
- //
144
- case 'text_delta': {
145
- const chunks = transformer.transform(event.delta.text);
146
- for (const chunk of chunks) {
147
- log('text_delta', { chunk });
148
-
149
- switch (streamBlock?.type) {
150
- //
151
- // XML Fragment.
152
- //
153
- case 'tag': {
154
- if (chunk.type === 'tag') {
155
- if (chunk.selfClosing) {
156
- streamBlock.content.push(chunk);
157
- } else if (chunk.closing) {
158
- if (stack.length > 0) {
159
- const top = stack.pop();
160
- invariant(top && top.type === 'tag');
161
- log('pop', { top });
162
- top.content.push(streamBlock);
163
- streamBlock = top;
164
- } else {
165
- this._emitBlock(contentBlock, streamBlock);
166
- streamBlock = undefined;
167
- }
168
- } else {
169
- stack.push(streamBlock);
170
- streamBlock = chunk;
171
- }
172
- } else {
173
- // Append text.
174
- if (streamBlock.content.length === 0) {
175
- streamBlock.content.push(chunk);
176
- } else {
177
- const last = streamBlock.content.at(-1);
178
- invariant(last);
179
- if (last.type === 'text') {
180
- last.content += chunk.content;
181
- } else {
182
- streamBlock.content.push(chunk);
183
- }
184
- }
185
- }
186
- break;
187
- }
188
-
189
- //
190
- // Text Fragment.
191
- //
192
- case 'text': {
193
- if (chunk.type === 'tag') {
194
- this._emitBlock(contentBlock, streamBlock);
195
- if (chunk.selfClosing) {
196
- this._emitBlock(contentBlock, chunk);
197
- streamBlock = undefined;
198
- } else {
199
- streamBlock = chunk;
200
- }
201
- } else {
202
- // Append text.
203
- streamBlock.content += chunk.content;
204
- }
205
- break;
206
- }
207
-
208
- //
209
- // No current chunk.
210
- //
211
- default: {
212
- if (chunk.type === 'tag' && chunk.selfClosing) {
213
- this._emitBlock(contentBlock, chunk);
214
- } else {
215
- streamBlock = chunk;
216
- }
217
- }
218
- }
219
- }
220
- break;
221
- }
222
-
223
- //
224
- // JSON
225
- //
226
- case 'input_json_delta': {
227
- if (!contentBlock) {
228
- log.warn('unexpected input_json_delta', { contentBlock });
229
- break;
230
- }
231
-
232
- if (streamBlock?.type === 'json') {
233
- streamBlock.content += event.delta.partial_json;
234
- } else {
235
- streamBlock = { type: 'json', content: event.delta.partial_json };
236
- }
237
- break;
238
- }
239
- }
240
- break;
241
- }
242
-
243
- case 'content_block_stop': {
244
- if (!contentBlock) {
245
- log.warn('unexpected content_block_stop');
246
- break;
247
- }
248
-
249
- this._emitBlock(contentBlock, streamBlock);
250
- contentBlock = undefined;
251
- streamBlock = undefined;
252
- break;
253
- }
254
- }
255
-
256
- if (contentBlock && streamBlock) {
257
- this._emitUpdate(contentBlock, streamBlock);
258
- }
259
- } // for.
260
-
261
- return messagesCollected;
262
- }
263
- }
264
-
265
- /**
266
- * Convert stream block to message content block.
267
- */
268
- export const mergeMessageBlock = (
269
- contentBlock: MessageContentBlock,
270
- streamBlock: StreamBlock,
271
- ): MessageContentBlock | undefined => {
272
- log('mergeMessageBlock', { contentBlock, streamBlock });
273
-
274
- switch (streamBlock.type) {
275
- //
276
- // Text
277
- //
278
- case 'text': {
279
- return { ...contentBlock, type: 'text', text: streamBlock.content };
280
- }
281
-
282
- //
283
- // XML
284
- //
285
- case 'tag': {
286
- switch (streamBlock.tag) {
287
- case 'cot': {
288
- const content = streamBlock.content
289
- .map((block) => {
290
- switch (block.type) {
291
- case 'text':
292
- return block.content;
293
- default:
294
- return null;
295
- }
296
- })
297
- .filter(isNotFalsy)
298
- .join('\n');
299
-
300
- return { ...contentBlock, type: 'text', disposition: 'cot', text: content };
301
- }
302
-
303
- case 'artifact': {
304
- const { attributes } = streamBlock;
305
- return { type: 'json', disposition: 'artifact', json: JSON.stringify(attributes) };
306
- }
307
-
308
- case 'suggest': {
309
- if (streamBlock.content.length === 1 && streamBlock.content[0].type === 'text') {
310
- return {
311
- type: 'json',
312
- disposition: 'suggest',
313
- json: JSON.stringify({ text: streamBlock.content[0].content }),
314
- };
315
- }
316
- break;
317
- }
318
-
319
- case 'select': {
320
- return {
321
- type: 'json',
322
- disposition: 'select',
323
- json: JSON.stringify({
324
- options: streamBlock.content.flatMap((content) =>
325
- content.type === 'tag' && content.content.length === 1 && content.content[0].type === 'text'
326
- ? [content.content[0].content]
327
- : [],
328
- ),
329
- }),
330
- };
331
- }
332
-
333
- case 'tool-list': {
334
- return {
335
- type: 'json',
336
- disposition: 'tool_list',
337
- json: JSON.stringify({}),
338
- };
339
- }
340
- }
341
-
342
- break;
343
- }
344
-
345
- //
346
- // JSON
347
- //
348
- case 'json': {
349
- switch (contentBlock.type) {
350
- case 'tool_use': {
351
- return { ...contentBlock, input: safeParseJson(streamBlock.content) ?? {} };
352
- }
353
- }
354
-
355
- return { ...contentBlock, type: 'json', disposition: 'artifact', json: streamBlock.content };
356
- }
357
- }
358
- };
@@ -1,15 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { type GenerateRequest, type GenerationStreamEvent } from './types';
6
-
7
- // TODO(burdon): Create wrapper for edge/ollama. Create session?
8
- export interface AIServiceClient {
9
- exec(request: GenerateRequest): Promise<GenerationStream>;
10
- }
11
-
12
- export interface GenerationStream extends AsyncIterable<GenerationStreamEvent> {
13
- abort(): void;
14
- complete(): Promise<void>;
15
- }
@@ -1,125 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { describe, test } from 'vitest';
6
-
7
- import { type Message } from '@dxos/artifact';
8
- import { log } from '@dxos/log';
9
-
10
- import { MixedStreamParser } from './parser';
11
- import { createGenerationStream } from './stream';
12
- import { createTestSSEStream } from './testing';
13
- import { type GenerationStreamEvent } from './types';
14
-
15
- // TODO(burdon): Tool request/response.
16
-
17
- const TEST_BLOCKS = [
18
- [
19
- // Text
20
- 'some text',
21
- 'on multiple lines.',
22
-
23
- // XML
24
- '<cot>',
25
- ' 1. Analyze content.',
26
- ' 2. Create plan.',
27
- ' 3. Generate artifacts.',
28
- '</cot>',
29
-
30
- // Text
31
- 'some more text.',
32
-
33
- // XML
34
- '<artifact id="123" />',
35
-
36
- // XML
37
- '<artifact id="456" />',
38
-
39
- // Text
40
- 'and some more text.',
41
-
42
- '<suggest>Plan me a trip to the Rio.</suggest>',
43
- '<suggest>Show me steak recipes.</suggest>',
44
- '<select><option>Yes</option><option>No</option></select>',
45
- ].join('\n'),
46
-
47
- // JSON
48
- {
49
- type: 'input',
50
- value: {
51
- level: 1,
52
- optional: true,
53
- },
54
- },
55
- ];
56
-
57
- describe('GenerationStream', () => {
58
- test('should generate a stream of blocks', async ({ expect }) => {
59
- const stream = createGenerationStream(new Response(createTestSSEStream(TEST_BLOCKS)));
60
- const events: GenerationStreamEvent[] = [];
61
- for await (const event of stream) {
62
- events.push(event);
63
- }
64
-
65
- expect(events.map((event) => event.type === 'content_block_start').filter(Boolean)).to.have.length(2);
66
- });
67
-
68
- for (const splitBy of ['word'] as const) {
69
- test(`should emit xml blocks (splitBy: ${splitBy})`, async ({ expect }) => {
70
- const stream = createGenerationStream(new Response(createTestSSEStream(TEST_BLOCKS, { splitBy })));
71
- const parser = new MixedStreamParser();
72
-
73
- let message: Message | undefined;
74
- parser.message.on((_message) => {
75
- message = _message;
76
- });
77
-
78
- await parser.parse(stream);
79
-
80
- for (const block of message?.content ?? []) {
81
- log('block', { block });
82
- }
83
- expect(message?.content.map((block) => block.type)).to.deep.eq([
84
- //
85
- 'text',
86
- 'text',
87
- 'text',
88
- 'json',
89
- 'text',
90
- 'json',
91
- 'text',
92
- 'json',
93
- 'text',
94
- 'json',
95
- 'text',
96
- 'json',
97
- 'json',
98
- ]);
99
- });
100
- }
101
-
102
- test.skip('invalid xml', async ({ expect }) => {
103
- const BLOCKS = [
104
- //
105
- '<cot> thoughts <tool-list> more thoughts </cot> <tool-list/>',
106
- ];
107
-
108
- const stream = createGenerationStream(new Response(createTestSSEStream(BLOCKS, { splitBy: 'word' })));
109
- const parser = new MixedStreamParser();
110
-
111
- const result = await parser.parse(stream);
112
- expect(result[0].content).to.deep.eq([
113
- {
114
- type: 'text',
115
- disposition: 'cot',
116
- text: 'thoughts more thoughts',
117
- },
118
- {
119
- type: 'json',
120
- disposition: 'tool_list',
121
- json: '{}',
122
- },
123
- ]);
124
- });
125
- });
@@ -1,118 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { Trigger } from '@dxos/async';
6
- import { log } from '@dxos/log';
7
-
8
- import type { GenerationStream } from './service';
9
- import { type GenerationStreamEvent } from './types';
10
- import { iterSSEMessages } from './util';
11
-
12
- /**
13
- * Creates a stream from an SSE response.
14
- */
15
- export const createGenerationStream = (response: Response, controller = new AbortController()): GenerationStream => {
16
- const iterator = async function* () {
17
- for await (const sse of iterSSEMessages(response, controller)) {
18
- if (sse.event === 'completion') {
19
- try {
20
- yield JSON.parse(sse.data);
21
- } catch (err) {
22
- log.error('could not parse message into JSON:', { data: sse.data, raw: sse.raw });
23
- throw err;
24
- }
25
- }
26
-
27
- if (
28
- sse.event === 'message_start' ||
29
- sse.event === 'message_delta' ||
30
- sse.event === 'message_stop' ||
31
- sse.event === 'content_block_start' ||
32
- sse.event === 'content_block_delta' ||
33
- sse.event === 'content_block_stop'
34
- ) {
35
- try {
36
- yield JSON.parse(sse.data);
37
- } catch (err) {
38
- log.error('could not parse message into JSON:', { data: sse.data, raw: sse.raw });
39
- throw err;
40
- }
41
- }
42
-
43
- if (sse.event === 'ping') {
44
- continue;
45
- }
46
-
47
- if (sse.event === 'error') {
48
- throw new Error(`Message generation error: ${sse.data}`);
49
- }
50
- }
51
- };
52
-
53
- return new GenerationStreamImpl(controller, iterator);
54
- };
55
-
56
- /**
57
- * Server-Sent Events (SSE) stream from the AI service.
58
- * https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events
59
- */
60
- export class GenerationStreamImpl implements GenerationStream {
61
- /**
62
- * Trigger event when the stream is done.
63
- */
64
- private readonly _done = new Trigger();
65
-
66
- /**
67
- * Iterator over the stream.
68
- */
69
- private _iterator?: AsyncIterator<GenerationStreamEvent> = undefined;
70
-
71
- constructor(
72
- private readonly _controller: AbortController,
73
- private readonly _getIterator: () => AsyncIterableIterator<GenerationStreamEvent>,
74
- ) {}
75
-
76
- /**
77
- * Returns an iterator over the stream.
78
- */
79
- [Symbol.asyncIterator](): AsyncIterator<GenerationStreamEvent> {
80
- return this._createIterator();
81
- }
82
-
83
- /**
84
- * Aborts the stream.
85
- */
86
- abort() {
87
- this._controller.abort();
88
- }
89
-
90
- /**
91
- * Returns the complete message list generated by the AI service.
92
- */
93
- async complete() {
94
- await this._done.wait();
95
- }
96
-
97
- /**
98
- * Creates an iterator over the stream.
99
- */
100
- private _createIterator(): AsyncIterator<GenerationStreamEvent> {
101
- const self = this;
102
- return (this._iterator ??= (() => {
103
- const generator = async function* (this: GenerationStreamImpl) {
104
- try {
105
- for await (const event of self._getIterator()) {
106
- yield event;
107
- }
108
- this._done.wake();
109
- } catch (err: any) {
110
- this._done.throw(err);
111
- throw err;
112
- }
113
- };
114
-
115
- return generator.call(this);
116
- })());
117
- }
118
- }
@@ -1,6 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- export * from './mock';
6
- export * from './test-stream';
@@ -1,22 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import {
6
- type GenerationStream,
7
- type AIServiceClient,
8
- type GenerateRequest,
9
- createGenerationStream,
10
- } from '@dxos/assistant';
11
-
12
- import { createTestSSEStream } from './test-stream';
13
-
14
- export class MockAIService implements AIServiceClient {
15
- constructor(private readonly _responses: string[]) {}
16
-
17
- // TODO(burdon): Match response to request.
18
- // TODO(burdon): See MockGpt (from conductor).
19
- async exec(request: GenerateRequest): Promise<GenerationStream> {
20
- return createGenerationStream(new Response(createTestSSEStream(this._responses)));
21
- }
22
- }