@parallelworks/ui 0.19.0 → 0.19.1

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 (217) hide show
  1. package/dist/{chunks/memoryStore-CC91S5LP.js → ai/adapter/memoryStore.js} +2 -2
  2. package/dist/ai/adapter/mock/index.js +2 -80
  3. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  4. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  5. package/dist/ai/adapter/openai/errors.js +37 -0
  6. package/dist/ai/adapter/openai/index.js +5 -333
  7. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  8. package/dist/ai/adapter/openai/responses.js +108 -0
  9. package/dist/ai/adapter/openai/sse.js +112 -0
  10. package/dist/{chunks/types-Yy0NrerF.js → ai/adapter/types.js} +1 -1
  11. package/dist/ai/agent/classifier.js +12 -0
  12. package/dist/ai/agent/diff.js +223 -0
  13. package/dist/ai/agent/diffHighlight.js +101 -0
  14. package/dist/ai/agent/permissions.js +29 -0
  15. package/dist/ai/agent/toolfmt.js +383 -0
  16. package/dist/ai/agent/transcript.js +127 -0
  17. package/dist/ai/components/AllocationSelector.js +56 -0
  18. package/dist/ai/components/AttachmentManager.js +144 -0
  19. package/dist/ai/components/AttachmentUpload.js +179 -0
  20. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  21. package/dist/ai/components/BranchNavigator.js +57 -0
  22. package/dist/ai/components/ChatEmptyState.js +171 -0
  23. package/dist/ai/components/ChatInput.js +265 -0
  24. package/dist/ai/components/ChatLayout.js +15 -0
  25. package/dist/ai/components/ChatMessage.js +338 -0
  26. package/dist/ai/components/ChatMessageList.js +301 -0
  27. package/dist/ai/components/ChatSidebar.js +345 -0
  28. package/dist/ai/components/ChatThread.js +198 -0
  29. package/dist/ai/components/ComposerChrome.js +50 -0
  30. package/dist/ai/components/ComposerFrame.js +33 -0
  31. package/dist/ai/components/DragOverlay.js +20 -0
  32. package/dist/ai/components/ModelSelector.js +343 -0
  33. package/dist/ai/components/ShareDialog.js +260 -0
  34. package/dist/ai/components/SlashMenu.js +91 -0
  35. package/dist/ai/components/agent/ActivityLine.js +151 -0
  36. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  37. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  38. package/dist/ai/components/attachmentKeys.js +10 -0
  39. package/dist/ai/core/ChatProvider.js +848 -0
  40. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  41. package/dist/ai/core/chatReducer.js +223 -0
  42. package/dist/ai/core/config.js +45 -0
  43. package/dist/ai/core/events.js +4 -0
  44. package/dist/ai/core/exportConversation.js +42 -0
  45. package/dist/ai/core/greeting.js +38 -0
  46. package/dist/ai/core/parts.js +84 -0
  47. package/dist/ai/core/useDragDrop.js +36 -0
  48. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  49. package/dist/ai/core/useSSEStream.js +98 -0
  50. package/dist/ai/index.js +43 -6644
  51. package/dist/ai/strings.js +175 -0
  52. package/dist/ai/ui/Avatar.js +35 -0
  53. package/dist/ai/ui/Dropdown.js +110 -0
  54. package/dist/ai/ui/Markdown.js +79 -0
  55. package/dist/ai/ui/Notice.js +77 -0
  56. package/dist/ai/ui/codeBreaks.js +36 -0
  57. package/dist/ai/ui/prose.js +13 -0
  58. package/dist/ai/ui/sidebar.js +148 -0
  59. package/dist/ai/utils.js +23 -0
  60. package/dist/components/AddMenu.js +46 -0
  61. package/dist/components/Avatar.js +46 -0
  62. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  63. package/dist/components/Breadcrumbs.js +83 -0
  64. package/dist/components/Button.js +83 -0
  65. package/dist/components/Callout.js +23 -0
  66. package/dist/components/Card.js +31 -0
  67. package/dist/components/CollapsiblePanel.js +77 -0
  68. package/dist/{chunks/ConfirmModal-aOR4O1QE.js → components/ConfirmModal.js} +4 -9
  69. package/dist/components/CopyToClipboard.js +81 -0
  70. package/dist/{chunks/Markdown-CM-FenNE.js → components/CreateModal.js} +9 -116
  71. package/dist/components/DescriptionList.js +42 -0
  72. package/dist/components/Dropdown.js +349 -0
  73. package/dist/components/EmptyState.js +27 -0
  74. package/dist/components/Filter.js +32 -0
  75. package/dist/components/FilterPill.js +13 -0
  76. package/dist/components/IconButton.js +34 -0
  77. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  78. package/dist/components/Input.js +88 -0
  79. package/dist/components/LetterBadge.js +13 -0
  80. package/dist/components/Listbox.js +123 -0
  81. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  82. package/dist/components/Markdown.js +15 -0
  83. package/dist/components/MultiSelect.js +171 -0
  84. package/dist/components/Overview.js +39 -0
  85. package/dist/components/PageHeader.js +194 -0
  86. package/dist/components/Pagination.js +35 -0
  87. package/dist/{chunks/Provider-CDYurraE.js → components/Provider.js} +1 -1
  88. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  89. package/dist/components/SectionHeader.js +11 -0
  90. package/dist/components/SettingModeOption.js +30 -0
  91. package/dist/components/SettingRow.js +35 -0
  92. package/dist/components/SettingsCard.js +31 -0
  93. package/dist/components/SettingsGroup.js +17 -0
  94. package/dist/components/Sort.js +42 -0
  95. package/dist/components/StatusBadge.js +63 -0
  96. package/dist/components/SwitchToggle.js +59 -0
  97. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  98. package/dist/{chunks/Table-DU2MW8xz.js → components/Table.js} +3 -3
  99. package/dist/components/Toggle.js +20 -0
  100. package/dist/{chunks/Tooltip-K5HJr4_p.js → components/Tooltip.js} +2 -27
  101. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  102. package/dist/{chunks/focus-D_iZJTW1.js → components/focus.js} +1 -1
  103. package/dist/components/ghostButton.js +7 -0
  104. package/dist/components/inputClasses.js +4 -0
  105. package/dist/components/keys.js +27 -0
  106. package/dist/components/relativeTime.js +32 -0
  107. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  108. package/dist/{chunks/duration-BOv8Phl8.js → duration.js} +1 -1
  109. package/dist/editor/Editor.js +13 -0
  110. package/dist/editor/EditorField.js +53 -0
  111. package/dist/editor/FormCodePanel.js +173 -0
  112. package/dist/{chunks/Monaco-DJ8MW-Dq.js → editor/Monaco.js} +4 -2
  113. package/dist/editor/index.js +6 -1
  114. package/dist/editor/themes.js +91 -0
  115. package/dist/editor/workers.js +29 -0
  116. package/dist/editor/yaml.js +15 -0
  117. package/dist/file-explorer/FileExplorer.js +1402 -0
  118. package/dist/file-explorer/FileExplorerContext.js +370 -0
  119. package/dist/{chunks/CodePreviewEditor-YqQBz1YL.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  120. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  121. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  122. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  123. package/dist/file-explorer/FilePreview/index.js +266 -0
  124. package/dist/file-explorer/FilePreview/parse.js +63 -0
  125. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  126. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  127. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  128. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  129. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  130. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  131. package/dist/file-explorer/FileRow.js +165 -0
  132. package/dist/file-explorer/FileRowsWindow.js +92 -0
  133. package/dist/file-explorer/FileTable.js +172 -0
  134. package/dist/file-explorer/FileTableHeaders.js +49 -0
  135. package/dist/file-explorer/LoadMoreRow.js +46 -0
  136. package/dist/file-explorer/TreeView.js +376 -0
  137. package/dist/file-explorer/helpers.js +537 -0
  138. package/dist/file-explorer/index.js +7 -5671
  139. package/dist/file-explorer/lib/batch.js +39 -0
  140. package/dist/file-explorer/lib/errors.js +9 -0
  141. package/dist/file-explorer/lib/paging.js +124 -0
  142. package/dist/file-explorer/lib/utils.js +322 -0
  143. package/dist/file-explorer/toasts.js +400 -0
  144. package/dist/file-explorer/treeRows.js +73 -0
  145. package/dist/file-explorer/useNearEnd.js +27 -0
  146. package/dist/form/FieldWrapper.js +54 -0
  147. package/dist/form/Fields/BooleanField.js +51 -0
  148. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  149. package/dist/form/Fields/ColorField.js +33 -0
  150. package/dist/form/Fields/DropdownField.js +49 -0
  151. package/dist/form/Fields/DurationField.js +130 -0
  152. package/dist/form/Fields/LabelField.js +19 -0
  153. package/dist/form/Fields/MultiDropdownField.js +40 -0
  154. package/dist/form/Fields/NumberField.js +51 -0
  155. package/dist/form/Fields/ObjectField.js +54 -0
  156. package/dist/form/Fields/RadioField.js +49 -0
  157. package/dist/form/Fields/RangeField.js +58 -0
  158. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  159. package/dist/form/Fields/StepField.js +6 -0
  160. package/dist/form/Fields/StringField.js +37 -0
  161. package/dist/form/Fields/TextAreaField.js +31 -0
  162. package/dist/form/Fields/durationValidity.js +11 -0
  163. package/dist/form/Form.js +643 -0
  164. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  165. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  166. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  167. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  168. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  169. package/dist/form/Wizard/WizardContainer.js +138 -0
  170. package/dist/form/Wizard/WizardNavigation.js +92 -0
  171. package/dist/form/Wizard/WizardStepContent.js +35 -0
  172. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  173. package/dist/form/Wizard/useWizardState.js +118 -0
  174. package/dist/form/Wizard/utils.js +34 -0
  175. package/dist/form/fieldContext.js +26 -0
  176. package/dist/form/fieldRegistry.js +50 -0
  177. package/dist/form/index.js +17 -2321
  178. package/dist/{chunks/FieldWrapper-Cszwg-4B.js → form/lib.js} +2 -97
  179. package/dist/form/useParsedOpts.js +62 -0
  180. package/dist/form/usePrevious.js +11 -0
  181. package/dist/form/utils/checkShowIfObject.js +14 -0
  182. package/dist/form/utils/duration.js +69 -0
  183. package/dist/form/utils/getParentValue.js +23 -0
  184. package/dist/form/utils/getPlaceholder.js +9 -0
  185. package/dist/form/utils/getValueUsingPath.js +23 -0
  186. package/dist/form/utils/secondaryField.js +22 -0
  187. package/dist/graph/AnnotationBanner.js +141 -0
  188. package/dist/graph/Collapse.js +41 -0
  189. package/dist/graph/DependencyGraph.js +1398 -0
  190. package/dist/graph/JobSummary.js +104 -0
  191. package/dist/graph/MatrixGroup.js +107 -0
  192. package/dist/graph/Reveal.js +57 -0
  193. package/dist/graph/TreeView.js +711 -0
  194. package/dist/graph/index.js +1 -2519
  195. package/dist/graph/types.js +6 -0
  196. package/dist/index.js +46 -599
  197. package/dist/list/ActionToolbar.js +184 -0
  198. package/dist/list/HoverCard.js +73 -0
  199. package/dist/list/ListPage.js +145 -0
  200. package/dist/list/ListPager.js +59 -0
  201. package/dist/list/ListTable.js +163 -0
  202. package/dist/list/ListViewControls.js +372 -0
  203. package/dist/list/RowContextMenu.js +350 -0
  204. package/dist/list/UserHoverCard.js +117 -0
  205. package/dist/list/index.js +11 -2
  206. package/dist/list/listSearch.js +137 -0
  207. package/dist/list/listView.js +210 -0
  208. package/dist/list/textEntry.js +10 -0
  209. package/dist/{chunks/logviewer-BTKuQWzw.js → logviewer/LogViewer.js} +4 -82
  210. package/dist/logviewer/index.js +2 -1
  211. package/dist/logviewer/segments.js +80 -0
  212. package/package.json +1 -1
  213. package/dist/chunks/PageHeader-0kG_iypP.js +0 -692
  214. package/dist/chunks/SwitchToggle-DShNdcao.js +0 -758
  215. package/dist/chunks/editor-Dk1rbV75.js +0 -361
  216. package/dist/chunks/list-CJm875m7.js +0 -1418
  217. /package/dist/{chunks/MarkdownImpl-CyeTVvSt.js → components/MarkdownImpl.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { t as ChatAdapterError } from "./types-Yy0NrerF.js";
1
+ import { ChatAdapterError } from "./types.js";
2
2
  //#region src/ai/adapter/memoryStore.ts
3
3
  function newId() {
4
4
  return crypto.randomUUID();
@@ -149,4 +149,4 @@ function createMemoryConversationStore(options) {
149
149
  };
150
150
  }
151
151
  //#endregion
152
- export { createMemoryConversationStore as t };
152
+ export { createMemoryConversationStore };
@@ -1,81 +1,3 @@
1
- import { t as createMemoryConversationStore } from "../../../chunks/memoryStore-CC91S5LP.js";
2
- //#region src/ai/adapter/mock/mockAdapter.ts
3
- var CANNED_REPLIES = [
4
- "Here's a canned reply from the mock adapter. It streams word by word so the typewriter animation, cancel behavior, and background streams all exercise the same code paths as a real backend.\n\n- No network requests are made\n- Conversations persist in localStorage\n- Try cancelling mid-stream: the partial reply is kept",
5
- "Everything you see is generated locally. The mock adapter implements the same `ChatAdapter` interface as the OpenAI-compatible adapter and any backend's own, so the UI can't tell the difference.\n\n```ts\nconst adapter = createMockChatAdapter()\n```\n\nMarkdown, `inline code`, and code blocks render through the same pipeline.",
6
- "Branching works too: edit one of your earlier messages or hit regenerate on a reply, then use the arrows to flip between branches."
7
- ];
8
- var THINKING_PREAMBLE = "Thinking about the question, weighing a few canned options, and picking the next reply in the rotation...";
9
- function sleep(ms, signal) {
10
- return new Promise((resolve, reject) => {
11
- if (signal?.aborted) {
12
- reject(new DOMException("The operation was aborted.", "AbortError"));
13
- return;
14
- }
15
- const onAbort = () => {
16
- clearTimeout(timer);
17
- reject(new DOMException("The operation was aborted.", "AbortError"));
18
- };
19
- const timer = setTimeout(() => {
20
- signal?.removeEventListener("abort", onAbort);
21
- resolve();
22
- }, ms);
23
- signal?.addEventListener("abort", onAbort, { once: true });
24
- });
25
- }
26
- function createMockChatAdapter(options) {
27
- const store = createMemoryConversationStore({ storageKey: options?.storageKey ?? "ai-chat-mock-conversations" });
28
- let replyIndex = 0;
29
- return {
30
- conversations: store.conversations,
31
- models: { async list() {
32
- return {
33
- models: [{
34
- id: "mock:local/canned-small",
35
- object: "model",
36
- created: 0,
37
- owned_by: "mock",
38
- provider: "Mock Provider",
39
- tool_calling_mode: "none"
40
- }, {
41
- id: "mock:local/canned-thinking",
42
- object: "model",
43
- created: 0,
44
- owned_by: "mock",
45
- provider: "Mock Provider",
46
- tool_calling_mode: "none"
47
- }],
48
- unreachableSessions: []
49
- };
50
- } },
51
- streamCompletion: store.recordingStream(async (req, handlers, signal) => {
52
- if (signal?.aborted) throw new DOMException("The operation was aborted.", "AbortError");
53
- let reasoning = "";
54
- if (req.model.includes("thinking")) for (const word of THINKING_PREAMBLE.split(/(?<= )/)) {
55
- await sleep(40, signal);
56
- reasoning += word;
57
- handlers.onReasoning?.(word);
58
- }
59
- const reply = CANNED_REPLIES[replyIndex % CANNED_REPLIES.length] ?? "";
60
- replyIndex++;
61
- await sleep(250, signal);
62
- let content = "";
63
- for (const word of reply.split(/(?<= )/)) {
64
- await sleep(25, signal);
65
- content += word;
66
- handlers.onContent(word);
67
- }
68
- return {
69
- content,
70
- messageId: "",
71
- toolCalls: [],
72
- finishReason: "stop",
73
- model: req.model,
74
- reasoning,
75
- responsesOutput: []
76
- };
77
- })
78
- };
79
- }
80
- //#endregion
1
+ import { createMemoryConversationStore } from "../memoryStore.js";
2
+ import { createMockChatAdapter } from "./mockAdapter.js";
81
3
  export { createMemoryConversationStore, createMockChatAdapter };
@@ -0,0 +1,81 @@
1
+ import { createMemoryConversationStore } from "../memoryStore.js";
2
+ //#region src/ai/adapter/mock/mockAdapter.ts
3
+ var CANNED_REPLIES = [
4
+ "Here's a canned reply from the mock adapter. It streams word by word so the typewriter animation, cancel behavior, and background streams all exercise the same code paths as a real backend.\n\n- No network requests are made\n- Conversations persist in localStorage\n- Try cancelling mid-stream: the partial reply is kept",
5
+ "Everything you see is generated locally. The mock adapter implements the same `ChatAdapter` interface as the OpenAI-compatible adapter and any backend's own, so the UI can't tell the difference.\n\n```ts\nconst adapter = createMockChatAdapter()\n```\n\nMarkdown, `inline code`, and code blocks render through the same pipeline.",
6
+ "Branching works too: edit one of your earlier messages or hit regenerate on a reply, then use the arrows to flip between branches."
7
+ ];
8
+ var THINKING_PREAMBLE = "Thinking about the question, weighing a few canned options, and picking the next reply in the rotation...";
9
+ function sleep(ms, signal) {
10
+ return new Promise((resolve, reject) => {
11
+ if (signal?.aborted) {
12
+ reject(new DOMException("The operation was aborted.", "AbortError"));
13
+ return;
14
+ }
15
+ const onAbort = () => {
16
+ clearTimeout(timer);
17
+ reject(new DOMException("The operation was aborted.", "AbortError"));
18
+ };
19
+ const timer = setTimeout(() => {
20
+ signal?.removeEventListener("abort", onAbort);
21
+ resolve();
22
+ }, ms);
23
+ signal?.addEventListener("abort", onAbort, { once: true });
24
+ });
25
+ }
26
+ function createMockChatAdapter(options) {
27
+ const store = createMemoryConversationStore({ storageKey: options?.storageKey ?? "ai-chat-mock-conversations" });
28
+ let replyIndex = 0;
29
+ return {
30
+ conversations: store.conversations,
31
+ models: { async list() {
32
+ return {
33
+ models: [{
34
+ id: "mock:local/canned-small",
35
+ object: "model",
36
+ created: 0,
37
+ owned_by: "mock",
38
+ provider: "Mock Provider",
39
+ tool_calling_mode: "none"
40
+ }, {
41
+ id: "mock:local/canned-thinking",
42
+ object: "model",
43
+ created: 0,
44
+ owned_by: "mock",
45
+ provider: "Mock Provider",
46
+ tool_calling_mode: "none"
47
+ }],
48
+ unreachableSessions: []
49
+ };
50
+ } },
51
+ streamCompletion: store.recordingStream(async (req, handlers, signal) => {
52
+ if (signal?.aborted) throw new DOMException("The operation was aborted.", "AbortError");
53
+ let reasoning = "";
54
+ if (req.model.includes("thinking")) for (const word of THINKING_PREAMBLE.split(/(?<= )/)) {
55
+ await sleep(40, signal);
56
+ reasoning += word;
57
+ handlers.onReasoning?.(word);
58
+ }
59
+ const reply = CANNED_REPLIES[replyIndex % CANNED_REPLIES.length] ?? "";
60
+ replyIndex++;
61
+ await sleep(250, signal);
62
+ let content = "";
63
+ for (const word of reply.split(/(?<= )/)) {
64
+ await sleep(25, signal);
65
+ content += word;
66
+ handlers.onContent(word);
67
+ }
68
+ return {
69
+ content,
70
+ messageId: "",
71
+ toolCalls: [],
72
+ finishReason: "stop",
73
+ model: req.model,
74
+ reasoning,
75
+ responsesOutput: []
76
+ };
77
+ })
78
+ };
79
+ }
80
+ //#endregion
81
+ export { createMockChatAdapter };
@@ -0,0 +1,33 @@
1
+ import { ChatAdapterError } from "../types.js";
2
+ import { errorMessageFromResponse } from "./errors.js";
3
+ import { buildResponsesRequestBody, parseResponsesSSELine } from "./responses.js";
4
+ import { processSSEStream } from "./sse.js";
5
+ //#region src/ai/adapter/openai/createOpenAIStream.ts
6
+ function createOpenAICompatibleStream(config) {
7
+ return async (req, handlers, signal) => {
8
+ const useResponses = req.useResponses === true && !!config.responsesUrl;
9
+ const url = useResponses ? config.responsesUrl : config.completionsUrl;
10
+ const body = useResponses ? buildResponsesRequestBody(req.model, req.messages) : {
11
+ model: req.model,
12
+ messages: req.messages,
13
+ stream: true
14
+ };
15
+ const res = await (config.fetch ?? globalThis.fetch.bind(globalThis))(url, {
16
+ method: "POST",
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ Accept: "text/event-stream",
20
+ ...config.buildHeaders?.(req)
21
+ },
22
+ ...config.credentials ? { credentials: config.credentials } : {},
23
+ body: JSON.stringify(body),
24
+ ...signal ? { signal } : {}
25
+ });
26
+ if (!res.ok) throw new ChatAdapterError(await errorMessageFromResponse(res), { status: res.status });
27
+ const reader = res.body?.getReader();
28
+ if (!reader) throw new ChatAdapterError("No response body");
29
+ return processSSEStream(reader, handlers.onContent, void 0, void 0, handlers.onReasoning, handlers.onMessageId, useResponses ? parseResponsesSSELine : void 0);
30
+ };
31
+ }
32
+ //#endregion
33
+ export { createOpenAICompatibleStream };
@@ -0,0 +1,37 @@
1
+ //#region src/ai/adapter/openai/errors.ts
2
+ function apiErrorMessage(errorData, fallback) {
3
+ if (typeof errorData !== "object" || errorData === null) return fallback;
4
+ const data = errorData;
5
+ const candidate = data["message"] ?? data["error"] ?? data["detail"];
6
+ if (typeof candidate === "string" && candidate.trim()) return candidate;
7
+ if (candidate && typeof candidate === "object") {
8
+ const nested = candidate["message"];
9
+ if (typeof nested === "string" && nested.trim()) return nested;
10
+ try {
11
+ return JSON.stringify(candidate);
12
+ } catch {
13
+ return fallback;
14
+ }
15
+ }
16
+ return fallback;
17
+ }
18
+ function statusFallbackMessage(status) {
19
+ if (status === 401) return "Your session has expired. Please refresh the page and try again.";
20
+ if (status === 403) return "You do not have permission to use this AI provider.";
21
+ if (status === 404) return "The AI provider or model was not found. Please select a different provider.";
22
+ if (status === 429) return "Too many requests. Please wait a moment and try again.";
23
+ if (status >= 500) return "The AI service is temporarily unavailable. Please try again later.";
24
+ return "Failed to send message";
25
+ }
26
+ async function errorMessageFromResponse(res) {
27
+ let message = statusFallbackMessage(res.status);
28
+ try {
29
+ const contentType = res.headers.get("content-type");
30
+ const text = await res.text();
31
+ if (contentType?.includes("application/json") && text) message = apiErrorMessage(JSON.parse(text), message);
32
+ else if (text && res.status < 500) message = text.substring(0, 200);
33
+ } catch {}
34
+ return message;
35
+ }
36
+ //#endregion
37
+ export { apiErrorMessage, errorMessageFromResponse, statusFallbackMessage };
@@ -1,334 +1,6 @@
1
- import { t as ChatAdapterError } from "../../../chunks/types-Yy0NrerF.js";
2
- import { t as createMemoryConversationStore } from "../../../chunks/memoryStore-CC91S5LP.js";
3
- //#region src/ai/adapter/openai/errors.ts
4
- function apiErrorMessage(errorData, fallback) {
5
- if (typeof errorData !== "object" || errorData === null) return fallback;
6
- const data = errorData;
7
- const candidate = data["message"] ?? data["error"] ?? data["detail"];
8
- if (typeof candidate === "string" && candidate.trim()) return candidate;
9
- if (candidate && typeof candidate === "object") {
10
- const nested = candidate["message"];
11
- if (typeof nested === "string" && nested.trim()) return nested;
12
- try {
13
- return JSON.stringify(candidate);
14
- } catch {
15
- return fallback;
16
- }
17
- }
18
- return fallback;
19
- }
20
- function statusFallbackMessage(status) {
21
- if (status === 401) return "Your session has expired. Please refresh the page and try again.";
22
- if (status === 403) return "You do not have permission to use this AI provider.";
23
- if (status === 404) return "The AI provider or model was not found. Please select a different provider.";
24
- if (status === 429) return "Too many requests. Please wait a moment and try again.";
25
- if (status >= 500) return "The AI service is temporarily unavailable. Please try again later.";
26
- return "Failed to send message";
27
- }
28
- async function errorMessageFromResponse(res) {
29
- let message = statusFallbackMessage(res.status);
30
- try {
31
- const contentType = res.headers.get("content-type");
32
- const text = await res.text();
33
- if (contentType?.includes("application/json") && text) message = apiErrorMessage(JSON.parse(text), message);
34
- else if (text && res.status < 500) message = text.substring(0, 200);
35
- } catch {}
36
- return message;
37
- }
38
- //#endregion
39
- //#region src/ai/adapter/openai/responses.ts
40
- function contentString(content) {
41
- return typeof content === "string" ? content : "";
42
- }
43
- function buildResponsesRequestBody(model, messages) {
44
- const input = [];
45
- let attachmentIds;
46
- for (const m of messages) switch (m.role) {
47
- case "system":
48
- input.push({
49
- type: "message",
50
- role: "system",
51
- content: contentString(m.content)
52
- });
53
- break;
54
- case "user":
55
- if (m.attachment_ids?.length) attachmentIds = m.attachment_ids;
56
- input.push({
57
- type: "message",
58
- role: "user",
59
- content: contentString(m.content)
60
- });
61
- break;
62
- case "assistant": {
63
- const replayed = Array.isArray(m.responses_output) ? replayableOutput(m.responses_output) : [];
64
- if (replayed.length > 0) {
65
- for (const item of replayed) input.push(item);
66
- break;
67
- }
68
- const text = contentString(m.content);
69
- const toolCalls = m.tool_calls ?? [];
70
- if (text || toolCalls.length === 0) input.push({
71
- type: "message",
72
- role: "assistant",
73
- content: text
74
- });
75
- for (const tc of toolCalls) input.push({
76
- type: "function_call",
77
- call_id: tc.id ?? "",
78
- name: tc.function.name,
79
- arguments: tc.function.arguments
80
- });
81
- break;
82
- }
83
- case "tool": input.push({
84
- type: "function_call_output",
85
- call_id: m.tool_call_id ?? "",
86
- output: contentString(m.content)
87
- });
88
- }
89
- const body = {
90
- model,
91
- input,
92
- stream: true,
93
- store: false,
94
- include: ["reasoning.encrypted_content"]
95
- };
96
- if (attachmentIds?.length) body["attachment_ids"] = attachmentIds;
97
- return body;
98
- }
99
- var REPLAYABLE_ITEM_TYPES = /* @__PURE__ */ new Set([
100
- "reasoning",
101
- "message",
102
- "function_call"
103
- ]);
104
- function replayableOutput(output) {
105
- if (!output?.length) return [];
106
- return output.filter((item) => {
107
- if (typeof item !== "object" || item === null) return false;
108
- const itemType = item.type;
109
- return typeof itemType === "string" && REPLAYABLE_ITEM_TYPES.has(itemType);
110
- });
111
- }
112
- function parseResponsesSSELine(line) {
113
- if (!line.startsWith("data: ")) return null;
114
- const data = line.slice(6);
115
- if (data === "[DONE]" || data === "\"[DONE]\"") return null;
116
- let event;
117
- try {
118
- event = JSON.parse(data);
119
- } catch {
120
- return null;
121
- }
122
- if (event.error) return { error: event.error.message || "Unknown error" };
123
- const model = event.response?.model;
124
- switch (event.type) {
125
- case "pw.message_id": return event.messageId ? { messageId: event.messageId } : null;
126
- case "response.output_text.delta":
127
- case "response.refusal.delta": return typeof event.delta === "string" ? { content: event.delta } : null;
128
- case "response.reasoning_summary_text.delta": return typeof event.delta === "string" ? { reasoningContent: event.delta } : null;
129
- case "response.created": return model ? { model } : null;
130
- case "response.completed": return {
131
- ...model && { model },
132
- finishReason: "stop",
133
- responsesOutput: replayableOutput(event.response?.output)
134
- };
135
- case "response.incomplete": return {
136
- ...model && { model },
137
- finishReason: "length",
138
- responsesOutput: replayableOutput(event.response?.output)
139
- };
140
- case "response.failed": return { error: event.response?.error?.message || "Response failed" };
141
- case "error": return { error: event.message || "Stream error" };
142
- default: return null;
143
- }
144
- }
145
- //#endregion
146
- //#region src/ai/adapter/openai/sse.ts
147
- function parseSSELine(line) {
148
- if (!line.startsWith("data: ")) return null;
149
- const data = line.slice(6);
150
- if (data === "[DONE]" || data === "\"[DONE]\"") return null;
151
- try {
152
- const parsed = JSON.parse(data);
153
- if (parsed.error) return { error: parsed.error.message || "Unknown error" };
154
- const chunk = parsed;
155
- const choice = chunk.choices?.[0];
156
- return {
157
- id: chunk.id,
158
- messageId: chunk.messageId,
159
- content: choice?.delta?.content,
160
- reasoningContent: choice?.delta?.reasoning_content,
161
- toolCalls: choice?.delta?.tool_calls ?? void 0,
162
- finishReason: choice?.finish_reason,
163
- model: chunk.model
164
- };
165
- } catch {
166
- return null;
167
- }
168
- }
169
- /** Process an SSE stream from a ReadableStream reader */
170
- async function processSSEStream(reader, onContent, onToolCall, onError, onReasoning, onMessageId, parseLine = parseSSELine) {
171
- const decoder = new TextDecoder();
172
- let assistantContent = "";
173
- let assistantReasoning = "";
174
- let assistantMessageId = "";
175
- let finishReason = null;
176
- let resolvedModel = null;
177
- let responsesOutput = [];
178
- const toolCallsMap = /* @__PURE__ */ new Map();
179
- let lineBuffer = "";
180
- while (true) {
181
- const result = await reader.read();
182
- if (result.done) break;
183
- const chunk = decoder.decode(result.value, { stream: true });
184
- const lines = (lineBuffer + chunk).split("\n");
185
- lineBuffer = lines.pop() || "";
186
- for (const line of lines) {
187
- const parsed = parseLine(line);
188
- if (parsed) {
189
- if (parsed.error) {
190
- onError?.(parsed.error);
191
- throw new Error(parsed.error);
192
- }
193
- if (parsed.messageId && !assistantMessageId) {
194
- assistantMessageId = parsed.messageId;
195
- onMessageId?.(parsed.messageId);
196
- } else if (parsed.id && !assistantMessageId) assistantMessageId = parsed.id;
197
- if (parsed.model && !resolvedModel) resolvedModel = parsed.model;
198
- if (parsed.content) {
199
- assistantContent += parsed.content;
200
- onContent(parsed.content);
201
- }
202
- if (parsed.reasoningContent) {
203
- assistantReasoning += parsed.reasoningContent;
204
- onReasoning?.(parsed.reasoningContent);
205
- }
206
- if (parsed.finishReason) finishReason = parsed.finishReason;
207
- if (parsed.responsesOutput?.length) responsesOutput = parsed.responsesOutput;
208
- if (parsed.toolCalls) for (const tc of parsed.toolCalls) {
209
- if (tc.index === void 0) continue;
210
- const existing = toolCallsMap.get(tc.index);
211
- if (existing) {
212
- if (tc.function?.arguments) existing.arguments += tc.function.arguments;
213
- } else toolCallsMap.set(tc.index, {
214
- id: tc.id || "",
215
- type: tc.type || "function",
216
- name: tc.function?.name || "",
217
- arguments: tc.function?.arguments || ""
218
- });
219
- }
220
- }
221
- }
222
- }
223
- if (lineBuffer.trim()) {
224
- const parsed = parseLine(lineBuffer);
225
- if (parsed) {
226
- if (parsed.content) {
227
- assistantContent += parsed.content;
228
- onContent(parsed.content);
229
- }
230
- if (parsed.reasoningContent) {
231
- assistantReasoning += parsed.reasoningContent;
232
- onReasoning?.(parsed.reasoningContent);
233
- }
234
- if (parsed.finishReason) finishReason = parsed.finishReason;
235
- }
236
- }
237
- const toolCalls = Array.from(toolCallsMap.values()).map((tc) => ({
238
- id: tc.id,
239
- type: tc.type,
240
- function: {
241
- name: tc.name,
242
- arguments: tc.arguments
243
- }
244
- }));
245
- if (toolCalls.length > 0 && onToolCall) onToolCall(toolCalls);
246
- return {
247
- content: assistantContent,
248
- messageId: assistantMessageId,
249
- toolCalls,
250
- finishReason,
251
- model: resolvedModel,
252
- reasoning: assistantReasoning,
253
- responsesOutput
254
- };
255
- }
256
- //#endregion
257
- //#region src/ai/adapter/openai/createOpenAIStream.ts
258
- function createOpenAICompatibleStream(config) {
259
- return async (req, handlers, signal) => {
260
- const useResponses = req.useResponses === true && !!config.responsesUrl;
261
- const url = useResponses ? config.responsesUrl : config.completionsUrl;
262
- const body = useResponses ? buildResponsesRequestBody(req.model, req.messages) : {
263
- model: req.model,
264
- messages: req.messages,
265
- stream: true
266
- };
267
- const res = await (config.fetch ?? globalThis.fetch.bind(globalThis))(url, {
268
- method: "POST",
269
- headers: {
270
- "Content-Type": "application/json",
271
- Accept: "text/event-stream",
272
- ...config.buildHeaders?.(req)
273
- },
274
- ...config.credentials ? { credentials: config.credentials } : {},
275
- body: JSON.stringify(body),
276
- ...signal ? { signal } : {}
277
- });
278
- if (!res.ok) throw new ChatAdapterError(await errorMessageFromResponse(res), { status: res.status });
279
- const reader = res.body?.getReader();
280
- if (!reader) throw new ChatAdapterError("No response body");
281
- return processSSEStream(reader, handlers.onContent, void 0, void 0, handlers.onReasoning, handlers.onMessageId, useResponses ? parseResponsesSSELine : void 0);
282
- };
283
- }
284
- //#endregion
285
- //#region src/ai/adapter/openai/openaiAdapter.ts
286
- function providerLabelFor(baseUrl) {
287
- try {
288
- return new URL(baseUrl).host;
289
- } catch {
290
- return "OpenAI compatible";
291
- }
292
- }
293
- function staticModel(id, provider) {
294
- return {
295
- id,
296
- object: "model",
297
- created: 0,
298
- owned_by: "openai-compatible",
299
- provider,
300
- tool_calling_mode: "none"
301
- };
302
- }
303
- function createOpenAIChatAdapter(options) {
304
- const baseUrl = options.baseUrl.replace(/\/$/, "");
305
- const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
306
- const headers = options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {};
307
- const store = createMemoryConversationStore({ storageKey: options.storageKey ?? "ai-chat-openai-conversations" });
308
- return {
309
- conversations: store.conversations,
310
- models: { async list() {
311
- const providerLabel = providerLabelFor(baseUrl);
312
- if (options.models?.length) return {
313
- models: options.models.map((id) => staticModel(id, providerLabel)),
314
- unreachableSessions: []
315
- };
316
- const res = await doFetch(`${baseUrl}/models`, { headers });
317
- if (!res.ok) throw new ChatAdapterError(await errorMessageFromResponse(res), { status: res.status });
318
- return {
319
- models: ((await res.json()).data ?? []).map((m) => ({
320
- ...staticModel(m.id, providerLabel),
321
- ...m
322
- })),
323
- unreachableSessions: []
324
- };
325
- } },
326
- streamCompletion: store.recordingStream(createOpenAICompatibleStream({
327
- completionsUrl: `${baseUrl}/chat/completions`,
328
- buildHeaders: () => headers,
329
- ...options.fetch ? { fetch: options.fetch } : {}
330
- }))
331
- };
332
- }
333
- //#endregion
1
+ import { apiErrorMessage, errorMessageFromResponse, statusFallbackMessage } from "./errors.js";
2
+ import { buildResponsesRequestBody, parseResponsesSSELine } from "./responses.js";
3
+ import { parseSSELine, processSSEStream } from "./sse.js";
4
+ import { createOpenAICompatibleStream } from "./createOpenAIStream.js";
5
+ import { createOpenAIChatAdapter } from "./openaiAdapter.js";
334
6
  export { apiErrorMessage, buildResponsesRequestBody, createOpenAIChatAdapter, createOpenAICompatibleStream, errorMessageFromResponse, parseResponsesSSELine, parseSSELine, processSSEStream, statusFallbackMessage };
@@ -0,0 +1,54 @@
1
+ import { ChatAdapterError } from "../types.js";
2
+ import { errorMessageFromResponse } from "./errors.js";
3
+ import { createOpenAICompatibleStream } from "./createOpenAIStream.js";
4
+ import { createMemoryConversationStore } from "../memoryStore.js";
5
+ //#region src/ai/adapter/openai/openaiAdapter.ts
6
+ function providerLabelFor(baseUrl) {
7
+ try {
8
+ return new URL(baseUrl).host;
9
+ } catch {
10
+ return "OpenAI compatible";
11
+ }
12
+ }
13
+ function staticModel(id, provider) {
14
+ return {
15
+ id,
16
+ object: "model",
17
+ created: 0,
18
+ owned_by: "openai-compatible",
19
+ provider,
20
+ tool_calling_mode: "none"
21
+ };
22
+ }
23
+ function createOpenAIChatAdapter(options) {
24
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
25
+ const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
26
+ const headers = options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {};
27
+ const store = createMemoryConversationStore({ storageKey: options.storageKey ?? "ai-chat-openai-conversations" });
28
+ return {
29
+ conversations: store.conversations,
30
+ models: { async list() {
31
+ const providerLabel = providerLabelFor(baseUrl);
32
+ if (options.models?.length) return {
33
+ models: options.models.map((id) => staticModel(id, providerLabel)),
34
+ unreachableSessions: []
35
+ };
36
+ const res = await doFetch(`${baseUrl}/models`, { headers });
37
+ if (!res.ok) throw new ChatAdapterError(await errorMessageFromResponse(res), { status: res.status });
38
+ return {
39
+ models: ((await res.json()).data ?? []).map((m) => ({
40
+ ...staticModel(m.id, providerLabel),
41
+ ...m
42
+ })),
43
+ unreachableSessions: []
44
+ };
45
+ } },
46
+ streamCompletion: store.recordingStream(createOpenAICompatibleStream({
47
+ completionsUrl: `${baseUrl}/chat/completions`,
48
+ buildHeaders: () => headers,
49
+ ...options.fetch ? { fetch: options.fetch } : {}
50
+ }))
51
+ };
52
+ }
53
+ //#endregion
54
+ export { createOpenAIChatAdapter };