ruflo 3.5.2 → 3.5.4

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 (521) hide show
  1. package/dist/rvf.manifest.json +295 -0
  2. package/package.json +16 -2
  3. package/src/chat-ui/Dockerfile +25 -0
  4. package/src/chat-ui/patch-mcp-url-safety.sh +28 -0
  5. package/src/chat-ui/static/chatui/icon-144x144.png +0 -0
  6. package/src/chat-ui/static/chatui/omni-welcome.gif +0 -0
  7. package/src/config/config.example.json +76 -0
  8. package/src/mcp-bridge/Dockerfile +45 -0
  9. package/src/mcp-bridge/index.js +1668 -0
  10. package/src/mcp-bridge/mcp-stdio-kernel.js +159 -0
  11. package/src/mcp-bridge/package.json +17 -0
  12. package/src/mcp-bridge/test-harness.js +470 -0
  13. package/src/nginx/Dockerfile +10 -0
  14. package/src/nginx/nginx.conf +67 -0
  15. package/src/nginx/static/favicon-dark.svg +4 -0
  16. package/src/nginx/static/favicon.svg +4 -0
  17. package/src/nginx/static/icon.svg +5 -0
  18. package/src/nginx/static/logo.svg +9 -0
  19. package/src/nginx/static/manifest.json +22 -0
  20. package/src/nginx/static/welcome.js +184 -0
  21. package/src/ruvocal/.claude/skills/add-model-descriptions/SKILL.md +73 -0
  22. package/src/ruvocal/.devcontainer/Dockerfile +9 -0
  23. package/src/ruvocal/.devcontainer/devcontainer.json +36 -0
  24. package/src/ruvocal/.dockerignore +13 -0
  25. package/src/ruvocal/.env +194 -0
  26. package/src/ruvocal/.env.ci +1 -0
  27. package/src/ruvocal/.eslintignore +13 -0
  28. package/src/ruvocal/.eslintrc.cjs +45 -0
  29. package/src/ruvocal/.github/ISSUE_TEMPLATE/bug-report--chat-ui-.md +43 -0
  30. package/src/ruvocal/.github/ISSUE_TEMPLATE/config-support.md +9 -0
  31. package/src/ruvocal/.github/ISSUE_TEMPLATE/feature-request--chat-ui-.md +17 -0
  32. package/src/ruvocal/.github/ISSUE_TEMPLATE/huggingchat.md +11 -0
  33. package/src/ruvocal/.github/release.yml +16 -0
  34. package/src/ruvocal/.github/workflows/build-docs.yml +18 -0
  35. package/src/ruvocal/.github/workflows/build-image.yml +142 -0
  36. package/src/ruvocal/.github/workflows/build-pr-docs.yml +20 -0
  37. package/src/ruvocal/.github/workflows/deploy-dev.yml +63 -0
  38. package/src/ruvocal/.github/workflows/deploy-prod.yml +78 -0
  39. package/src/ruvocal/.github/workflows/lint-and-test.yml +84 -0
  40. package/src/ruvocal/.github/workflows/slugify.yaml +72 -0
  41. package/src/ruvocal/.github/workflows/trufflehog.yml +17 -0
  42. package/src/ruvocal/.github/workflows/upload-pr-documentation.yml +16 -0
  43. package/src/ruvocal/.husky/lint-stage-config.js +4 -0
  44. package/src/ruvocal/.husky/pre-commit +2 -0
  45. package/src/ruvocal/.prettierignore +14 -0
  46. package/src/ruvocal/.prettierrc +7 -0
  47. package/src/ruvocal/.vscode/launch.json +11 -0
  48. package/src/ruvocal/.vscode/settings.json +14 -0
  49. package/src/ruvocal/CLAUDE.md +126 -0
  50. package/src/ruvocal/Dockerfile +93 -0
  51. package/src/ruvocal/LICENSE +203 -0
  52. package/src/ruvocal/PRIVACY.md +41 -0
  53. package/src/ruvocal/README.md +190 -0
  54. package/src/ruvocal/chart/Chart.yaml +5 -0
  55. package/src/ruvocal/chart/env/dev.yaml +260 -0
  56. package/src/ruvocal/chart/env/prod.yaml +273 -0
  57. package/src/ruvocal/chart/templates/_helpers.tpl +22 -0
  58. package/src/ruvocal/chart/templates/config.yaml +10 -0
  59. package/src/ruvocal/chart/templates/deployment.yaml +81 -0
  60. package/src/ruvocal/chart/templates/hpa.yaml +45 -0
  61. package/src/ruvocal/chart/templates/infisical.yaml +24 -0
  62. package/src/ruvocal/chart/templates/ingress-internal.yaml +32 -0
  63. package/src/ruvocal/chart/templates/ingress.yaml +32 -0
  64. package/src/ruvocal/chart/templates/network-policy.yaml +36 -0
  65. package/src/ruvocal/chart/templates/service-account.yaml +13 -0
  66. package/src/ruvocal/chart/templates/service-monitor.yaml +17 -0
  67. package/src/ruvocal/chart/templates/service.yaml +21 -0
  68. package/src/ruvocal/chart/values.yaml +73 -0
  69. package/src/ruvocal/docker-compose.yml +21 -0
  70. package/src/ruvocal/docs/adr/ADR-029-HUGGINGFACE-CHAT-UI-CLOUD-RUN.md +1236 -0
  71. package/src/ruvocal/docs/adr/ADR-033-RUVECTOR-RUFLO-MCP-INTEGRATION.md +111 -0
  72. package/src/ruvocal/docs/adr/ADR-034-OPTIONAL-MCP-BACKENDS.md +117 -0
  73. package/src/ruvocal/docs/adr/ADR-035-MCP-TOOL-GROUPS.md +186 -0
  74. package/src/ruvocal/docs/adr/ADR-037-AUTOPILOT-CHAT-MODE.md +1500 -0
  75. package/src/ruvocal/docs/adr/ADR-038-RUVOCAL-FORK.md +286 -0
  76. package/src/ruvocal/docs/source/_toctree.yml +30 -0
  77. package/src/ruvocal/docs/source/configuration/common-issues.md +38 -0
  78. package/src/ruvocal/docs/source/configuration/llm-router.md +105 -0
  79. package/src/ruvocal/docs/source/configuration/mcp-tools.md +84 -0
  80. package/src/ruvocal/docs/source/configuration/metrics.md +9 -0
  81. package/src/ruvocal/docs/source/configuration/open-id.md +57 -0
  82. package/src/ruvocal/docs/source/configuration/overview.md +89 -0
  83. package/src/ruvocal/docs/source/configuration/theming.md +20 -0
  84. package/src/ruvocal/docs/source/developing/architecture.md +48 -0
  85. package/src/ruvocal/docs/source/index.md +53 -0
  86. package/src/ruvocal/docs/source/installation/docker.md +43 -0
  87. package/src/ruvocal/docs/source/installation/helm.md +43 -0
  88. package/src/ruvocal/docs/source/installation/local.md +62 -0
  89. package/src/ruvocal/entrypoint.sh +19 -0
  90. package/src/ruvocal/mcp-bridge/.claude-flow/agents/store.json +27 -0
  91. package/src/ruvocal/mcp-bridge/.claude-flow/daemon-state.json +130 -0
  92. package/src/ruvocal/mcp-bridge/.claude-flow/daemon.log +0 -0
  93. package/src/ruvocal/mcp-bridge/.claude-flow/daemon.pid +1 -0
  94. package/src/ruvocal/mcp-bridge/.claude-flow/tasks/store.json +21 -0
  95. package/src/ruvocal/mcp-bridge/.swarm/hnsw.index +0 -0
  96. package/src/ruvocal/mcp-bridge/.swarm/hnsw.metadata.json +1 -0
  97. package/src/ruvocal/mcp-bridge/.swarm/memory.db +0 -0
  98. package/src/ruvocal/mcp-bridge/.swarm/model-router-state.json +14 -0
  99. package/src/ruvocal/mcp-bridge/.swarm/schema.sql +305 -0
  100. package/src/ruvocal/mcp-bridge/Dockerfile +45 -0
  101. package/src/ruvocal/mcp-bridge/cloudbuild.yaml +49 -0
  102. package/src/ruvocal/mcp-bridge/index.js +1864 -0
  103. package/src/ruvocal/mcp-bridge/mcp-stdio-kernel.js +159 -0
  104. package/src/ruvocal/mcp-bridge/package-lock.json +762 -0
  105. package/src/ruvocal/mcp-bridge/package.json +17 -0
  106. package/src/ruvocal/mcp-bridge/test-harness.js +470 -0
  107. package/src/ruvocal/models/add-your-models-here.txt +1 -0
  108. package/src/ruvocal/package-lock.json +11741 -0
  109. package/src/ruvocal/package.json +121 -0
  110. package/src/ruvocal/postcss.config.js +6 -0
  111. package/src/ruvocal/rvf.manifest.json +204 -0
  112. package/src/ruvocal/scripts/config.ts +64 -0
  113. package/src/ruvocal/scripts/generate-welcome.mjs +181 -0
  114. package/src/ruvocal/scripts/populate.ts +288 -0
  115. package/src/ruvocal/scripts/samples.txt +194 -0
  116. package/src/ruvocal/scripts/setups/vitest-setup-client.ts +0 -0
  117. package/src/ruvocal/scripts/setups/vitest-setup-server.ts +44 -0
  118. package/src/ruvocal/scripts/updateLocalEnv.ts +48 -0
  119. package/src/ruvocal/src/ambient.d.ts +7 -0
  120. package/src/ruvocal/src/app.d.ts +29 -0
  121. package/src/ruvocal/src/app.html +53 -0
  122. package/src/ruvocal/src/hooks.server.ts +32 -0
  123. package/src/ruvocal/src/hooks.ts +6 -0
  124. package/src/ruvocal/src/lib/APIClient.ts +148 -0
  125. package/src/ruvocal/src/lib/actions/clickOutside.ts +18 -0
  126. package/src/ruvocal/src/lib/actions/snapScrollToBottom.ts +346 -0
  127. package/src/ruvocal/src/lib/buildPrompt.ts +33 -0
  128. package/src/ruvocal/src/lib/components/AnnouncementBanner.svelte +20 -0
  129. package/src/ruvocal/src/lib/components/BackgroundGenerationPoller.svelte +168 -0
  130. package/src/ruvocal/src/lib/components/CodeBlock.svelte +73 -0
  131. package/src/ruvocal/src/lib/components/CopyToClipBoardBtn.svelte +92 -0
  132. package/src/ruvocal/src/lib/components/DeleteConversationModal.svelte +75 -0
  133. package/src/ruvocal/src/lib/components/EditConversationModal.svelte +100 -0
  134. package/src/ruvocal/src/lib/components/ExpandNavigation.svelte +22 -0
  135. package/src/ruvocal/src/lib/components/HoverTooltip.svelte +44 -0
  136. package/src/ruvocal/src/lib/components/HtmlPreviewModal.svelte +143 -0
  137. package/src/ruvocal/src/lib/components/InfiniteScroll.svelte +50 -0
  138. package/src/ruvocal/src/lib/components/MobileNav.svelte +300 -0
  139. package/src/ruvocal/src/lib/components/Modal.svelte +115 -0
  140. package/src/ruvocal/src/lib/components/ModelCardMetadata.svelte +71 -0
  141. package/src/ruvocal/src/lib/components/NavConversationItem.svelte +151 -0
  142. package/src/ruvocal/src/lib/components/NavMenu.svelte +295 -0
  143. package/src/ruvocal/src/lib/components/Pagination.svelte +97 -0
  144. package/src/ruvocal/src/lib/components/PaginationArrow.svelte +27 -0
  145. package/src/ruvocal/src/lib/components/Portal.svelte +24 -0
  146. package/src/ruvocal/src/lib/components/RetryBtn.svelte +18 -0
  147. package/src/ruvocal/src/lib/components/RuFloUniverse.svelte +185 -0
  148. package/src/ruvocal/src/lib/components/ScrollToBottomBtn.svelte +47 -0
  149. package/src/ruvocal/src/lib/components/ScrollToPreviousBtn.svelte +77 -0
  150. package/src/ruvocal/src/lib/components/ShareConversationModal.svelte +182 -0
  151. package/src/ruvocal/src/lib/components/StopGeneratingBtn.svelte +69 -0
  152. package/src/ruvocal/src/lib/components/SubscribeModal.svelte +87 -0
  153. package/src/ruvocal/src/lib/components/Switch.svelte +36 -0
  154. package/src/ruvocal/src/lib/components/SystemPromptModal.svelte +44 -0
  155. package/src/ruvocal/src/lib/components/Toast.svelte +27 -0
  156. package/src/ruvocal/src/lib/components/Tooltip.svelte +30 -0
  157. package/src/ruvocal/src/lib/components/WelcomeModal.svelte +46 -0
  158. package/src/ruvocal/src/lib/components/chat/Alternatives.svelte +77 -0
  159. package/src/ruvocal/src/lib/components/chat/BlockWrapper.svelte +72 -0
  160. package/src/ruvocal/src/lib/components/chat/ChatInput.svelte +490 -0
  161. package/src/ruvocal/src/lib/components/chat/ChatIntroduction.svelte +123 -0
  162. package/src/ruvocal/src/lib/components/chat/ChatMessage.svelte +548 -0
  163. package/src/ruvocal/src/lib/components/chat/ChatWindow.svelte +939 -0
  164. package/src/ruvocal/src/lib/components/chat/FileDropzone.svelte +92 -0
  165. package/src/ruvocal/src/lib/components/chat/ImageLightbox.svelte +66 -0
  166. package/src/ruvocal/src/lib/components/chat/MarkdownBlock.svelte +23 -0
  167. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte +69 -0
  168. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte.test.ts +58 -0
  169. package/src/ruvocal/src/lib/components/chat/MessageAvatar.svelte +103 -0
  170. package/src/ruvocal/src/lib/components/chat/ModelSwitch.svelte +64 -0
  171. package/src/ruvocal/src/lib/components/chat/OpenReasoningResults.svelte +81 -0
  172. package/src/ruvocal/src/lib/components/chat/TaskGroup.svelte +88 -0
  173. package/src/ruvocal/src/lib/components/chat/ToolUpdate.svelte +273 -0
  174. package/src/ruvocal/src/lib/components/chat/UploadedFile.svelte +253 -0
  175. package/src/ruvocal/src/lib/components/chat/UrlFetchModal.svelte +203 -0
  176. package/src/ruvocal/src/lib/components/chat/VoiceRecorder.svelte +214 -0
  177. package/src/ruvocal/src/lib/components/icons/IconBurger.svelte +20 -0
  178. package/src/ruvocal/src/lib/components/icons/IconCheap.svelte +20 -0
  179. package/src/ruvocal/src/lib/components/icons/IconChevron.svelte +24 -0
  180. package/src/ruvocal/src/lib/components/icons/IconDazzled.svelte +40 -0
  181. package/src/ruvocal/src/lib/components/icons/IconFast.svelte +20 -0
  182. package/src/ruvocal/src/lib/components/icons/IconLoading.svelte +22 -0
  183. package/src/ruvocal/src/lib/components/icons/IconMCP.svelte +28 -0
  184. package/src/ruvocal/src/lib/components/icons/IconMoon.svelte +21 -0
  185. package/src/ruvocal/src/lib/components/icons/IconNew.svelte +20 -0
  186. package/src/ruvocal/src/lib/components/icons/IconOmni.svelte +90 -0
  187. package/src/ruvocal/src/lib/components/icons/IconPaperclip.svelte +24 -0
  188. package/src/ruvocal/src/lib/components/icons/IconPro.svelte +37 -0
  189. package/src/ruvocal/src/lib/components/icons/IconShare.svelte +21 -0
  190. package/src/ruvocal/src/lib/components/icons/IconSun.svelte +93 -0
  191. package/src/ruvocal/src/lib/components/icons/Logo.svelte +68 -0
  192. package/src/ruvocal/src/lib/components/icons/LogoHuggingFaceBorderless.svelte +54 -0
  193. package/src/ruvocal/src/lib/components/mcp/AddServerForm.svelte +250 -0
  194. package/src/ruvocal/src/lib/components/mcp/MCPServerManager.svelte +185 -0
  195. package/src/ruvocal/src/lib/components/mcp/ServerCard.svelte +203 -0
  196. package/src/ruvocal/src/lib/components/players/AudioPlayer.svelte +82 -0
  197. package/src/ruvocal/src/lib/components/voice/AudioWaveform.svelte +96 -0
  198. package/src/ruvocal/src/lib/constants/mcpExamples.ts +135 -0
  199. package/src/ruvocal/src/lib/constants/mime.ts +11 -0
  200. package/src/ruvocal/src/lib/constants/pagination.ts +1 -0
  201. package/src/ruvocal/src/lib/constants/publicSepToken.ts +1 -0
  202. package/src/ruvocal/src/lib/constants/routerExamples.ts +209 -0
  203. package/src/ruvocal/src/lib/createShareLink.ts +27 -0
  204. package/src/ruvocal/src/lib/jobs/refresh-conversation-stats.ts +297 -0
  205. package/src/ruvocal/src/lib/migrations/lock.ts +56 -0
  206. package/src/ruvocal/src/lib/migrations/migrations.spec.ts +74 -0
  207. package/src/ruvocal/src/lib/migrations/migrations.ts +109 -0
  208. package/src/ruvocal/src/lib/migrations/routines/01-update-search-assistants.ts +50 -0
  209. package/src/ruvocal/src/lib/migrations/routines/02-update-assistants-models.ts +48 -0
  210. package/src/ruvocal/src/lib/migrations/routines/04-update-message-updates.ts +151 -0
  211. package/src/ruvocal/src/lib/migrations/routines/05-update-message-files.ts +56 -0
  212. package/src/ruvocal/src/lib/migrations/routines/06-trim-message-updates.ts +56 -0
  213. package/src/ruvocal/src/lib/migrations/routines/08-update-featured-to-review.ts +32 -0
  214. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.spec.ts +214 -0
  215. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.ts +88 -0
  216. package/src/ruvocal/src/lib/migrations/routines/10-update-reports-assistantid.ts +29 -0
  217. package/src/ruvocal/src/lib/migrations/routines/index.ts +15 -0
  218. package/src/ruvocal/src/lib/server/__tests__/conversation-stop-generating.spec.ts +103 -0
  219. package/src/ruvocal/src/lib/server/abortRegistry.ts +57 -0
  220. package/src/ruvocal/src/lib/server/abortedGenerations.ts +43 -0
  221. package/src/ruvocal/src/lib/server/adminToken.ts +62 -0
  222. package/src/ruvocal/src/lib/server/api/__tests__/conversations-id.spec.ts +296 -0
  223. package/src/ruvocal/src/lib/server/api/__tests__/conversations-message.spec.ts +216 -0
  224. package/src/ruvocal/src/lib/server/api/__tests__/conversations.spec.ts +235 -0
  225. package/src/ruvocal/src/lib/server/api/__tests__/misc.spec.ts +72 -0
  226. package/src/ruvocal/src/lib/server/api/__tests__/testHelpers.ts +86 -0
  227. package/src/ruvocal/src/lib/server/api/__tests__/user-reports.spec.ts +78 -0
  228. package/src/ruvocal/src/lib/server/api/__tests__/user.spec.ts +239 -0
  229. package/src/ruvocal/src/lib/server/api/types.ts +37 -0
  230. package/src/ruvocal/src/lib/server/api/utils/requireAuth.ts +22 -0
  231. package/src/ruvocal/src/lib/server/api/utils/resolveConversation.ts +69 -0
  232. package/src/ruvocal/src/lib/server/api/utils/resolveModel.ts +27 -0
  233. package/src/ruvocal/src/lib/server/api/utils/superjsonResponse.ts +15 -0
  234. package/src/ruvocal/src/lib/server/apiToken.ts +11 -0
  235. package/src/ruvocal/src/lib/server/auth.ts +554 -0
  236. package/src/ruvocal/src/lib/server/config.ts +187 -0
  237. package/src/ruvocal/src/lib/server/conversation.ts +83 -0
  238. package/src/ruvocal/src/lib/server/database/__tests__/rvf.spec.ts +709 -0
  239. package/src/ruvocal/src/lib/server/database/postgres.ts +700 -0
  240. package/src/ruvocal/src/lib/server/database/rvf.ts +1078 -0
  241. package/src/ruvocal/src/lib/server/database.ts +145 -0
  242. package/src/ruvocal/src/lib/server/endpoints/document.ts +68 -0
  243. package/src/ruvocal/src/lib/server/endpoints/endpoints.ts +43 -0
  244. package/src/ruvocal/src/lib/server/endpoints/images.ts +211 -0
  245. package/src/ruvocal/src/lib/server/endpoints/openai/endpointOai.ts +266 -0
  246. package/src/ruvocal/src/lib/server/endpoints/openai/openAIChatToTextGenerationStream.ts +212 -0
  247. package/src/ruvocal/src/lib/server/endpoints/openai/openAICompletionToTextGenerationStream.ts +32 -0
  248. package/src/ruvocal/src/lib/server/endpoints/preprocessMessages.ts +61 -0
  249. package/src/ruvocal/src/lib/server/exitHandler.ts +59 -0
  250. package/src/ruvocal/src/lib/server/files/downloadFile.ts +34 -0
  251. package/src/ruvocal/src/lib/server/files/uploadFile.ts +29 -0
  252. package/src/ruvocal/src/lib/server/findRepoRoot.ts +13 -0
  253. package/src/ruvocal/src/lib/server/fonts/Inter-Black.ttf +0 -0
  254. package/src/ruvocal/src/lib/server/fonts/Inter-Bold.ttf +0 -0
  255. package/src/ruvocal/src/lib/server/fonts/Inter-ExtraBold.ttf +0 -0
  256. package/src/ruvocal/src/lib/server/fonts/Inter-ExtraLight.ttf +0 -0
  257. package/src/ruvocal/src/lib/server/fonts/Inter-Light.ttf +0 -0
  258. package/src/ruvocal/src/lib/server/fonts/Inter-Medium.ttf +0 -0
  259. package/src/ruvocal/src/lib/server/fonts/Inter-Regular.ttf +0 -0
  260. package/src/ruvocal/src/lib/server/fonts/Inter-SemiBold.ttf +0 -0
  261. package/src/ruvocal/src/lib/server/fonts/Inter-Thin.ttf +0 -0
  262. package/src/ruvocal/src/lib/server/generateFromDefaultEndpoint.ts +46 -0
  263. package/src/ruvocal/src/lib/server/hooks/error.ts +37 -0
  264. package/src/ruvocal/src/lib/server/hooks/fetch.ts +22 -0
  265. package/src/ruvocal/src/lib/server/hooks/handle.ts +250 -0
  266. package/src/ruvocal/src/lib/server/hooks/init.ts +51 -0
  267. package/src/ruvocal/src/lib/server/isURLLocal.spec.ts +31 -0
  268. package/src/ruvocal/src/lib/server/isURLLocal.ts +74 -0
  269. package/src/ruvocal/src/lib/server/logger.ts +42 -0
  270. package/src/ruvocal/src/lib/server/mcp/clientPool.ts +70 -0
  271. package/src/ruvocal/src/lib/server/mcp/hf.ts +32 -0
  272. package/src/ruvocal/src/lib/server/mcp/httpClient.ts +122 -0
  273. package/src/ruvocal/src/lib/server/mcp/registry.ts +76 -0
  274. package/src/ruvocal/src/lib/server/mcp/tools.ts +196 -0
  275. package/src/ruvocal/src/lib/server/metrics.ts +255 -0
  276. package/src/ruvocal/src/lib/server/models.ts +518 -0
  277. package/src/ruvocal/src/lib/server/requestContext.ts +55 -0
  278. package/src/ruvocal/src/lib/server/router/arch.ts +230 -0
  279. package/src/ruvocal/src/lib/server/router/endpoint.ts +316 -0
  280. package/src/ruvocal/src/lib/server/router/multimodal.ts +28 -0
  281. package/src/ruvocal/src/lib/server/router/policy.ts +49 -0
  282. package/src/ruvocal/src/lib/server/router/toolsRoute.ts +51 -0
  283. package/src/ruvocal/src/lib/server/router/types.ts +21 -0
  284. package/src/ruvocal/src/lib/server/sendSlack.ts +23 -0
  285. package/src/ruvocal/src/lib/server/textGeneration/generate.ts +258 -0
  286. package/src/ruvocal/src/lib/server/textGeneration/index.ts +95 -0
  287. package/src/ruvocal/src/lib/server/textGeneration/mcp/fileRefs.ts +155 -0
  288. package/src/ruvocal/src/lib/server/textGeneration/mcp/routerResolution.ts +108 -0
  289. package/src/ruvocal/src/lib/server/textGeneration/mcp/runMcpFlow.ts +822 -0
  290. package/src/ruvocal/src/lib/server/textGeneration/mcp/toolInvocation.ts +349 -0
  291. package/src/ruvocal/src/lib/server/textGeneration/reasoning.ts +23 -0
  292. package/src/ruvocal/src/lib/server/textGeneration/title.ts +83 -0
  293. package/src/ruvocal/src/lib/server/textGeneration/types.ts +26 -0
  294. package/src/ruvocal/src/lib/server/textGeneration/utils/prepareFiles.ts +88 -0
  295. package/src/ruvocal/src/lib/server/textGeneration/utils/routing.ts +21 -0
  296. package/src/ruvocal/src/lib/server/textGeneration/utils/toolPrompt.ts +49 -0
  297. package/src/ruvocal/src/lib/server/urlSafety.ts +72 -0
  298. package/src/ruvocal/src/lib/server/usageLimits.ts +30 -0
  299. package/src/ruvocal/src/lib/stores/autopilotStore.svelte.ts +175 -0
  300. package/src/ruvocal/src/lib/stores/backgroundGenerations.svelte.ts +32 -0
  301. package/src/ruvocal/src/lib/stores/backgroundGenerations.ts +1 -0
  302. package/src/ruvocal/src/lib/stores/errors.ts +9 -0
  303. package/src/ruvocal/src/lib/stores/isAborted.ts +3 -0
  304. package/src/ruvocal/src/lib/stores/isPro.ts +4 -0
  305. package/src/ruvocal/src/lib/stores/loading.ts +3 -0
  306. package/src/ruvocal/src/lib/stores/mcpServers.ts +345 -0
  307. package/src/ruvocal/src/lib/stores/pendingChatInput.ts +3 -0
  308. package/src/ruvocal/src/lib/stores/pendingMessage.ts +9 -0
  309. package/src/ruvocal/src/lib/stores/settings.ts +182 -0
  310. package/src/ruvocal/src/lib/stores/shareModal.ts +13 -0
  311. package/src/ruvocal/src/lib/stores/titleUpdate.ts +8 -0
  312. package/src/ruvocal/src/lib/switchTheme.ts +124 -0
  313. package/src/ruvocal/src/lib/types/AbortedGeneration.ts +8 -0
  314. package/src/ruvocal/src/lib/types/Assistant.ts +31 -0
  315. package/src/ruvocal/src/lib/types/AssistantStats.ts +11 -0
  316. package/src/ruvocal/src/lib/types/ConfigKey.ts +4 -0
  317. package/src/ruvocal/src/lib/types/ConvSidebar.ts +9 -0
  318. package/src/ruvocal/src/lib/types/Conversation.ts +27 -0
  319. package/src/ruvocal/src/lib/types/ConversationStats.ts +13 -0
  320. package/src/ruvocal/src/lib/types/Message.ts +41 -0
  321. package/src/ruvocal/src/lib/types/MessageEvent.ts +10 -0
  322. package/src/ruvocal/src/lib/types/MessageUpdate.ts +139 -0
  323. package/src/ruvocal/src/lib/types/MigrationResult.ts +7 -0
  324. package/src/ruvocal/src/lib/types/Model.ts +23 -0
  325. package/src/ruvocal/src/lib/types/Report.ts +12 -0
  326. package/src/ruvocal/src/lib/types/Review.ts +6 -0
  327. package/src/ruvocal/src/lib/types/Semaphore.ts +19 -0
  328. package/src/ruvocal/src/lib/types/Session.ts +22 -0
  329. package/src/ruvocal/src/lib/types/Settings.ts +86 -0
  330. package/src/ruvocal/src/lib/types/SharedConversation.ts +9 -0
  331. package/src/ruvocal/src/lib/types/Template.ts +6 -0
  332. package/src/ruvocal/src/lib/types/Timestamps.ts +4 -0
  333. package/src/ruvocal/src/lib/types/TokenCache.ts +6 -0
  334. package/src/ruvocal/src/lib/types/Tool.ts +74 -0
  335. package/src/ruvocal/src/lib/types/UrlDependency.ts +5 -0
  336. package/src/ruvocal/src/lib/types/User.ts +14 -0
  337. package/src/ruvocal/src/lib/utils/PublicConfig.svelte.ts +75 -0
  338. package/src/ruvocal/src/lib/utils/auth.ts +17 -0
  339. package/src/ruvocal/src/lib/utils/chunk.ts +33 -0
  340. package/src/ruvocal/src/lib/utils/cookiesAreEnabled.ts +13 -0
  341. package/src/ruvocal/src/lib/utils/debounce.ts +17 -0
  342. package/src/ruvocal/src/lib/utils/deepestChild.ts +6 -0
  343. package/src/ruvocal/src/lib/utils/favicon.ts +21 -0
  344. package/src/ruvocal/src/lib/utils/fetchJSON.ts +23 -0
  345. package/src/ruvocal/src/lib/utils/file2base64.ts +14 -0
  346. package/src/ruvocal/src/lib/utils/formatUserCount.ts +37 -0
  347. package/src/ruvocal/src/lib/utils/generationState.spec.ts +75 -0
  348. package/src/ruvocal/src/lib/utils/generationState.ts +26 -0
  349. package/src/ruvocal/src/lib/utils/getHref.ts +41 -0
  350. package/src/ruvocal/src/lib/utils/getReturnFromGenerator.ts +7 -0
  351. package/src/ruvocal/src/lib/utils/haptics.ts +64 -0
  352. package/src/ruvocal/src/lib/utils/hashConv.ts +12 -0
  353. package/src/ruvocal/src/lib/utils/hf.ts +17 -0
  354. package/src/ruvocal/src/lib/utils/isDesktop.ts +7 -0
  355. package/src/ruvocal/src/lib/utils/isUrl.ts +8 -0
  356. package/src/ruvocal/src/lib/utils/isVirtualKeyboard.ts +16 -0
  357. package/src/ruvocal/src/lib/utils/loadAttachmentsFromUrls.ts +115 -0
  358. package/src/ruvocal/src/lib/utils/marked.spec.ts +96 -0
  359. package/src/ruvocal/src/lib/utils/marked.ts +531 -0
  360. package/src/ruvocal/src/lib/utils/mcpValidation.ts +147 -0
  361. package/src/ruvocal/src/lib/utils/mergeAsyncGenerators.ts +38 -0
  362. package/src/ruvocal/src/lib/utils/messageUpdates.spec.ts +262 -0
  363. package/src/ruvocal/src/lib/utils/messageUpdates.ts +324 -0
  364. package/src/ruvocal/src/lib/utils/mime.ts +56 -0
  365. package/src/ruvocal/src/lib/utils/models.ts +14 -0
  366. package/src/ruvocal/src/lib/utils/parseBlocks.ts +120 -0
  367. package/src/ruvocal/src/lib/utils/parseIncompleteMarkdown.ts +644 -0
  368. package/src/ruvocal/src/lib/utils/parseStringToList.ts +10 -0
  369. package/src/ruvocal/src/lib/utils/randomUuid.ts +14 -0
  370. package/src/ruvocal/src/lib/utils/searchTokens.ts +33 -0
  371. package/src/ruvocal/src/lib/utils/sha256.ts +7 -0
  372. package/src/ruvocal/src/lib/utils/stringifyError.ts +12 -0
  373. package/src/ruvocal/src/lib/utils/sum.ts +3 -0
  374. package/src/ruvocal/src/lib/utils/template.spec.ts +59 -0
  375. package/src/ruvocal/src/lib/utils/template.ts +53 -0
  376. package/src/ruvocal/src/lib/utils/timeout.ts +9 -0
  377. package/src/ruvocal/src/lib/utils/toolProgress.spec.ts +46 -0
  378. package/src/ruvocal/src/lib/utils/toolProgress.ts +11 -0
  379. package/src/ruvocal/src/lib/utils/tree/addChildren.spec.ts +102 -0
  380. package/src/ruvocal/src/lib/utils/tree/addChildren.ts +48 -0
  381. package/src/ruvocal/src/lib/utils/tree/addSibling.spec.ts +81 -0
  382. package/src/ruvocal/src/lib/utils/tree/addSibling.ts +41 -0
  383. package/src/ruvocal/src/lib/utils/tree/buildSubtree.spec.ts +110 -0
  384. package/src/ruvocal/src/lib/utils/tree/buildSubtree.ts +24 -0
  385. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.spec.ts +31 -0
  386. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.ts +36 -0
  387. package/src/ruvocal/src/lib/utils/tree/isMessageId.spec.ts +15 -0
  388. package/src/ruvocal/src/lib/utils/tree/isMessageId.ts +5 -0
  389. package/src/ruvocal/src/lib/utils/tree/tree.d.ts +14 -0
  390. package/src/ruvocal/src/lib/utils/tree/treeHelpers.spec.ts +167 -0
  391. package/src/ruvocal/src/lib/utils/updates.ts +39 -0
  392. package/src/ruvocal/src/lib/utils/urlParams.ts +13 -0
  393. package/src/ruvocal/src/lib/workers/autopilotWorker.ts +221 -0
  394. package/src/ruvocal/src/lib/workers/detailFetchWorker.ts +100 -0
  395. package/src/ruvocal/src/lib/workers/markdownWorker.ts +61 -0
  396. package/src/ruvocal/src/routes/+error.svelte +20 -0
  397. package/src/ruvocal/src/routes/+layout.svelte +324 -0
  398. package/src/ruvocal/src/routes/+layout.ts +91 -0
  399. package/src/ruvocal/src/routes/+page.svelte +168 -0
  400. package/src/ruvocal/src/routes/.well-known/oauth-cimd/+server.ts +37 -0
  401. package/src/ruvocal/src/routes/__debug/openai/+server.ts +21 -0
  402. package/src/ruvocal/src/routes/admin/export/+server.ts +159 -0
  403. package/src/ruvocal/src/routes/admin/stats/compute/+server.ts +16 -0
  404. package/src/ruvocal/src/routes/api/conversation/[id]/+server.ts +40 -0
  405. package/src/ruvocal/src/routes/api/conversation/[id]/message/[messageId]/+server.ts +42 -0
  406. package/src/ruvocal/src/routes/api/conversations/+server.ts +48 -0
  407. package/src/ruvocal/src/routes/api/fetch-url/+server.ts +147 -0
  408. package/src/ruvocal/src/routes/api/mcp/health/+server.ts +292 -0
  409. package/src/ruvocal/src/routes/api/mcp/servers/+server.ts +32 -0
  410. package/src/ruvocal/src/routes/api/models/+server.ts +25 -0
  411. package/src/ruvocal/src/routes/api/transcribe/+server.ts +104 -0
  412. package/src/ruvocal/src/routes/api/user/+server.ts +15 -0
  413. package/src/ruvocal/src/routes/api/user/validate-token/+server.ts +20 -0
  414. package/src/ruvocal/src/routes/api/v2/conversations/+server.ts +48 -0
  415. package/src/ruvocal/src/routes/api/v2/conversations/[id]/+server.ts +94 -0
  416. package/src/ruvocal/src/routes/api/v2/conversations/[id]/message/[messageId]/+server.ts +43 -0
  417. package/src/ruvocal/src/routes/api/v2/conversations/import-share/+server.ts +23 -0
  418. package/src/ruvocal/src/routes/api/v2/debug/config/+server.ts +16 -0
  419. package/src/ruvocal/src/routes/api/v2/debug/refresh/+server.ts +30 -0
  420. package/src/ruvocal/src/routes/api/v2/export/+server.ts +196 -0
  421. package/src/ruvocal/src/routes/api/v2/feature-flags/+server.ts +14 -0
  422. package/src/ruvocal/src/routes/api/v2/models/+server.ts +38 -0
  423. package/src/ruvocal/src/routes/api/v2/models/[namespace]/+server.ts +8 -0
  424. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/+server.ts +8 -0
  425. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/subscribe/+server.ts +28 -0
  426. package/src/ruvocal/src/routes/api/v2/models/[namespace]/subscribe/+server.ts +28 -0
  427. package/src/ruvocal/src/routes/api/v2/models/old/+server.ts +7 -0
  428. package/src/ruvocal/src/routes/api/v2/models/refresh/+server.ts +33 -0
  429. package/src/ruvocal/src/routes/api/v2/public-config/+server.ts +7 -0
  430. package/src/ruvocal/src/routes/api/v2/user/+server.ts +17 -0
  431. package/src/ruvocal/src/routes/api/v2/user/billing-orgs/+server.ts +73 -0
  432. package/src/ruvocal/src/routes/api/v2/user/reports/+server.ts +17 -0
  433. package/src/ruvocal/src/routes/api/v2/user/settings/+server.ts +103 -0
  434. package/src/ruvocal/src/routes/conversation/+server.ts +115 -0
  435. package/src/ruvocal/src/routes/conversation/[id]/+page.svelte +582 -0
  436. package/src/ruvocal/src/routes/conversation/[id]/+page.ts +60 -0
  437. package/src/ruvocal/src/routes/conversation/[id]/+server.ts +736 -0
  438. package/src/ruvocal/src/routes/conversation/[id]/message/[messageId]/prompt/+server.ts +66 -0
  439. package/src/ruvocal/src/routes/conversation/[id]/output/[sha256]/+server.ts +58 -0
  440. package/src/ruvocal/src/routes/conversation/[id]/share/+server.ts +69 -0
  441. package/src/ruvocal/src/routes/conversation/[id]/stop-generating/+server.ts +35 -0
  442. package/src/ruvocal/src/routes/healthcheck/+server.ts +3 -0
  443. package/src/ruvocal/src/routes/login/+server.ts +5 -0
  444. package/src/ruvocal/src/routes/login/callback/+server.ts +103 -0
  445. package/src/ruvocal/src/routes/login/callback/updateUser.spec.ts +157 -0
  446. package/src/ruvocal/src/routes/login/callback/updateUser.ts +215 -0
  447. package/src/ruvocal/src/routes/logout/+server.ts +18 -0
  448. package/src/ruvocal/src/routes/metrics/+server.ts +18 -0
  449. package/src/ruvocal/src/routes/models/+page.svelte +233 -0
  450. package/src/ruvocal/src/routes/models/[...model]/+page.svelte +161 -0
  451. package/src/ruvocal/src/routes/models/[...model]/+page.ts +14 -0
  452. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/+server.ts +64 -0
  453. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte +28 -0
  454. package/src/ruvocal/src/routes/privacy/+page.svelte +11 -0
  455. package/src/ruvocal/src/routes/r/[id]/+page.ts +34 -0
  456. package/src/ruvocal/src/routes/settings/(nav)/+layout.svelte +282 -0
  457. package/src/ruvocal/src/routes/settings/(nav)/+layout.ts +1 -0
  458. package/src/ruvocal/src/routes/settings/(nav)/+page.svelte +0 -0
  459. package/src/ruvocal/src/routes/settings/(nav)/+server.ts +53 -0
  460. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.svelte +464 -0
  461. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.ts +14 -0
  462. package/src/ruvocal/src/routes/settings/(nav)/application/+page.svelte +362 -0
  463. package/src/ruvocal/src/routes/settings/+layout.svelte +40 -0
  464. package/src/ruvocal/src/styles/highlight-js.css +195 -0
  465. package/src/ruvocal/src/styles/main.css +144 -0
  466. package/src/ruvocal/static/chatui/apple-touch-icon.png +0 -0
  467. package/src/ruvocal/static/chatui/favicon-dark.svg +3 -0
  468. package/src/ruvocal/static/chatui/favicon-dev.svg +3 -0
  469. package/src/ruvocal/static/chatui/favicon.ico +0 -0
  470. package/src/ruvocal/static/chatui/favicon.svg +3 -0
  471. package/src/ruvocal/static/chatui/icon-128x128.png +0 -0
  472. package/src/ruvocal/static/chatui/icon-144x144.png +0 -0
  473. package/src/ruvocal/static/chatui/icon-192x192.png +0 -0
  474. package/src/ruvocal/static/chatui/icon-256x256.png +0 -0
  475. package/src/ruvocal/static/chatui/icon-36x36.png +0 -0
  476. package/src/ruvocal/static/chatui/icon-48x48.png +0 -0
  477. package/src/ruvocal/static/chatui/icon-512x512.png +0 -0
  478. package/src/ruvocal/static/chatui/icon-72x72.png +0 -0
  479. package/src/ruvocal/static/chatui/icon-96x96.png +0 -0
  480. package/src/ruvocal/static/chatui/icon.svg +3 -0
  481. package/src/ruvocal/static/chatui/logo.svg +7 -0
  482. package/src/ruvocal/static/chatui/manifest.json +54 -0
  483. package/src/ruvocal/static/chatui/omni-welcome.gif +0 -0
  484. package/src/ruvocal/static/chatui/omni-welcome.png +0 -0
  485. package/src/ruvocal/static/chatui/welcome.js +184 -0
  486. package/src/ruvocal/static/chatui/welcome.svg +1 -0
  487. package/src/ruvocal/static/huggingchat/apple-touch-icon.png +0 -0
  488. package/src/ruvocal/static/huggingchat/assistants-thumbnail.png +0 -0
  489. package/src/ruvocal/static/huggingchat/castle-example.jpg +0 -0
  490. package/src/ruvocal/static/huggingchat/favicon-dark.svg +4 -0
  491. package/src/ruvocal/static/huggingchat/favicon-dev.svg +4 -0
  492. package/src/ruvocal/static/huggingchat/favicon.ico +0 -0
  493. package/src/ruvocal/static/huggingchat/favicon.svg +4 -0
  494. package/src/ruvocal/static/huggingchat/fulltext-logo.svg +2 -0
  495. package/src/ruvocal/static/huggingchat/icon-128x128.png +0 -0
  496. package/src/ruvocal/static/huggingchat/icon-144x144.png +0 -0
  497. package/src/ruvocal/static/huggingchat/icon-192x192.png +0 -0
  498. package/src/ruvocal/static/huggingchat/icon-256x256.png +0 -0
  499. package/src/ruvocal/static/huggingchat/icon-36x36.png +0 -0
  500. package/src/ruvocal/static/huggingchat/icon-48x48.png +0 -0
  501. package/src/ruvocal/static/huggingchat/icon-512x512.png +0 -0
  502. package/src/ruvocal/static/huggingchat/icon-72x72.png +0 -0
  503. package/src/ruvocal/static/huggingchat/icon-96x96.png +0 -0
  504. package/src/ruvocal/static/huggingchat/icon.svg +4 -0
  505. package/src/ruvocal/static/huggingchat/logo.svg +4 -0
  506. package/src/ruvocal/static/huggingchat/manifest.json +54 -0
  507. package/src/ruvocal/static/huggingchat/omni-welcome.gif +0 -0
  508. package/src/ruvocal/static/huggingchat/routes.chat.json +226 -0
  509. package/src/ruvocal/static/huggingchat/thumbnail.png +0 -0
  510. package/src/ruvocal/static/huggingchat/tools-thumbnail.png +0 -0
  511. package/src/ruvocal/static/robots.txt +10 -0
  512. package/src/ruvocal/stub/@reflink/reflink/index.js +0 -0
  513. package/src/ruvocal/stub/@reflink/reflink/package.json +5 -0
  514. package/src/ruvocal/svelte.config.js +53 -0
  515. package/src/ruvocal/tailwind.config.cjs +30 -0
  516. package/src/ruvocal/tsconfig.json +19 -0
  517. package/src/ruvocal/vite.config.ts +87 -0
  518. package/src/scripts/deploy.sh +116 -0
  519. package/src/scripts/generate-config.js +245 -0
  520. package/src/scripts/generate-welcome.js +187 -0
  521. package/src/scripts/package-rvf.sh +116 -0
@@ -0,0 +1,266 @@
1
+ import { z } from "zod";
2
+ import { openAICompletionToTextGenerationStream } from "./openAICompletionToTextGenerationStream";
3
+ import {
4
+ openAIChatToTextGenerationSingle,
5
+ openAIChatToTextGenerationStream,
6
+ } from "./openAIChatToTextGenerationStream";
7
+ import type { CompletionCreateParamsStreaming } from "openai/resources/completions";
8
+ import type {
9
+ ChatCompletionCreateParamsNonStreaming,
10
+ ChatCompletionCreateParamsStreaming,
11
+ } from "openai/resources/chat/completions";
12
+ import { buildPrompt } from "$lib/buildPrompt";
13
+ import { config } from "$lib/server/config";
14
+ import type { Endpoint } from "../endpoints";
15
+ import type OpenAI from "openai";
16
+ import { createImageProcessorOptionsValidator, makeImageProcessor } from "../images";
17
+ import { prepareMessagesWithFiles } from "$lib/server/textGeneration/utils/prepareFiles";
18
+ // uuid import removed (no tool call ids)
19
+
20
+ export const endpointOAIParametersSchema = z.object({
21
+ weight: z.number().int().positive().default(1),
22
+ model: z.any(),
23
+ type: z.literal("openai"),
24
+ baseURL: z.string().url().default("https://api.openai.com/v1"),
25
+ // Canonical auth token is OPENAI_API_KEY; keep HF_TOKEN as legacy alias
26
+ apiKey: z.string().default(config.OPENAI_API_KEY || config.HF_TOKEN || "sk-"),
27
+ completion: z
28
+ .union([z.literal("completions"), z.literal("chat_completions")])
29
+ .default("chat_completions"),
30
+ defaultHeaders: z.record(z.string()).optional(),
31
+ defaultQuery: z.record(z.string()).optional(),
32
+ extraBody: z.record(z.any()).optional(),
33
+ multimodal: z
34
+ .object({
35
+ image: createImageProcessorOptionsValidator({
36
+ supportedMimeTypes: [
37
+ // Restrict to the most widely-supported formats
38
+ "image/png",
39
+ "image/jpeg",
40
+ ],
41
+ preferredMimeType: "image/jpeg",
42
+ maxSizeInMB: 1,
43
+ maxWidth: 1024,
44
+ maxHeight: 1024,
45
+ }),
46
+ })
47
+ .default({}),
48
+ /* enable use of max_completion_tokens in place of max_tokens */
49
+ useCompletionTokens: z.boolean().default(false),
50
+ streamingSupported: z.boolean().default(true),
51
+ });
52
+
53
+ export async function endpointOai(
54
+ input: z.input<typeof endpointOAIParametersSchema>
55
+ ): Promise<Endpoint> {
56
+ const {
57
+ baseURL,
58
+ apiKey,
59
+ completion,
60
+ model,
61
+ defaultHeaders,
62
+ defaultQuery,
63
+ multimodal,
64
+ extraBody,
65
+ useCompletionTokens,
66
+ streamingSupported,
67
+ } = endpointOAIParametersSchema.parse(input);
68
+
69
+ let OpenAI;
70
+ try {
71
+ OpenAI = (await import("openai")).OpenAI;
72
+ } catch (e) {
73
+ throw new Error("Failed to import OpenAI", { cause: e });
74
+ }
75
+
76
+ // Store router metadata if captured
77
+ let routerMetadata: { route?: string; model?: string; provider?: string } = {};
78
+
79
+ // Custom fetch wrapper to capture response headers for router metadata
80
+ const customFetch = async (url: RequestInfo, init?: RequestInit): Promise<Response> => {
81
+ const response = await fetch(url, init);
82
+
83
+ // Capture router headers if present (fallback for non-streaming)
84
+ const routeHeader = response.headers.get("X-Router-Route");
85
+ const modelHeader = response.headers.get("X-Router-Model");
86
+ const providerHeader = response.headers.get("x-inference-provider");
87
+
88
+ if (routeHeader && modelHeader) {
89
+ routerMetadata = {
90
+ route: routeHeader,
91
+ model: modelHeader,
92
+ provider: providerHeader || undefined,
93
+ };
94
+ } else if (providerHeader) {
95
+ // Even without router metadata, capture provider info
96
+ routerMetadata = {
97
+ provider: providerHeader,
98
+ };
99
+ }
100
+
101
+ return response;
102
+ };
103
+
104
+ const openai = new OpenAI({
105
+ apiKey: apiKey || "sk-",
106
+ baseURL,
107
+ defaultHeaders: {
108
+ ...(config.PUBLIC_APP_NAME === "HuggingChat" && { "User-Agent": "huggingchat" }),
109
+ ...defaultHeaders,
110
+ },
111
+ defaultQuery,
112
+ fetch: customFetch,
113
+ });
114
+
115
+ const imageProcessor = makeImageProcessor(multimodal.image);
116
+
117
+ if (completion === "completions") {
118
+ return async ({
119
+ messages,
120
+ preprompt,
121
+ generateSettings,
122
+ conversationId,
123
+ locals,
124
+ abortSignal,
125
+ provider,
126
+ }) => {
127
+ const prompt = await buildPrompt({
128
+ messages,
129
+ preprompt,
130
+ model,
131
+ });
132
+
133
+ // Build model ID with optional provider suffix (e.g., "model:fastest" or "model:together")
134
+ const baseModelId = model.id ?? model.name;
135
+ const modelId = provider && provider !== "auto" ? `${baseModelId}:${provider}` : baseModelId;
136
+
137
+ const parameters = { ...model.parameters, ...generateSettings };
138
+ const body: CompletionCreateParamsStreaming = {
139
+ model: modelId,
140
+ prompt,
141
+ stream: true,
142
+ max_tokens: parameters?.max_tokens,
143
+ stop: parameters?.stop,
144
+ temperature: parameters?.temperature,
145
+ top_p: parameters?.top_p,
146
+ frequency_penalty: parameters?.frequency_penalty,
147
+ presence_penalty: parameters?.presence_penalty,
148
+ };
149
+
150
+ const openAICompletion = await openai.completions.create(body, {
151
+ body: { ...body, ...extraBody },
152
+ headers: {
153
+ "ChatUI-Conversation-ID": conversationId?.toString() ?? "",
154
+ "X-use-cache": "false",
155
+ ...(locals?.token ? { Authorization: `Bearer ${locals.token}` } : {}),
156
+ // Bill to organization if configured
157
+ ...(locals?.billingOrganization ? { "X-HF-Bill-To": locals.billingOrganization } : {}),
158
+ },
159
+ signal: abortSignal,
160
+ });
161
+
162
+ return openAICompletionToTextGenerationStream(openAICompletion);
163
+ };
164
+ } else if (completion === "chat_completions") {
165
+ return async ({
166
+ messages,
167
+ preprompt,
168
+ generateSettings,
169
+ conversationId,
170
+ isMultimodal,
171
+ locals,
172
+ abortSignal,
173
+ provider,
174
+ }) => {
175
+ // Format messages for the chat API, handling multimodal content if supported
176
+ let messagesOpenAI: OpenAI.Chat.Completions.ChatCompletionMessageParam[] =
177
+ await prepareMessagesWithFiles(messages, imageProcessor, isMultimodal ?? model.multimodal);
178
+
179
+ // Normalize preprompt and handle empty values
180
+ const normalizedPreprompt = typeof preprompt === "string" ? preprompt.trim() : "";
181
+
182
+ // Check if a system message already exists as the first message
183
+ const hasSystemMessage = messagesOpenAI.length > 0 && messagesOpenAI[0]?.role === "system";
184
+
185
+ if (hasSystemMessage) {
186
+ // Prepend normalized preprompt to existing system content when non-empty
187
+ if (normalizedPreprompt) {
188
+ const userSystemPrompt =
189
+ (typeof messagesOpenAI[0].content === "string"
190
+ ? (messagesOpenAI[0].content as string)
191
+ : "") || "";
192
+ messagesOpenAI[0].content =
193
+ normalizedPreprompt + (userSystemPrompt ? "\n\n" + userSystemPrompt : "");
194
+ }
195
+ } else {
196
+ // Insert a system message only if the preprompt is non-empty
197
+ if (normalizedPreprompt) {
198
+ messagesOpenAI = [{ role: "system", content: normalizedPreprompt }, ...messagesOpenAI];
199
+ }
200
+ }
201
+
202
+ // Combine model defaults with request-specific parameters
203
+ const parameters = { ...model.parameters, ...generateSettings };
204
+
205
+ // Build model ID with optional provider suffix (e.g., "model:fastest" or "model:together")
206
+ const baseModelId = model.id ?? model.name;
207
+ const modelId = provider && provider !== "auto" ? `${baseModelId}:${provider}` : baseModelId;
208
+
209
+ const body = {
210
+ model: modelId,
211
+ messages: messagesOpenAI,
212
+ stream: streamingSupported,
213
+ // Support two different ways of specifying token limits depending on the model
214
+ ...(useCompletionTokens
215
+ ? { max_completion_tokens: parameters?.max_tokens }
216
+ : { max_tokens: parameters?.max_tokens }),
217
+ stop: parameters?.stop,
218
+ temperature: parameters?.temperature,
219
+ top_p: parameters?.top_p,
220
+ frequency_penalty: parameters?.frequency_penalty,
221
+ presence_penalty: parameters?.presence_penalty,
222
+ };
223
+
224
+ // Handle both streaming and non-streaming responses with appropriate processors
225
+ if (streamingSupported) {
226
+ const openChatAICompletion = await openai.chat.completions.create(
227
+ body as ChatCompletionCreateParamsStreaming,
228
+ {
229
+ body: { ...body, ...extraBody },
230
+ headers: {
231
+ "ChatUI-Conversation-ID": conversationId?.toString() ?? "",
232
+ "X-use-cache": "false",
233
+ ...(locals?.token ? { Authorization: `Bearer ${locals.token}` } : {}),
234
+ // Bill to organization if configured
235
+ ...(locals?.billingOrganization
236
+ ? { "X-HF-Bill-To": locals.billingOrganization }
237
+ : {}),
238
+ },
239
+ signal: abortSignal,
240
+ }
241
+ );
242
+ return openAIChatToTextGenerationStream(openChatAICompletion, () => routerMetadata);
243
+ } else {
244
+ const openChatAICompletion = await openai.chat.completions.create(
245
+ body as ChatCompletionCreateParamsNonStreaming,
246
+ {
247
+ body: { ...body, ...extraBody },
248
+ headers: {
249
+ "ChatUI-Conversation-ID": conversationId?.toString() ?? "",
250
+ "X-use-cache": "false",
251
+ ...(locals?.token ? { Authorization: `Bearer ${locals.token}` } : {}),
252
+ // Bill to organization if configured
253
+ ...(locals?.billingOrganization
254
+ ? { "X-HF-Bill-To": locals.billingOrganization }
255
+ : {}),
256
+ },
257
+ signal: abortSignal,
258
+ }
259
+ );
260
+ return openAIChatToTextGenerationSingle(openChatAICompletion, () => routerMetadata);
261
+ }
262
+ };
263
+ } else {
264
+ throw new Error("Invalid completion type");
265
+ }
266
+ }
@@ -0,0 +1,212 @@
1
+ import type { TextGenerationStreamOutput } from "@huggingface/inference";
2
+ import type OpenAI from "openai";
3
+ import type { Stream } from "openai/streaming";
4
+
5
+ /**
6
+ * Transform a stream of OpenAI.Chat.ChatCompletion into a stream of TextGenerationStreamOutput
7
+ */
8
+ export async function* openAIChatToTextGenerationStream(
9
+ completionStream: Stream<OpenAI.Chat.Completions.ChatCompletionChunk>,
10
+ getRouterMetadata?: () => { route?: string; model?: string; provider?: string }
11
+ ) {
12
+ let generatedText = "";
13
+ let tokenId = 0;
14
+ let toolBuffer = ""; // legacy hack kept harmless
15
+ let metadataYielded = false;
16
+ let thinkOpen = false;
17
+
18
+ for await (const completion of completionStream) {
19
+ const retyped = completion as {
20
+ "x-router-metadata"?: { route: string; model: string; provider?: string };
21
+ };
22
+ // Check if this chunk contains router metadata (first chunk from llm-router)
23
+ if (!metadataYielded && retyped["x-router-metadata"]) {
24
+ const metadata = retyped["x-router-metadata"];
25
+ yield {
26
+ token: {
27
+ id: tokenId++,
28
+ text: "",
29
+ logprob: 0,
30
+ special: true,
31
+ },
32
+ generated_text: null,
33
+ details: null,
34
+ routerMetadata: {
35
+ route: metadata.route,
36
+ model: metadata.model,
37
+ provider: metadata.provider,
38
+ },
39
+ } as TextGenerationStreamOutput & {
40
+ routerMetadata: { route: string; model: string; provider?: string };
41
+ };
42
+ metadataYielded = true;
43
+ // Skip processing this chunk as content since it's just metadata
44
+ if (
45
+ !completion.choices ||
46
+ completion.choices.length === 0 ||
47
+ !completion.choices[0].delta?.content
48
+ ) {
49
+ continue;
50
+ }
51
+ }
52
+ const { choices } = completion;
53
+ const delta: OpenAI.Chat.Completions.ChatCompletionChunk.Choice.Delta & {
54
+ reasoning?: string;
55
+ reasoning_content?: string;
56
+ } = choices?.[0]?.delta ?? {};
57
+ const content: string = delta.content ?? "";
58
+ const reasoning: string =
59
+ typeof delta?.reasoning === "string"
60
+ ? (delta.reasoning as string)
61
+ : typeof delta?.reasoning_content === "string"
62
+ ? (delta.reasoning_content as string)
63
+ : "";
64
+ const last = choices?.[0]?.finish_reason === "stop" || choices?.[0]?.finish_reason === "length";
65
+
66
+ // if the last token is a stop and the tool buffer is not empty, yield it as a generated_text
67
+ if (choices?.[0]?.finish_reason === "stop" && toolBuffer.length > 0) {
68
+ yield {
69
+ token: {
70
+ id: tokenId++,
71
+ special: true,
72
+ logprob: 0,
73
+ text: "",
74
+ },
75
+ generated_text: toolBuffer,
76
+ details: null,
77
+ } as TextGenerationStreamOutput;
78
+ break;
79
+ }
80
+
81
+ // weird bug where the parameters are streamed in like this
82
+ if (choices?.[0]?.delta?.tool_calls) {
83
+ const calls = Array.isArray(choices[0].delta.tool_calls)
84
+ ? choices[0].delta.tool_calls
85
+ : [choices[0].delta.tool_calls];
86
+
87
+ if (
88
+ calls.length === 1 &&
89
+ calls[0].index === 0 &&
90
+ calls[0].id === "" &&
91
+ calls[0].type === "function" &&
92
+ !!calls[0].function &&
93
+ calls[0].function.name === null
94
+ ) {
95
+ toolBuffer += calls[0].function.arguments;
96
+ continue;
97
+ }
98
+ }
99
+
100
+ let combined = "";
101
+ if (reasoning && reasoning.length > 0) {
102
+ if (!thinkOpen) {
103
+ combined += "<think>" + reasoning;
104
+ thinkOpen = true;
105
+ } else {
106
+ combined += reasoning;
107
+ }
108
+ }
109
+
110
+ if (content && content.length > 0) {
111
+ const trimmed = content.trim();
112
+ // Allow <think> tags in content to pass through (for models like DeepSeek R1)
113
+ if (thinkOpen && trimmed === "</think>") {
114
+ // close once without duplicating the tag
115
+ combined += "</think>";
116
+ thinkOpen = false;
117
+ } else if (thinkOpen) {
118
+ combined += "</think>" + content;
119
+ thinkOpen = false;
120
+ } else {
121
+ combined += content;
122
+ }
123
+ }
124
+
125
+ // Accumulate the combined token into the full text
126
+ generatedText += combined;
127
+ const output: TextGenerationStreamOutput = {
128
+ token: {
129
+ id: tokenId++,
130
+ text: combined,
131
+ logprob: 0,
132
+ special: last,
133
+ },
134
+ generated_text: last ? generatedText : null,
135
+ details: null,
136
+ };
137
+ yield output;
138
+
139
+ // Tools removed: ignore tool_calls deltas
140
+ }
141
+
142
+ // If metadata wasn't yielded from chunks (e.g., from headers), yield it at the end
143
+ if (!metadataYielded && getRouterMetadata) {
144
+ const routerMetadata = getRouterMetadata();
145
+ // Yield if we have either complete router metadata OR just provider info
146
+ if (
147
+ (routerMetadata && routerMetadata.route && routerMetadata.model) ||
148
+ routerMetadata?.provider
149
+ ) {
150
+ yield {
151
+ token: {
152
+ id: tokenId++,
153
+ text: "",
154
+ logprob: 0,
155
+ special: true,
156
+ },
157
+ generated_text: null,
158
+ details: null,
159
+ routerMetadata,
160
+ } as TextGenerationStreamOutput & {
161
+ routerMetadata: { route?: string; model?: string; provider?: string };
162
+ };
163
+ }
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Transform a non-streaming OpenAI chat completion into a stream of TextGenerationStreamOutput
169
+ */
170
+ export async function* openAIChatToTextGenerationSingle(
171
+ completion: OpenAI.Chat.Completions.ChatCompletion,
172
+ getRouterMetadata?: () => { route?: string; model?: string; provider?: string }
173
+ ) {
174
+ const message: NonNullable<OpenAI.Chat.Completions.ChatCompletion.Choice>["message"] & {
175
+ reasoning?: string;
176
+ reasoning_content?: string;
177
+ } = completion.choices?.[0]?.message ?? {};
178
+ let content: string = message?.content || "";
179
+ // Provider-dependent reasoning shapes (non-streaming)
180
+ const r: string =
181
+ typeof message?.reasoning === "string"
182
+ ? (message.reasoning as string)
183
+ : typeof message?.reasoning_content === "string"
184
+ ? (message.reasoning_content as string)
185
+ : "";
186
+ if (r && r.length > 0) {
187
+ content = `<think>${r}</think>` + content;
188
+ }
189
+ const tokenId = 0;
190
+
191
+ // Yield the content as a single token
192
+ yield {
193
+ token: {
194
+ id: tokenId,
195
+ text: content,
196
+ logprob: 0,
197
+ special: false,
198
+ },
199
+ generated_text: content,
200
+ details: null,
201
+ ...(getRouterMetadata
202
+ ? (() => {
203
+ const metadata = getRouterMetadata();
204
+ return (metadata && metadata.route && metadata.model) || metadata?.provider
205
+ ? { routerMetadata: metadata }
206
+ : {};
207
+ })()
208
+ : {}),
209
+ } as TextGenerationStreamOutput & {
210
+ routerMetadata?: { route?: string; model?: string; provider?: string };
211
+ };
212
+ }
@@ -0,0 +1,32 @@
1
+ import type { TextGenerationStreamOutput } from "@huggingface/inference";
2
+ import type OpenAI from "openai";
3
+ import type { Stream } from "openai/streaming";
4
+
5
+ /**
6
+ * Transform a stream of OpenAI.Completions.Completion into a stream of TextGenerationStreamOutput
7
+ */
8
+ export async function* openAICompletionToTextGenerationStream(
9
+ completionStream: Stream<OpenAI.Completions.Completion>
10
+ ) {
11
+ let generatedText = "";
12
+ let tokenId = 0;
13
+ for await (const completion of completionStream) {
14
+ const { choices } = completion;
15
+ const text = choices?.[0]?.text ?? "";
16
+ const last = choices?.[0]?.finish_reason === "stop" || choices?.[0]?.finish_reason === "length";
17
+ if (text) {
18
+ generatedText = generatedText + text;
19
+ }
20
+ const output: TextGenerationStreamOutput = {
21
+ token: {
22
+ id: tokenId++,
23
+ text,
24
+ logprob: 0,
25
+ special: last,
26
+ },
27
+ generated_text: last ? generatedText : null,
28
+ details: null,
29
+ };
30
+ yield output;
31
+ }
32
+ }
@@ -0,0 +1,61 @@
1
+ import type { Message } from "$lib/types/Message";
2
+ import type { EndpointMessage } from "./endpoints";
3
+ import { downloadFile } from "../files/downloadFile";
4
+ import type { ObjectId } from "mongodb";
5
+
6
+ export async function preprocessMessages(
7
+ messages: Message[],
8
+ convId: ObjectId
9
+ ): Promise<EndpointMessage[]> {
10
+ return Promise.resolve(messages)
11
+ .then((msgs) => downloadFiles(msgs, convId))
12
+ .then((msgs) => injectClipboardFiles(msgs))
13
+ .then(stripEmptyInitialSystemMessage);
14
+ }
15
+
16
+ async function downloadFiles(messages: Message[], convId: ObjectId): Promise<EndpointMessage[]> {
17
+ return Promise.all(
18
+ messages.map<Promise<EndpointMessage>>((message) =>
19
+ Promise.all((message.files ?? []).map((file) => downloadFile(file.value, convId))).then(
20
+ (files) => ({ ...message, files })
21
+ )
22
+ )
23
+ );
24
+ }
25
+
26
+ async function injectClipboardFiles(messages: EndpointMessage[]) {
27
+ return Promise.all(
28
+ messages.map((message) => {
29
+ const plaintextFiles = message.files
30
+ ?.filter((file) => file.mime === "application/vnd.chatui.clipboard")
31
+ .map((file) => Buffer.from(file.value, "base64").toString("utf-8"));
32
+
33
+ if (!plaintextFiles || plaintextFiles.length === 0) return message;
34
+
35
+ return {
36
+ ...message,
37
+ content: `${plaintextFiles.join("\n\n")}\n\n${message.content}`,
38
+ files: message.files?.filter((file) => file.mime !== "application/vnd.chatui.clipboard"),
39
+ };
40
+ })
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Remove an initial system message if its content is empty/whitespace only.
46
+ * This prevents sending an empty system prompt to any provider.
47
+ */
48
+ function stripEmptyInitialSystemMessage(messages: EndpointMessage[]): EndpointMessage[] {
49
+ if (!messages?.length) return messages;
50
+ const first = messages[0];
51
+ if (first?.from !== "system") return messages;
52
+
53
+ const content = first?.content as unknown;
54
+ const isEmpty = typeof content === "string" ? content.trim().length === 0 : false;
55
+
56
+ if (isEmpty) {
57
+ return messages.slice(1);
58
+ }
59
+
60
+ return messages;
61
+ }
@@ -0,0 +1,59 @@
1
+ import { randomUUID } from "$lib/utils/randomUuid";
2
+ import { timeout } from "$lib/utils/timeout";
3
+ import { logger } from "./logger";
4
+
5
+ type ExitHandler = () => void | Promise<void>;
6
+ type ExitHandlerUnsubscribe = () => void;
7
+
8
+ const listeners = new Map<string, ExitHandler>();
9
+
10
+ export function onExit(cb: ExitHandler): ExitHandlerUnsubscribe {
11
+ const uuid = randomUUID();
12
+ listeners.set(uuid, cb);
13
+ return () => {
14
+ listeners.delete(uuid);
15
+ };
16
+ }
17
+
18
+ async function runExitHandler(handler: ExitHandler): Promise<void> {
19
+ return timeout(Promise.resolve().then(handler), 30_000).catch((err) => {
20
+ logger.error(err, "Exit handler failed to run");
21
+ });
22
+ }
23
+
24
+ export function initExitHandler() {
25
+ let signalCount = 0;
26
+ const exitHandler = async () => {
27
+ if (signalCount === 1) {
28
+ logger.info("Received signal... Exiting");
29
+ await Promise.all(Array.from(listeners.values()).map(runExitHandler));
30
+ logger.info("All exit handlers ran... Waiting for svelte server to exit");
31
+ }
32
+ };
33
+
34
+ process.on("SIGINT", () => {
35
+ signalCount++;
36
+
37
+ if (signalCount >= 2) {
38
+ process.kill(process.pid, "SIGKILL");
39
+ } else {
40
+ exitHandler().catch((err) => {
41
+ logger.error(err, "Error in exit handler on SIGINT:");
42
+ process.kill(process.pid, "SIGKILL");
43
+ });
44
+ }
45
+ });
46
+
47
+ process.on("SIGTERM", () => {
48
+ signalCount++;
49
+
50
+ if (signalCount >= 2) {
51
+ process.kill(process.pid, "SIGKILL");
52
+ } else {
53
+ exitHandler().catch((err) => {
54
+ logger.error(err, "Error in exit handler on SIGTERM:");
55
+ process.kill(process.pid, "SIGKILL");
56
+ });
57
+ }
58
+ });
59
+ }
@@ -0,0 +1,34 @@
1
+ import { error } from "@sveltejs/kit";
2
+ import { collections } from "$lib/server/database";
3
+ import type { Conversation } from "$lib/types/Conversation";
4
+ import type { SharedConversation } from "$lib/types/SharedConversation";
5
+ import type { MessageFile } from "$lib/types/Message";
6
+
7
+ export async function downloadFile(
8
+ sha256: string,
9
+ convId: Conversation["_id"] | SharedConversation["_id"]
10
+ ): Promise<MessageFile & { type: "base64" }> {
11
+ const fileId = collections.bucket.find({ filename: `${convId.toString()}-${sha256}` });
12
+
13
+ const file = await fileId.next();
14
+ if (!file) {
15
+ error(404, "File not found");
16
+ }
17
+ if (file.metadata?.conversation !== convId.toString()) {
18
+ error(403, "You don't have access to this file.");
19
+ }
20
+
21
+ const mime = file.metadata?.mime;
22
+ const name = file.filename;
23
+
24
+ const fileStream = collections.bucket.openDownloadStream(file._id);
25
+
26
+ const buffer = await new Promise<Buffer>((resolve, reject) => {
27
+ const chunks: Uint8Array[] = [];
28
+ fileStream.on("data", (chunk) => chunks.push(chunk));
29
+ fileStream.on("error", reject);
30
+ fileStream.on("end", () => resolve(Buffer.concat(chunks)));
31
+ });
32
+
33
+ return { type: "base64", name, value: buffer.toString("base64"), mime };
34
+ }
@@ -0,0 +1,29 @@
1
+ import type { Conversation } from "$lib/types/Conversation";
2
+ import type { MessageFile } from "$lib/types/Message";
3
+ import { sha256 } from "$lib/utils/sha256";
4
+ import { fileTypeFromBuffer } from "file-type";
5
+ import { collections } from "$lib/server/database";
6
+
7
+ export async function uploadFile(file: File, conv: Conversation): Promise<MessageFile> {
8
+ const sha = await sha256(await file.text());
9
+ const buffer = await file.arrayBuffer();
10
+
11
+ // Attempt to detect the mime type of the file, fallback to the uploaded mime
12
+ const mime = await fileTypeFromBuffer(buffer).then((fileType) => fileType?.mime ?? file.type);
13
+
14
+ const upload = collections.bucket.openUploadStream(`${conv._id}-${sha}`, {
15
+ metadata: { conversation: conv._id.toString(), mime },
16
+ });
17
+
18
+ upload.write((await file.arrayBuffer()) as unknown as Buffer);
19
+ upload.end();
20
+
21
+ // only return the filename when upload throws a finish event or a 20s time out occurs
22
+ return new Promise((resolve, reject) => {
23
+ upload.once("finish", () =>
24
+ resolve({ type: "hash", value: sha, mime: file.type, name: file.name })
25
+ );
26
+ upload.once("error", reject);
27
+ setTimeout(() => reject(new Error("Upload timed out")), 20_000);
28
+ });
29
+ }