@aigentic/ruflo 3.7.0-alpha.69

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 (524) hide show
  1. package/README.md +410 -0
  2. package/bin/ruflo.js +57 -0
  3. package/package.json +98 -0
  4. package/src/chat-ui/Dockerfile +25 -0
  5. package/src/chat-ui/patch-mcp-url-safety.sh +28 -0
  6. package/src/chat-ui/static/chatui/icon-144x144.png +0 -0
  7. package/src/chat-ui/static/chatui/omni-welcome.gif +0 -0
  8. package/src/config/config.example.json +76 -0
  9. package/src/mcp-bridge/Dockerfile +45 -0
  10. package/src/mcp-bridge/index.js +1668 -0
  11. package/src/mcp-bridge/mcp-stdio-kernel.js +159 -0
  12. package/src/mcp-bridge/package.json +17 -0
  13. package/src/mcp-bridge/test-harness.js +470 -0
  14. package/src/nginx/Dockerfile +10 -0
  15. package/src/nginx/nginx.conf +67 -0
  16. package/src/nginx/static/favicon-dark.svg +4 -0
  17. package/src/nginx/static/favicon.svg +4 -0
  18. package/src/nginx/static/icon.svg +5 -0
  19. package/src/nginx/static/logo.svg +9 -0
  20. package/src/nginx/static/manifest.json +22 -0
  21. package/src/nginx/static/welcome.js +184 -0
  22. package/src/ruvocal/.claude/skills/add-model-descriptions/SKILL.md +73 -0
  23. package/src/ruvocal/.devcontainer/Dockerfile +9 -0
  24. package/src/ruvocal/.devcontainer/devcontainer.json +36 -0
  25. package/src/ruvocal/.dockerignore +17 -0
  26. package/src/ruvocal/.eslintignore +13 -0
  27. package/src/ruvocal/.eslintrc.cjs +45 -0
  28. package/src/ruvocal/.gcloudignore +18 -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/CLAUDE.md +126 -0
  48. package/src/ruvocal/Dockerfile +96 -0
  49. package/src/ruvocal/LICENSE +203 -0
  50. package/src/ruvocal/PRIVACY.md +41 -0
  51. package/src/ruvocal/README.md +164 -0
  52. package/src/ruvocal/chart/Chart.yaml +5 -0
  53. package/src/ruvocal/chart/env/dev.yaml +260 -0
  54. package/src/ruvocal/chart/env/prod.yaml +273 -0
  55. package/src/ruvocal/chart/templates/_helpers.tpl +22 -0
  56. package/src/ruvocal/chart/templates/config.yaml +10 -0
  57. package/src/ruvocal/chart/templates/deployment.yaml +81 -0
  58. package/src/ruvocal/chart/templates/hpa.yaml +45 -0
  59. package/src/ruvocal/chart/templates/infisical.yaml +24 -0
  60. package/src/ruvocal/chart/templates/ingress-internal.yaml +32 -0
  61. package/src/ruvocal/chart/templates/ingress.yaml +32 -0
  62. package/src/ruvocal/chart/templates/network-policy.yaml +36 -0
  63. package/src/ruvocal/chart/templates/service-account.yaml +13 -0
  64. package/src/ruvocal/chart/templates/service-monitor.yaml +17 -0
  65. package/src/ruvocal/chart/templates/service.yaml +21 -0
  66. package/src/ruvocal/chart/values.yaml +73 -0
  67. package/src/ruvocal/cloudbuild.yaml +68 -0
  68. package/src/ruvocal/config/branding.env.example +19 -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/Dockerfile +45 -0
  91. package/src/ruvocal/mcp-bridge/cloudbuild.yaml +49 -0
  92. package/src/ruvocal/mcp-bridge/index.js +1878 -0
  93. package/src/ruvocal/mcp-bridge/mcp-stdio-kernel.js +159 -0
  94. package/src/ruvocal/mcp-bridge/package-lock.json +762 -0
  95. package/src/ruvocal/mcp-bridge/package.json +17 -0
  96. package/src/ruvocal/mcp-bridge/test-harness.js +470 -0
  97. package/src/ruvocal/models/add-your-models-here.txt +1 -0
  98. package/src/ruvocal/package-lock.json +11741 -0
  99. package/src/ruvocal/package.json +121 -0
  100. package/src/ruvocal/postcss.config.js +6 -0
  101. package/src/ruvocal/rvf.manifest.json +204 -0
  102. package/src/ruvocal/scripts/config.ts +64 -0
  103. package/src/ruvocal/scripts/generate-welcome.mjs +181 -0
  104. package/src/ruvocal/scripts/populate.ts +288 -0
  105. package/src/ruvocal/scripts/samples.txt +194 -0
  106. package/src/ruvocal/scripts/setups/vitest-setup-client.ts +0 -0
  107. package/src/ruvocal/scripts/setups/vitest-setup-server.ts +44 -0
  108. package/src/ruvocal/scripts/updateLocalEnv.ts +48 -0
  109. package/src/ruvocal/src/ambient.d.ts +7 -0
  110. package/src/ruvocal/src/app.d.ts +29 -0
  111. package/src/ruvocal/src/app.html +53 -0
  112. package/src/ruvocal/src/hooks.server.ts +32 -0
  113. package/src/ruvocal/src/hooks.ts +6 -0
  114. package/src/ruvocal/src/lib/APIClient.ts +148 -0
  115. package/src/ruvocal/src/lib/actions/clickOutside.ts +18 -0
  116. package/src/ruvocal/src/lib/actions/snapScrollToBottom.ts +346 -0
  117. package/src/ruvocal/src/lib/buildPrompt.ts +33 -0
  118. package/src/ruvocal/src/lib/components/AnnouncementBanner.svelte +20 -0
  119. package/src/ruvocal/src/lib/components/BackgroundGenerationPoller.svelte +168 -0
  120. package/src/ruvocal/src/lib/components/CodeBlock.svelte +73 -0
  121. package/src/ruvocal/src/lib/components/CopyToClipBoardBtn.svelte +92 -0
  122. package/src/ruvocal/src/lib/components/DeleteConversationModal.svelte +75 -0
  123. package/src/ruvocal/src/lib/components/EditConversationModal.svelte +100 -0
  124. package/src/ruvocal/src/lib/components/ExpandNavigation.svelte +22 -0
  125. package/src/ruvocal/src/lib/components/FoundationBackground.svelte +242 -0
  126. package/src/ruvocal/src/lib/components/HoverTooltip.svelte +44 -0
  127. package/src/ruvocal/src/lib/components/HtmlPreviewModal.svelte +143 -0
  128. package/src/ruvocal/src/lib/components/InfiniteScroll.svelte +50 -0
  129. package/src/ruvocal/src/lib/components/MobileNav.svelte +300 -0
  130. package/src/ruvocal/src/lib/components/Modal.svelte +115 -0
  131. package/src/ruvocal/src/lib/components/ModelCardMetadata.svelte +71 -0
  132. package/src/ruvocal/src/lib/components/NavConversationItem.svelte +151 -0
  133. package/src/ruvocal/src/lib/components/NavMenu.svelte +313 -0
  134. package/src/ruvocal/src/lib/components/Pagination.svelte +97 -0
  135. package/src/ruvocal/src/lib/components/PaginationArrow.svelte +27 -0
  136. package/src/ruvocal/src/lib/components/Portal.svelte +24 -0
  137. package/src/ruvocal/src/lib/components/RetryBtn.svelte +18 -0
  138. package/src/ruvocal/src/lib/components/RuFloUniverse.svelte +185 -0
  139. package/src/ruvocal/src/lib/components/RufloHelpModal.svelte +411 -0
  140. package/src/ruvocal/src/lib/components/ScrollToBottomBtn.svelte +47 -0
  141. package/src/ruvocal/src/lib/components/ScrollToPreviousBtn.svelte +77 -0
  142. package/src/ruvocal/src/lib/components/ShareConversationModal.svelte +182 -0
  143. package/src/ruvocal/src/lib/components/StopGeneratingBtn.svelte +69 -0
  144. package/src/ruvocal/src/lib/components/SubscribeModal.svelte +87 -0
  145. package/src/ruvocal/src/lib/components/Switch.svelte +36 -0
  146. package/src/ruvocal/src/lib/components/SystemPromptModal.svelte +44 -0
  147. package/src/ruvocal/src/lib/components/Toast.svelte +27 -0
  148. package/src/ruvocal/src/lib/components/Tooltip.svelte +30 -0
  149. package/src/ruvocal/src/lib/components/WelcomeModal.svelte +46 -0
  150. package/src/ruvocal/src/lib/components/chat/Alternatives.svelte +77 -0
  151. package/src/ruvocal/src/lib/components/chat/BlockWrapper.svelte +72 -0
  152. package/src/ruvocal/src/lib/components/chat/ChatInput.svelte +490 -0
  153. package/src/ruvocal/src/lib/components/chat/ChatIntroduction.svelte +123 -0
  154. package/src/ruvocal/src/lib/components/chat/ChatMessage.svelte +548 -0
  155. package/src/ruvocal/src/lib/components/chat/ChatWindow.svelte +1057 -0
  156. package/src/ruvocal/src/lib/components/chat/FileDropzone.svelte +92 -0
  157. package/src/ruvocal/src/lib/components/chat/ImageLightbox.svelte +66 -0
  158. package/src/ruvocal/src/lib/components/chat/MarkdownBlock.svelte +23 -0
  159. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte +69 -0
  160. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte.test.ts +58 -0
  161. package/src/ruvocal/src/lib/components/chat/MessageAvatar.svelte +103 -0
  162. package/src/ruvocal/src/lib/components/chat/ModelSwitch.svelte +64 -0
  163. package/src/ruvocal/src/lib/components/chat/OpenReasoningResults.svelte +81 -0
  164. package/src/ruvocal/src/lib/components/chat/TaskGroup.svelte +88 -0
  165. package/src/ruvocal/src/lib/components/chat/ToolUpdate.svelte +273 -0
  166. package/src/ruvocal/src/lib/components/chat/UploadedFile.svelte +253 -0
  167. package/src/ruvocal/src/lib/components/chat/UrlFetchModal.svelte +203 -0
  168. package/src/ruvocal/src/lib/components/chat/VoiceRecorder.svelte +214 -0
  169. package/src/ruvocal/src/lib/components/icons/IconBurger.svelte +20 -0
  170. package/src/ruvocal/src/lib/components/icons/IconCheap.svelte +20 -0
  171. package/src/ruvocal/src/lib/components/icons/IconChevron.svelte +24 -0
  172. package/src/ruvocal/src/lib/components/icons/IconDazzled.svelte +40 -0
  173. package/src/ruvocal/src/lib/components/icons/IconFast.svelte +20 -0
  174. package/src/ruvocal/src/lib/components/icons/IconLoading.svelte +22 -0
  175. package/src/ruvocal/src/lib/components/icons/IconMCP.svelte +28 -0
  176. package/src/ruvocal/src/lib/components/icons/IconMoon.svelte +21 -0
  177. package/src/ruvocal/src/lib/components/icons/IconNew.svelte +20 -0
  178. package/src/ruvocal/src/lib/components/icons/IconOmni.svelte +90 -0
  179. package/src/ruvocal/src/lib/components/icons/IconPaperclip.svelte +24 -0
  180. package/src/ruvocal/src/lib/components/icons/IconPro.svelte +37 -0
  181. package/src/ruvocal/src/lib/components/icons/IconShare.svelte +21 -0
  182. package/src/ruvocal/src/lib/components/icons/IconSun.svelte +93 -0
  183. package/src/ruvocal/src/lib/components/icons/Logo.svelte +68 -0
  184. package/src/ruvocal/src/lib/components/icons/LogoHuggingFaceBorderless.svelte +54 -0
  185. package/src/ruvocal/src/lib/components/mcp/AddServerForm.svelte +250 -0
  186. package/src/ruvocal/src/lib/components/mcp/MCPServerManager.svelte +185 -0
  187. package/src/ruvocal/src/lib/components/mcp/ServerCard.svelte +203 -0
  188. package/src/ruvocal/src/lib/components/players/AudioPlayer.svelte +82 -0
  189. package/src/ruvocal/src/lib/components/voice/AudioWaveform.svelte +96 -0
  190. package/src/ruvocal/src/lib/components/wasm/GalleryPanel.svelte +357 -0
  191. package/src/ruvocal/src/lib/constants/mcpExamples.ts +114 -0
  192. package/src/ruvocal/src/lib/constants/mime.ts +11 -0
  193. package/src/ruvocal/src/lib/constants/pagination.ts +1 -0
  194. package/src/ruvocal/src/lib/constants/publicSepToken.ts +1 -0
  195. package/src/ruvocal/src/lib/constants/routerExamples.ts +133 -0
  196. package/src/ruvocal/src/lib/constants/rvagentPresets.ts +206 -0
  197. package/src/ruvocal/src/lib/createShareLink.ts +27 -0
  198. package/src/ruvocal/src/lib/jobs/refresh-conversation-stats.ts +297 -0
  199. package/src/ruvocal/src/lib/migrations/lock.ts +56 -0
  200. package/src/ruvocal/src/lib/migrations/migrations.spec.ts +74 -0
  201. package/src/ruvocal/src/lib/migrations/migrations.ts +109 -0
  202. package/src/ruvocal/src/lib/migrations/routines/01-update-search-assistants.ts +50 -0
  203. package/src/ruvocal/src/lib/migrations/routines/02-update-assistants-models.ts +48 -0
  204. package/src/ruvocal/src/lib/migrations/routines/04-update-message-updates.ts +151 -0
  205. package/src/ruvocal/src/lib/migrations/routines/05-update-message-files.ts +56 -0
  206. package/src/ruvocal/src/lib/migrations/routines/06-trim-message-updates.ts +56 -0
  207. package/src/ruvocal/src/lib/migrations/routines/08-update-featured-to-review.ts +32 -0
  208. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.spec.ts +214 -0
  209. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.ts +88 -0
  210. package/src/ruvocal/src/lib/migrations/routines/10-update-reports-assistantid.ts +29 -0
  211. package/src/ruvocal/src/lib/migrations/routines/index.ts +15 -0
  212. package/src/ruvocal/src/lib/server/__tests__/conversation-stop-generating.spec.ts +103 -0
  213. package/src/ruvocal/src/lib/server/abortRegistry.ts +57 -0
  214. package/src/ruvocal/src/lib/server/abortedGenerations.ts +43 -0
  215. package/src/ruvocal/src/lib/server/adminToken.ts +62 -0
  216. package/src/ruvocal/src/lib/server/api/__tests__/conversations-id.spec.ts +296 -0
  217. package/src/ruvocal/src/lib/server/api/__tests__/conversations-message.spec.ts +216 -0
  218. package/src/ruvocal/src/lib/server/api/__tests__/conversations.spec.ts +235 -0
  219. package/src/ruvocal/src/lib/server/api/__tests__/misc.spec.ts +72 -0
  220. package/src/ruvocal/src/lib/server/api/__tests__/testHelpers.ts +86 -0
  221. package/src/ruvocal/src/lib/server/api/__tests__/user-reports.spec.ts +78 -0
  222. package/src/ruvocal/src/lib/server/api/__tests__/user.spec.ts +239 -0
  223. package/src/ruvocal/src/lib/server/api/types.ts +37 -0
  224. package/src/ruvocal/src/lib/server/api/utils/requireAuth.ts +22 -0
  225. package/src/ruvocal/src/lib/server/api/utils/resolveConversation.ts +69 -0
  226. package/src/ruvocal/src/lib/server/api/utils/resolveModel.ts +27 -0
  227. package/src/ruvocal/src/lib/server/api/utils/superjsonResponse.ts +15 -0
  228. package/src/ruvocal/src/lib/server/apiToken.ts +11 -0
  229. package/src/ruvocal/src/lib/server/auth.ts +554 -0
  230. package/src/ruvocal/src/lib/server/config.ts +187 -0
  231. package/src/ruvocal/src/lib/server/conversation.ts +83 -0
  232. package/src/ruvocal/src/lib/server/database/__tests__/rvf.spec.ts +709 -0
  233. package/src/ruvocal/src/lib/server/database/postgres.ts +700 -0
  234. package/src/ruvocal/src/lib/server/database/rvf.ts +1078 -0
  235. package/src/ruvocal/src/lib/server/database.ts +145 -0
  236. package/src/ruvocal/src/lib/server/endpoints/document.ts +68 -0
  237. package/src/ruvocal/src/lib/server/endpoints/endpoints.ts +43 -0
  238. package/src/ruvocal/src/lib/server/endpoints/images.ts +211 -0
  239. package/src/ruvocal/src/lib/server/endpoints/openai/endpointOai.ts +266 -0
  240. package/src/ruvocal/src/lib/server/endpoints/openai/openAIChatToTextGenerationStream.ts +212 -0
  241. package/src/ruvocal/src/lib/server/endpoints/openai/openAICompletionToTextGenerationStream.ts +32 -0
  242. package/src/ruvocal/src/lib/server/endpoints/preprocessMessages.ts +61 -0
  243. package/src/ruvocal/src/lib/server/exitHandler.ts +59 -0
  244. package/src/ruvocal/src/lib/server/files/downloadFile.ts +34 -0
  245. package/src/ruvocal/src/lib/server/files/uploadFile.ts +29 -0
  246. package/src/ruvocal/src/lib/server/findRepoRoot.ts +13 -0
  247. package/src/ruvocal/src/lib/server/fonts/Inter-Black.ttf +0 -0
  248. package/src/ruvocal/src/lib/server/fonts/Inter-Bold.ttf +0 -0
  249. package/src/ruvocal/src/lib/server/fonts/Inter-ExtraBold.ttf +0 -0
  250. package/src/ruvocal/src/lib/server/fonts/Inter-ExtraLight.ttf +0 -0
  251. package/src/ruvocal/src/lib/server/fonts/Inter-Light.ttf +0 -0
  252. package/src/ruvocal/src/lib/server/fonts/Inter-Medium.ttf +0 -0
  253. package/src/ruvocal/src/lib/server/fonts/Inter-Regular.ttf +0 -0
  254. package/src/ruvocal/src/lib/server/fonts/Inter-SemiBold.ttf +0 -0
  255. package/src/ruvocal/src/lib/server/fonts/Inter-Thin.ttf +0 -0
  256. package/src/ruvocal/src/lib/server/generateFromDefaultEndpoint.ts +46 -0
  257. package/src/ruvocal/src/lib/server/hooks/error.ts +37 -0
  258. package/src/ruvocal/src/lib/server/hooks/fetch.ts +22 -0
  259. package/src/ruvocal/src/lib/server/hooks/handle.ts +250 -0
  260. package/src/ruvocal/src/lib/server/hooks/init.ts +51 -0
  261. package/src/ruvocal/src/lib/server/isURLLocal.spec.ts +31 -0
  262. package/src/ruvocal/src/lib/server/isURLLocal.ts +74 -0
  263. package/src/ruvocal/src/lib/server/logger.ts +42 -0
  264. package/src/ruvocal/src/lib/server/mcp/clientPool.spec.ts +175 -0
  265. package/src/ruvocal/src/lib/server/mcp/clientPool.ts +0 -0
  266. package/src/ruvocal/src/lib/server/mcp/hf.ts +32 -0
  267. package/src/ruvocal/src/lib/server/mcp/httpClient.ts +122 -0
  268. package/src/ruvocal/src/lib/server/mcp/registry.ts +76 -0
  269. package/src/ruvocal/src/lib/server/mcp/tools.ts +196 -0
  270. package/src/ruvocal/src/lib/server/metrics.ts +255 -0
  271. package/src/ruvocal/src/lib/server/models.ts +518 -0
  272. package/src/ruvocal/src/lib/server/requestContext.ts +55 -0
  273. package/src/ruvocal/src/lib/server/router/arch.ts +230 -0
  274. package/src/ruvocal/src/lib/server/router/endpoint.ts +316 -0
  275. package/src/ruvocal/src/lib/server/router/multimodal.ts +28 -0
  276. package/src/ruvocal/src/lib/server/router/policy.ts +49 -0
  277. package/src/ruvocal/src/lib/server/router/toolsRoute.ts +51 -0
  278. package/src/ruvocal/src/lib/server/router/types.ts +21 -0
  279. package/src/ruvocal/src/lib/server/sendSlack.ts +23 -0
  280. package/src/ruvocal/src/lib/server/textGeneration/generate.ts +258 -0
  281. package/src/ruvocal/src/lib/server/textGeneration/index.ts +96 -0
  282. package/src/ruvocal/src/lib/server/textGeneration/mcp/fileRefs.ts +155 -0
  283. package/src/ruvocal/src/lib/server/textGeneration/mcp/routerResolution.ts +108 -0
  284. package/src/ruvocal/src/lib/server/textGeneration/mcp/runMcpFlow.ts +831 -0
  285. package/src/ruvocal/src/lib/server/textGeneration/mcp/toolInvocation.ts +349 -0
  286. package/src/ruvocal/src/lib/server/textGeneration/mcp/wasmTools.test.ts +633 -0
  287. package/src/ruvocal/src/lib/server/textGeneration/reasoning.ts +23 -0
  288. package/src/ruvocal/src/lib/server/textGeneration/title.ts +83 -0
  289. package/src/ruvocal/src/lib/server/textGeneration/types.ts +28 -0
  290. package/src/ruvocal/src/lib/server/textGeneration/utils/prepareFiles.ts +88 -0
  291. package/src/ruvocal/src/lib/server/textGeneration/utils/routing.ts +21 -0
  292. package/src/ruvocal/src/lib/server/textGeneration/utils/toolPrompt.ts +49 -0
  293. package/src/ruvocal/src/lib/server/urlSafety.ts +77 -0
  294. package/src/ruvocal/src/lib/server/usageLimits.ts +30 -0
  295. package/src/ruvocal/src/lib/stores/autopilotStore.svelte.ts +175 -0
  296. package/src/ruvocal/src/lib/stores/backgroundGenerations.svelte.ts +32 -0
  297. package/src/ruvocal/src/lib/stores/backgroundGenerations.ts +1 -0
  298. package/src/ruvocal/src/lib/stores/errors.ts +9 -0
  299. package/src/ruvocal/src/lib/stores/isAborted.ts +3 -0
  300. package/src/ruvocal/src/lib/stores/isPro.ts +4 -0
  301. package/src/ruvocal/src/lib/stores/loading.ts +3 -0
  302. package/src/ruvocal/src/lib/stores/mcpServers.ts +534 -0
  303. package/src/ruvocal/src/lib/stores/pendingChatInput.ts +3 -0
  304. package/src/ruvocal/src/lib/stores/pendingMessage.ts +9 -0
  305. package/src/ruvocal/src/lib/stores/settings.ts +182 -0
  306. package/src/ruvocal/src/lib/stores/shareModal.ts +13 -0
  307. package/src/ruvocal/src/lib/stores/titleUpdate.ts +8 -0
  308. package/src/ruvocal/src/lib/stores/wasmMcp.ts +472 -0
  309. package/src/ruvocal/src/lib/switchTheme.ts +124 -0
  310. package/src/ruvocal/src/lib/types/AbortedGeneration.ts +8 -0
  311. package/src/ruvocal/src/lib/types/Assistant.ts +31 -0
  312. package/src/ruvocal/src/lib/types/AssistantStats.ts +11 -0
  313. package/src/ruvocal/src/lib/types/ConfigKey.ts +4 -0
  314. package/src/ruvocal/src/lib/types/ConvSidebar.ts +9 -0
  315. package/src/ruvocal/src/lib/types/Conversation.ts +27 -0
  316. package/src/ruvocal/src/lib/types/ConversationStats.ts +13 -0
  317. package/src/ruvocal/src/lib/types/Message.ts +41 -0
  318. package/src/ruvocal/src/lib/types/MessageEvent.ts +10 -0
  319. package/src/ruvocal/src/lib/types/MessageUpdate.ts +139 -0
  320. package/src/ruvocal/src/lib/types/MigrationResult.ts +7 -0
  321. package/src/ruvocal/src/lib/types/Model.ts +23 -0
  322. package/src/ruvocal/src/lib/types/Report.ts +12 -0
  323. package/src/ruvocal/src/lib/types/Review.ts +6 -0
  324. package/src/ruvocal/src/lib/types/Semaphore.ts +19 -0
  325. package/src/ruvocal/src/lib/types/Session.ts +22 -0
  326. package/src/ruvocal/src/lib/types/Settings.ts +93 -0
  327. package/src/ruvocal/src/lib/types/SharedConversation.ts +9 -0
  328. package/src/ruvocal/src/lib/types/Template.ts +6 -0
  329. package/src/ruvocal/src/lib/types/Timestamps.ts +4 -0
  330. package/src/ruvocal/src/lib/types/TokenCache.ts +6 -0
  331. package/src/ruvocal/src/lib/types/Tool.ts +77 -0
  332. package/src/ruvocal/src/lib/types/UrlDependency.ts +5 -0
  333. package/src/ruvocal/src/lib/types/User.ts +14 -0
  334. package/src/ruvocal/src/lib/utils/PublicConfig.svelte.ts +75 -0
  335. package/src/ruvocal/src/lib/utils/auth.ts +17 -0
  336. package/src/ruvocal/src/lib/utils/chunk.ts +33 -0
  337. package/src/ruvocal/src/lib/utils/cookiesAreEnabled.ts +13 -0
  338. package/src/ruvocal/src/lib/utils/debounce.ts +17 -0
  339. package/src/ruvocal/src/lib/utils/deepestChild.ts +6 -0
  340. package/src/ruvocal/src/lib/utils/favicon.ts +21 -0
  341. package/src/ruvocal/src/lib/utils/fetchJSON.ts +23 -0
  342. package/src/ruvocal/src/lib/utils/file2base64.ts +14 -0
  343. package/src/ruvocal/src/lib/utils/formatUserCount.ts +37 -0
  344. package/src/ruvocal/src/lib/utils/generationState.spec.ts +75 -0
  345. package/src/ruvocal/src/lib/utils/generationState.ts +26 -0
  346. package/src/ruvocal/src/lib/utils/getHref.ts +41 -0
  347. package/src/ruvocal/src/lib/utils/getReturnFromGenerator.ts +7 -0
  348. package/src/ruvocal/src/lib/utils/haptics.ts +64 -0
  349. package/src/ruvocal/src/lib/utils/hashConv.ts +12 -0
  350. package/src/ruvocal/src/lib/utils/hf.ts +17 -0
  351. package/src/ruvocal/src/lib/utils/isDesktop.ts +7 -0
  352. package/src/ruvocal/src/lib/utils/isUrl.ts +8 -0
  353. package/src/ruvocal/src/lib/utils/isVirtualKeyboard.ts +16 -0
  354. package/src/ruvocal/src/lib/utils/loadAttachmentsFromUrls.ts +115 -0
  355. package/src/ruvocal/src/lib/utils/marked.spec.ts +96 -0
  356. package/src/ruvocal/src/lib/utils/marked.ts +531 -0
  357. package/src/ruvocal/src/lib/utils/mcpValidation.ts +147 -0
  358. package/src/ruvocal/src/lib/utils/mergeAsyncGenerators.ts +38 -0
  359. package/src/ruvocal/src/lib/utils/messageUpdates.spec.ts +262 -0
  360. package/src/ruvocal/src/lib/utils/messageUpdates.ts +324 -0
  361. package/src/ruvocal/src/lib/utils/mime.ts +56 -0
  362. package/src/ruvocal/src/lib/utils/models.ts +14 -0
  363. package/src/ruvocal/src/lib/utils/parseBlocks.ts +120 -0
  364. package/src/ruvocal/src/lib/utils/parseIncompleteMarkdown.ts +644 -0
  365. package/src/ruvocal/src/lib/utils/parseStringToList.ts +10 -0
  366. package/src/ruvocal/src/lib/utils/randomUuid.ts +14 -0
  367. package/src/ruvocal/src/lib/utils/searchTokens.ts +33 -0
  368. package/src/ruvocal/src/lib/utils/sha256.ts +7 -0
  369. package/src/ruvocal/src/lib/utils/stringifyError.ts +12 -0
  370. package/src/ruvocal/src/lib/utils/sum.ts +3 -0
  371. package/src/ruvocal/src/lib/utils/template.spec.ts +59 -0
  372. package/src/ruvocal/src/lib/utils/template.ts +53 -0
  373. package/src/ruvocal/src/lib/utils/timeout.ts +9 -0
  374. package/src/ruvocal/src/lib/utils/toolProgress.spec.ts +46 -0
  375. package/src/ruvocal/src/lib/utils/toolProgress.ts +11 -0
  376. package/src/ruvocal/src/lib/utils/tree/addChildren.spec.ts +102 -0
  377. package/src/ruvocal/src/lib/utils/tree/addChildren.ts +48 -0
  378. package/src/ruvocal/src/lib/utils/tree/addSibling.spec.ts +81 -0
  379. package/src/ruvocal/src/lib/utils/tree/addSibling.ts +41 -0
  380. package/src/ruvocal/src/lib/utils/tree/buildSubtree.spec.ts +110 -0
  381. package/src/ruvocal/src/lib/utils/tree/buildSubtree.ts +24 -0
  382. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.spec.ts +31 -0
  383. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.ts +36 -0
  384. package/src/ruvocal/src/lib/utils/tree/isMessageId.spec.ts +15 -0
  385. package/src/ruvocal/src/lib/utils/tree/isMessageId.ts +5 -0
  386. package/src/ruvocal/src/lib/utils/tree/tree.d.ts +14 -0
  387. package/src/ruvocal/src/lib/utils/tree/treeHelpers.spec.ts +167 -0
  388. package/src/ruvocal/src/lib/utils/updates.ts +39 -0
  389. package/src/ruvocal/src/lib/utils/urlParams.ts +13 -0
  390. package/src/ruvocal/src/lib/wasm/idb.ts +438 -0
  391. package/src/ruvocal/src/lib/wasm/index.ts +1213 -0
  392. package/src/ruvocal/src/lib/wasm/tests/wasm-capabilities.test.ts +565 -0
  393. package/src/ruvocal/src/lib/wasm/wasm.worker.ts +332 -0
  394. package/src/ruvocal/src/lib/wasm/workerClient.ts +166 -0
  395. package/src/ruvocal/src/lib/workers/autopilotWorker.ts +221 -0
  396. package/src/ruvocal/src/lib/workers/detailFetchWorker.ts +100 -0
  397. package/src/ruvocal/src/lib/workers/markdownWorker.ts +61 -0
  398. package/src/ruvocal/src/routes/+error.svelte +20 -0
  399. package/src/ruvocal/src/routes/+layout.svelte +324 -0
  400. package/src/ruvocal/src/routes/+layout.ts +91 -0
  401. package/src/ruvocal/src/routes/+page.svelte +168 -0
  402. package/src/ruvocal/src/routes/.well-known/oauth-cimd/+server.ts +37 -0
  403. package/src/ruvocal/src/routes/__debug/openai/+server.ts +21 -0
  404. package/src/ruvocal/src/routes/admin/export/+server.ts +159 -0
  405. package/src/ruvocal/src/routes/admin/stats/compute/+server.ts +16 -0
  406. package/src/ruvocal/src/routes/api/conversation/[id]/+server.ts +40 -0
  407. package/src/ruvocal/src/routes/api/conversation/[id]/message/[messageId]/+server.ts +42 -0
  408. package/src/ruvocal/src/routes/api/conversations/+server.ts +48 -0
  409. package/src/ruvocal/src/routes/api/fetch-url/+server.ts +147 -0
  410. package/src/ruvocal/src/routes/api/mcp/health/+server.ts +292 -0
  411. package/src/ruvocal/src/routes/api/mcp/servers/+server.ts +32 -0
  412. package/src/ruvocal/src/routes/api/models/+server.ts +25 -0
  413. package/src/ruvocal/src/routes/api/transcribe/+server.ts +104 -0
  414. package/src/ruvocal/src/routes/api/user/+server.ts +15 -0
  415. package/src/ruvocal/src/routes/api/user/validate-token/+server.ts +20 -0
  416. package/src/ruvocal/src/routes/api/v2/conversations/+server.ts +48 -0
  417. package/src/ruvocal/src/routes/api/v2/conversations/[id]/+server.ts +94 -0
  418. package/src/ruvocal/src/routes/api/v2/conversations/[id]/message/[messageId]/+server.ts +43 -0
  419. package/src/ruvocal/src/routes/api/v2/conversations/import-share/+server.ts +23 -0
  420. package/src/ruvocal/src/routes/api/v2/debug/config/+server.ts +16 -0
  421. package/src/ruvocal/src/routes/api/v2/debug/refresh/+server.ts +30 -0
  422. package/src/ruvocal/src/routes/api/v2/export/+server.ts +196 -0
  423. package/src/ruvocal/src/routes/api/v2/feature-flags/+server.ts +14 -0
  424. package/src/ruvocal/src/routes/api/v2/models/+server.ts +38 -0
  425. package/src/ruvocal/src/routes/api/v2/models/[namespace]/+server.ts +8 -0
  426. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/+server.ts +8 -0
  427. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/subscribe/+server.ts +28 -0
  428. package/src/ruvocal/src/routes/api/v2/models/[namespace]/subscribe/+server.ts +28 -0
  429. package/src/ruvocal/src/routes/api/v2/models/old/+server.ts +7 -0
  430. package/src/ruvocal/src/routes/api/v2/models/refresh/+server.ts +33 -0
  431. package/src/ruvocal/src/routes/api/v2/public-config/+server.ts +7 -0
  432. package/src/ruvocal/src/routes/api/v2/user/+server.ts +17 -0
  433. package/src/ruvocal/src/routes/api/v2/user/billing-orgs/+server.ts +73 -0
  434. package/src/ruvocal/src/routes/api/v2/user/reports/+server.ts +17 -0
  435. package/src/ruvocal/src/routes/api/v2/user/settings/+server.ts +110 -0
  436. package/src/ruvocal/src/routes/conversation/+server.ts +115 -0
  437. package/src/ruvocal/src/routes/conversation/[id]/+page.svelte +586 -0
  438. package/src/ruvocal/src/routes/conversation/[id]/+page.ts +60 -0
  439. package/src/ruvocal/src/routes/conversation/[id]/+server.ts +740 -0
  440. package/src/ruvocal/src/routes/conversation/[id]/message/[messageId]/prompt/+server.ts +66 -0
  441. package/src/ruvocal/src/routes/conversation/[id]/share/+server.ts +69 -0
  442. package/src/ruvocal/src/routes/conversation/[id]/stop-generating/+server.ts +35 -0
  443. package/src/ruvocal/src/routes/healthcheck/+server.ts +3 -0
  444. package/src/ruvocal/src/routes/login/+server.ts +5 -0
  445. package/src/ruvocal/src/routes/login/callback/+server.ts +103 -0
  446. package/src/ruvocal/src/routes/login/callback/updateUser.spec.ts +157 -0
  447. package/src/ruvocal/src/routes/login/callback/updateUser.ts +215 -0
  448. package/src/ruvocal/src/routes/logout/+server.ts +18 -0
  449. package/src/ruvocal/src/routes/metrics/+server.ts +18 -0
  450. package/src/ruvocal/src/routes/models/+page.svelte +233 -0
  451. package/src/ruvocal/src/routes/models/[...model]/+page.svelte +161 -0
  452. package/src/ruvocal/src/routes/models/[...model]/+page.ts +14 -0
  453. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/+server.ts +64 -0
  454. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte +28 -0
  455. package/src/ruvocal/src/routes/privacy/+page.svelte +11 -0
  456. package/src/ruvocal/src/routes/r/[id]/+page.ts +34 -0
  457. package/src/ruvocal/src/routes/settings/(nav)/+layout.svelte +282 -0
  458. package/src/ruvocal/src/routes/settings/(nav)/+layout.ts +1 -0
  459. package/src/ruvocal/src/routes/settings/(nav)/+page.svelte +0 -0
  460. package/src/ruvocal/src/routes/settings/(nav)/+server.ts +59 -0
  461. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.svelte +464 -0
  462. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.ts +14 -0
  463. package/src/ruvocal/src/routes/settings/(nav)/application/+page.svelte +362 -0
  464. package/src/ruvocal/src/routes/settings/+layout.svelte +40 -0
  465. package/src/ruvocal/src/styles/highlight-js.css +195 -0
  466. package/src/ruvocal/src/styles/main.css +144 -0
  467. package/src/ruvocal/static/chatui/apple-touch-icon.png +0 -0
  468. package/src/ruvocal/static/chatui/favicon-dark.svg +3 -0
  469. package/src/ruvocal/static/chatui/favicon-dev.svg +3 -0
  470. package/src/ruvocal/static/chatui/favicon.ico +0 -0
  471. package/src/ruvocal/static/chatui/favicon.svg +3 -0
  472. package/src/ruvocal/static/chatui/icon-128x128.png +0 -0
  473. package/src/ruvocal/static/chatui/icon-144x144.png +0 -0
  474. package/src/ruvocal/static/chatui/icon-192x192.png +0 -0
  475. package/src/ruvocal/static/chatui/icon-256x256.png +0 -0
  476. package/src/ruvocal/static/chatui/icon-36x36.png +0 -0
  477. package/src/ruvocal/static/chatui/icon-48x48.png +0 -0
  478. package/src/ruvocal/static/chatui/icon-512x512.png +0 -0
  479. package/src/ruvocal/static/chatui/icon-72x72.png +0 -0
  480. package/src/ruvocal/static/chatui/icon-96x96.png +0 -0
  481. package/src/ruvocal/static/chatui/icon.svg +3 -0
  482. package/src/ruvocal/static/chatui/logo.svg +7 -0
  483. package/src/ruvocal/static/chatui/manifest.json +54 -0
  484. package/src/ruvocal/static/chatui/omni-welcome.gif +0 -0
  485. package/src/ruvocal/static/chatui/omni-welcome.png +0 -0
  486. package/src/ruvocal/static/chatui/welcome.js +184 -0
  487. package/src/ruvocal/static/chatui/welcome.svg +1 -0
  488. package/src/ruvocal/static/huggingchat/apple-touch-icon.png +0 -0
  489. package/src/ruvocal/static/huggingchat/assistants-thumbnail.png +0 -0
  490. package/src/ruvocal/static/huggingchat/castle-example.jpg +0 -0
  491. package/src/ruvocal/static/huggingchat/favicon-dark.svg +4 -0
  492. package/src/ruvocal/static/huggingchat/favicon-dev.svg +4 -0
  493. package/src/ruvocal/static/huggingchat/favicon.ico +0 -0
  494. package/src/ruvocal/static/huggingchat/favicon.svg +4 -0
  495. package/src/ruvocal/static/huggingchat/fulltext-logo.svg +2 -0
  496. package/src/ruvocal/static/huggingchat/icon-128x128.png +0 -0
  497. package/src/ruvocal/static/huggingchat/icon-144x144.png +0 -0
  498. package/src/ruvocal/static/huggingchat/icon-192x192.png +0 -0
  499. package/src/ruvocal/static/huggingchat/icon-256x256.png +0 -0
  500. package/src/ruvocal/static/huggingchat/icon-36x36.png +0 -0
  501. package/src/ruvocal/static/huggingchat/icon-48x48.png +0 -0
  502. package/src/ruvocal/static/huggingchat/icon-512x512.png +0 -0
  503. package/src/ruvocal/static/huggingchat/icon-72x72.png +0 -0
  504. package/src/ruvocal/static/huggingchat/icon-96x96.png +0 -0
  505. package/src/ruvocal/static/huggingchat/icon.svg +4 -0
  506. package/src/ruvocal/static/huggingchat/logo.svg +4 -0
  507. package/src/ruvocal/static/huggingchat/manifest.json +54 -0
  508. package/src/ruvocal/static/huggingchat/omni-welcome.gif +0 -0
  509. package/src/ruvocal/static/huggingchat/routes.chat.json +226 -0
  510. package/src/ruvocal/static/huggingchat/thumbnail.png +0 -0
  511. package/src/ruvocal/static/huggingchat/tools-thumbnail.png +0 -0
  512. package/src/ruvocal/static/robots.txt +10 -0
  513. package/src/ruvocal/static/wasm/rvagent_wasm.js +1539 -0
  514. package/src/ruvocal/static/wasm/rvagent_wasm_bg.wasm +0 -0
  515. package/src/ruvocal/stub/@reflink/reflink/index.js +0 -0
  516. package/src/ruvocal/stub/@reflink/reflink/package.json +5 -0
  517. package/src/ruvocal/svelte.config.js +53 -0
  518. package/src/ruvocal/tailwind.config.cjs +30 -0
  519. package/src/ruvocal/tsconfig.json +19 -0
  520. package/src/ruvocal/vite.config.ts +87 -0
  521. package/src/scripts/deploy.sh +116 -0
  522. package/src/scripts/generate-config.js +245 -0
  523. package/src/scripts/generate-welcome.js +187 -0
  524. package/src/scripts/package-rvf.sh +116 -0
@@ -0,0 +1,230 @@
1
+ import { config } from "$lib/server/config";
2
+ import { logger } from "$lib/server/logger";
3
+ import type { EndpointMessage } from "../endpoints/endpoints";
4
+ import type { Route, RouteConfig, RouteSelection } from "./types";
5
+ import { getRoutes } from "./policy";
6
+ import { getApiToken } from "$lib/server/apiToken";
7
+
8
+ const DEFAULT_LAST_TURNS = 16;
9
+
10
+ /**
11
+ * Trim a message by keeping start and end, replacing middle with minimal indicator.
12
+ * Uses simple ellipsis since router only needs context for intent classification, not exact content.
13
+ * @param content - The message content to trim
14
+ * @param maxLength - Maximum total length (including indicator)
15
+ * @returns Trimmed content with start, ellipsis, and end
16
+ */
17
+ function trimMiddle(content: string, maxLength: number): string {
18
+ if (content.length <= maxLength) return content;
19
+
20
+ const indicator = "…";
21
+ const availableLength = maxLength - indicator.length;
22
+
23
+ if (availableLength <= 0) {
24
+ // If no room even for indicator, just hard truncate
25
+ return content.slice(0, maxLength);
26
+ }
27
+
28
+ // Reserve more space for the start (typically contains context)
29
+ const startLength = Math.ceil(availableLength * 0.6);
30
+ const endLength = availableLength - startLength;
31
+
32
+ // Bug fix: slice(-0) returns entire string, so check for endLength <= 0
33
+ if (endLength <= 0) {
34
+ // Not enough space for end portion, just use start + indicator
35
+ return content.slice(0, availableLength) + indicator;
36
+ }
37
+
38
+ const start = content.slice(0, startLength);
39
+ const end = content.slice(-endLength);
40
+
41
+ return start + indicator + end;
42
+ }
43
+
44
+ const PROMPT_TEMPLATE = `
45
+ You are a helpful assistant designed to find the best suited route.
46
+ You are provided with route description within <routes></routes> XML tags:
47
+
48
+ <routes>
49
+
50
+ {routes}
51
+
52
+ </routes>
53
+
54
+ <conversation>
55
+
56
+ {conversation}
57
+
58
+ </conversation>
59
+
60
+ Your task is to decide which route is best suit with user intent on the conversation in <conversation></conversation> XML tags.
61
+
62
+ Follow those instructions:
63
+ 1. Use prior turns to choose the best route for the current message if needed.
64
+ 2. If no route match the full conversation respond with other route {"route": "other"}.
65
+ 3. Analyze the route descriptions and find the best match route for user latest intent.
66
+ 4. Respond only with the route name that best matches the user's request, using the exact name in the <routes> block.
67
+ Based on your analysis, provide your response in the following JSON format if you decide to match any route:
68
+ {"route": "route_name"}
69
+ `.trim();
70
+
71
+ function lastNTurns<T>(arr: T[], n = DEFAULT_LAST_TURNS) {
72
+ if (!Array.isArray(arr)) return [] as T[];
73
+ return arr.slice(-n);
74
+ }
75
+
76
+ function toRouterPrompt(messages: EndpointMessage[], routes: Route[]) {
77
+ const simpleRoutes: RouteConfig[] = routes.map((r) => ({
78
+ name: r.name,
79
+ description: r.description,
80
+ }));
81
+ const maxAssistantLength = parseInt(config.LLM_ROUTER_MAX_ASSISTANT_LENGTH || "1000", 10);
82
+ const maxPrevUserLength = parseInt(config.LLM_ROUTER_MAX_PREV_USER_LENGTH || "1000", 10);
83
+
84
+ const convo = messages
85
+ .map((m) => ({ role: m.from, content: m.content }))
86
+ .filter((m) => typeof m.content === "string" && m.content.trim() !== "");
87
+
88
+ // Find the last user message index to preserve its full content
89
+ const lastUserIndex = convo.findLastIndex((m) => m.role === "user");
90
+
91
+ const trimmedConvo = convo.map((m, idx) => {
92
+ if (typeof m.content !== "string") return m;
93
+
94
+ // Trim assistant messages to reduce routing prompt size and improve latency
95
+ // Keep start and end for better context understanding
96
+ if (m.role === "assistant") {
97
+ return {
98
+ ...m,
99
+ content: trimMiddle(m.content, maxAssistantLength),
100
+ };
101
+ }
102
+
103
+ // Trim previous user messages, but keep the latest user message full
104
+ // Keep start and end to preserve both context and question
105
+ if (m.role === "user" && idx !== lastUserIndex) {
106
+ return {
107
+ ...m,
108
+ content: trimMiddle(m.content, maxPrevUserLength),
109
+ };
110
+ }
111
+
112
+ return m;
113
+ });
114
+
115
+ return PROMPT_TEMPLATE.replace("{routes}", JSON.stringify(simpleRoutes)).replace(
116
+ "{conversation}",
117
+ JSON.stringify(lastNTurns(trimmedConvo))
118
+ );
119
+ }
120
+
121
+ function parseRouteName(text: string): string | undefined {
122
+ if (!text) return;
123
+ try {
124
+ const obj = JSON.parse(text);
125
+ if (typeof obj?.route === "string" && obj.route.trim()) return obj.route.trim();
126
+ } catch {}
127
+ const m = text.match(/["']route["']\s*:\s*["']([^"']+)["']/);
128
+ if (m?.[1]) return m[1].trim();
129
+ try {
130
+ const obj = JSON.parse(text.replace(/'/g, '"'));
131
+ if (typeof obj?.route === "string" && obj.route.trim()) return obj.route.trim();
132
+ } catch {}
133
+ return;
134
+ }
135
+
136
+ export async function archSelectRoute(
137
+ messages: EndpointMessage[],
138
+ traceId: string | undefined,
139
+ locals: App.Locals | undefined
140
+ ): Promise<RouteSelection> {
141
+ const routes = await getRoutes();
142
+ const prompt = toRouterPrompt(messages, routes);
143
+
144
+ const baseURL = (config.LLM_ROUTER_ARCH_BASE_URL || "").replace(/\/$/, "");
145
+ const archModel = config.LLM_ROUTER_ARCH_MODEL || "router/omni";
146
+
147
+ if (!baseURL) {
148
+ logger.warn("LLM_ROUTER_ARCH_BASE_URL not set; routing will fail over to fallback.");
149
+ return { routeName: "arch_router_failure" };
150
+ }
151
+
152
+ const headers: HeadersInit = {
153
+ Authorization: `Bearer ${getApiToken(locals)}`,
154
+ "Content-Type": "application/json",
155
+ // Bill to organization if configured (HuggingChat only)
156
+ ...(config.isHuggingChat && locals?.billingOrganization
157
+ ? { "X-HF-Bill-To": locals.billingOrganization }
158
+ : {}),
159
+ };
160
+ const body = {
161
+ model: archModel,
162
+ messages: [{ role: "user", content: prompt }],
163
+ temperature: 0,
164
+ max_tokens: 16,
165
+ stream: false,
166
+ };
167
+
168
+ const ctrl = new AbortController();
169
+ const timeoutMs = Number(config.LLM_ROUTER_ARCH_TIMEOUT_MS || 10000);
170
+ const to = setTimeout(() => ctrl.abort(), timeoutMs);
171
+
172
+ try {
173
+ const resp = await fetch(`${baseURL}/chat/completions`, {
174
+ method: "POST",
175
+ headers,
176
+ body: JSON.stringify(body),
177
+ signal: ctrl.signal,
178
+ });
179
+ clearTimeout(to);
180
+ if (!resp.ok) {
181
+ // Extract error message from response
182
+ let errorMessage = `arch-router ${resp.status}`;
183
+ try {
184
+ const errorData = await resp.json();
185
+ // Try to extract message from OpenAI-style error format
186
+ if (errorData.error?.message) {
187
+ errorMessage = errorData.error.message;
188
+ } else if (errorData.message) {
189
+ errorMessage = errorData.message;
190
+ }
191
+ } catch {
192
+ // If JSON parsing fails, use status text
193
+ errorMessage = resp.statusText || errorMessage;
194
+ }
195
+
196
+ logger.warn(
197
+ { status: resp.status, error: errorMessage, traceId },
198
+ "[arch] router returned error"
199
+ );
200
+
201
+ return {
202
+ routeName: "arch_router_failure",
203
+ error: {
204
+ message: errorMessage,
205
+ statusCode: resp.status,
206
+ },
207
+ };
208
+ }
209
+ const data: { choices: { message: { content: string } }[] } = await resp.json();
210
+ const text = (data?.choices?.[0]?.message?.content ?? "").toString().trim();
211
+ const raw = parseRouteName(text);
212
+
213
+ const other = config.LLM_ROUTER_OTHER_ROUTE || "casual_conversation";
214
+ const chosen = raw === "other" ? other : raw || "casual_conversation";
215
+ const exists = routes.some((r) => r.name === chosen);
216
+ return { routeName: exists ? chosen : "casual_conversation" };
217
+ } catch (e) {
218
+ clearTimeout(to);
219
+ const err = e as Error;
220
+ logger.warn({ err: String(e), traceId }, "arch router selection failed");
221
+
222
+ // Return error with context but no status code (network/timeout errors)
223
+ return {
224
+ routeName: "arch_router_failure",
225
+ error: {
226
+ message: err.message || String(e),
227
+ },
228
+ };
229
+ }
230
+ }
@@ -0,0 +1,316 @@
1
+ import type {
2
+ Endpoint,
3
+ EndpointParameters,
4
+ EndpointMessage,
5
+ TextGenerationStreamOutputSimplified,
6
+ } from "../endpoints/endpoints";
7
+ import endpoints from "../endpoints/endpoints";
8
+ import type { ProcessedModel } from "../models";
9
+ import { config } from "$lib/server/config";
10
+ import { logger } from "$lib/server/logger";
11
+ import { archSelectRoute } from "./arch";
12
+ import { getRoutes, resolveRouteModels } from "./policy";
13
+ import { getApiToken } from "$lib/server/apiToken";
14
+ import { ROUTER_FAILURE } from "./types";
15
+ import {
16
+ hasActiveToolsSelection,
17
+ isRouterToolsBypassEnabled,
18
+ pickToolsCapableModel,
19
+ ROUTER_TOOLS_ROUTE,
20
+ } from "./toolsRoute";
21
+ import { getConfiguredMultimodalModelId } from "./multimodal";
22
+
23
+ const REASONING_BLOCK_REGEX = /<think>[\s\S]*?(?:<\/think>|$)/g;
24
+
25
+ const ROUTER_MULTIMODAL_ROUTE = "multimodal";
26
+
27
+ // Cache models at module level to avoid redundant dynamic imports on every request
28
+ let cachedModels: ProcessedModel[] | undefined;
29
+
30
+ async function getModels(): Promise<ProcessedModel[]> {
31
+ if (!cachedModels) {
32
+ const mod = await import("../models");
33
+ cachedModels = (mod as { models: ProcessedModel[] }).models;
34
+ }
35
+ return cachedModels;
36
+ }
37
+
38
+ /**
39
+ * Custom error class that preserves HTTP status codes
40
+ */
41
+ class HTTPError extends Error {
42
+ constructor(
43
+ message: string,
44
+ public statusCode?: number
45
+ ) {
46
+ super(message);
47
+ this.name = "HTTPError";
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Extract the actual error message and status from OpenAI SDK errors or other upstream errors
53
+ */
54
+ function extractUpstreamError(error: unknown): { message: string; statusCode?: number } {
55
+ // Check if it's an OpenAI APIError with structured error info
56
+ if (error && typeof error === "object") {
57
+ const err = error as Record<string, unknown>;
58
+
59
+ // OpenAI SDK error with error.error.message and status
60
+ if (
61
+ err.error &&
62
+ typeof err.error === "object" &&
63
+ "message" in err.error &&
64
+ typeof err.error.message === "string"
65
+ ) {
66
+ return {
67
+ message: err.error.message,
68
+ statusCode: typeof err.status === "number" ? err.status : undefined,
69
+ };
70
+ }
71
+
72
+ // HTTPError or error with statusCode
73
+ if (typeof err.statusCode === "number" && typeof err.message === "string") {
74
+ return { message: err.message, statusCode: err.statusCode };
75
+ }
76
+
77
+ // Error with status field
78
+ if (typeof err.status === "number" && typeof err.message === "string") {
79
+ return { message: err.message, statusCode: err.status };
80
+ }
81
+
82
+ // Direct error message
83
+ if (typeof err.message === "string") {
84
+ return { message: err.message };
85
+ }
86
+ }
87
+
88
+ return { message: String(error) };
89
+ }
90
+
91
+ /**
92
+ * Determines if an error is a policy/entitlement error that should be shown to users immediately
93
+ * (vs transient errors that should trigger fallback)
94
+ */
95
+ function isPolicyError(statusCode?: number): boolean {
96
+ if (!statusCode) return false;
97
+ // 400: Bad Request, 402: Payment Required, 401: Unauthorized, 403: Forbidden
98
+ return statusCode === 400 || statusCode === 401 || statusCode === 402 || statusCode === 403;
99
+ }
100
+
101
+ function stripReasoningBlocks(text: string): string {
102
+ const stripped = text.replace(REASONING_BLOCK_REGEX, "");
103
+ return stripped === text ? text : stripped.trim();
104
+ }
105
+
106
+ function stripReasoningFromMessage(message: EndpointMessage): EndpointMessage {
107
+ const content =
108
+ typeof message.content === "string" ? stripReasoningBlocks(message.content) : message.content;
109
+ return {
110
+ ...message,
111
+ content,
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Create an Endpoint that performs route selection via Arch and then forwards
117
+ * to the selected model (with fallbacks) using the OpenAI-compatible endpoint.
118
+ */
119
+ export async function makeRouterEndpoint(routerModel: ProcessedModel): Promise<Endpoint> {
120
+ return async function routerEndpoint(params: EndpointParameters) {
121
+ const routes = await getRoutes();
122
+ const sanitizedMessages = params.messages.map(stripReasoningFromMessage);
123
+ const routerMultimodalEnabled =
124
+ (config.LLM_ROUTER_ENABLE_MULTIMODAL || "").toLowerCase() === "true";
125
+ const routerToolsEnabled = isRouterToolsBypassEnabled();
126
+ const hasImageInput = sanitizedMessages.some((message) =>
127
+ (message.files ?? []).some(
128
+ (file) => typeof file?.mime === "string" && file.mime.startsWith("image/")
129
+ )
130
+ );
131
+ // Tools are considered "active" if the client indicated any enabled MCP server
132
+ const hasToolsActive = hasActiveToolsSelection(params.locals);
133
+
134
+ // Helper to create an OpenAI endpoint for a specific candidate model id
135
+ async function createCandidateEndpoint(candidateModelId: string): Promise<Endpoint> {
136
+ // Try to use the real candidate model config if present in chat-ui's model list
137
+ let modelForCall: ProcessedModel | undefined;
138
+ try {
139
+ const all = await getModels();
140
+ modelForCall = all?.find((m) => m.id === candidateModelId || m.name === candidateModelId);
141
+ } catch (e) {
142
+ logger.warn({ err: String(e) }, "[router] failed to load models for candidate lookup");
143
+ }
144
+
145
+ if (!modelForCall) {
146
+ // Fallback: clone router model with candidate id
147
+ modelForCall = {
148
+ ...routerModel,
149
+ id: candidateModelId,
150
+ name: candidateModelId,
151
+ displayName: candidateModelId,
152
+ } as ProcessedModel;
153
+ }
154
+
155
+ return endpoints.openai({
156
+ type: "openai",
157
+ baseURL: (config.OPENAI_BASE_URL || "https://router.huggingface.co/v1").replace(/\/$/, ""),
158
+ apiKey: getApiToken(params.locals),
159
+ model: modelForCall,
160
+ // Ensure streaming path is used
161
+ streamingSupported: true,
162
+ });
163
+ }
164
+
165
+ // Yield router metadata for immediate UI display, using the actual candidate
166
+ async function* metadataThenStream(
167
+ gen: AsyncGenerator<TextGenerationStreamOutputSimplified>,
168
+ actualModel: string,
169
+ selectedRoute: string
170
+ ) {
171
+ yield {
172
+ token: { id: 0, text: "", special: true, logprob: 0 },
173
+ generated_text: null,
174
+ details: null,
175
+ routerMetadata: { route: selectedRoute, model: actualModel },
176
+ };
177
+ for await (const ev of gen) yield ev;
178
+ }
179
+
180
+ if (routerMultimodalEnabled && hasImageInput) {
181
+ let multimodalCandidate: string | undefined;
182
+ try {
183
+ const all = await getModels();
184
+ multimodalCandidate = getConfiguredMultimodalModelId(all);
185
+ } catch (e) {
186
+ logger.warn({ err: String(e) }, "[router] failed to load models for multimodal lookup");
187
+ }
188
+ if (!multimodalCandidate) {
189
+ throw new Error(
190
+ "Router multimodal is enabled but LLM_ROUTER_MULTIMODAL_MODEL is not correctly configured. Remove the image or configure a multimodal model via LLM_ROUTER_MULTIMODAL_MODEL."
191
+ );
192
+ }
193
+
194
+ try {
195
+ logger.info(
196
+ { route: ROUTER_MULTIMODAL_ROUTE, model: multimodalCandidate },
197
+ "[router] multimodal input detected; bypassing Arch selection"
198
+ );
199
+ const ep = await createCandidateEndpoint(multimodalCandidate);
200
+ const gen = await ep({ ...params });
201
+ return metadataThenStream(gen, multimodalCandidate, ROUTER_MULTIMODAL_ROUTE);
202
+ } catch (e) {
203
+ const { message, statusCode } = extractUpstreamError(e);
204
+ logger.error(
205
+ {
206
+ route: ROUTER_MULTIMODAL_ROUTE,
207
+ model: multimodalCandidate,
208
+ err: message,
209
+ ...(statusCode && { status: statusCode }),
210
+ },
211
+ "[router] multimodal fallback failed"
212
+ );
213
+ throw statusCode ? new HTTPError(message, statusCode) : new Error(message);
214
+ }
215
+ }
216
+
217
+ async function findToolsCandidateModel(): Promise<ProcessedModel | undefined> {
218
+ try {
219
+ const all = await getModels();
220
+ return pickToolsCapableModel(all);
221
+ } catch (e) {
222
+ logger.warn({ err: String(e) }, "[router] failed to load models for tools lookup");
223
+ return undefined;
224
+ }
225
+ }
226
+
227
+ if (routerToolsEnabled && hasToolsActive) {
228
+ const toolsModel = await findToolsCandidateModel();
229
+ const toolsCandidate = toolsModel?.id ?? toolsModel?.name;
230
+ if (!toolsCandidate) {
231
+ // No tool-capable model found — continue with normal routing instead of hard failing
232
+ } else {
233
+ try {
234
+ logger.info(
235
+ { route: ROUTER_TOOLS_ROUTE, model: toolsCandidate },
236
+ "[router] tools active; bypassing Arch selection"
237
+ );
238
+ const ep = await createCandidateEndpoint(toolsCandidate);
239
+ const gen = await ep({ ...params });
240
+ return metadataThenStream(gen, toolsCandidate, ROUTER_TOOLS_ROUTE);
241
+ } catch (e) {
242
+ const { message, statusCode } = extractUpstreamError(e);
243
+ const logData = {
244
+ route: ROUTER_TOOLS_ROUTE,
245
+ model: toolsCandidate,
246
+ err: message,
247
+ ...(statusCode && { status: statusCode }),
248
+ };
249
+ if (statusCode === 402) {
250
+ logger.warn(logData, "[router] tools fallback failed due to payment required");
251
+ } else {
252
+ logger.error(logData, "[router] tools fallback failed");
253
+ }
254
+ throw statusCode ? new HTTPError(message, statusCode) : new Error(message);
255
+ }
256
+ }
257
+ }
258
+
259
+ const routeSelection = await archSelectRoute(sanitizedMessages, undefined, params.locals);
260
+
261
+ // If arch router failed with an error, only hard-fail for policy errors (402/401/403)
262
+ // For transient errors (5xx, timeouts, network), allow fallback to continue
263
+ if (routeSelection.routeName === ROUTER_FAILURE && routeSelection.error) {
264
+ const { message, statusCode } = routeSelection.error;
265
+
266
+ if (isPolicyError(statusCode)) {
267
+ // Policy errors should be surfaced to the user immediately (e.g., subscription required)
268
+ logger.error(
269
+ { err: message, ...(statusCode && { status: statusCode }) },
270
+ "[router] arch router failed with policy error, propagating to client"
271
+ );
272
+ throw statusCode ? new HTTPError(message, statusCode) : new Error(message);
273
+ }
274
+
275
+ // Transient errors: log and continue to fallback
276
+ logger.warn(
277
+ { err: message, ...(statusCode && { status: statusCode }) },
278
+ "[router] arch router failed with transient error, attempting fallback"
279
+ );
280
+ }
281
+
282
+ const fallbackModel = config.LLM_ROUTER_FALLBACK_MODEL || routerModel.id;
283
+ const { candidates } = resolveRouteModels(routeSelection.routeName, routes, fallbackModel);
284
+
285
+ let lastErr: unknown = undefined;
286
+ for (const candidate of candidates) {
287
+ try {
288
+ logger.info(
289
+ { route: routeSelection.routeName, model: candidate },
290
+ "[router] trying candidate"
291
+ );
292
+ const ep = await createCandidateEndpoint(candidate);
293
+ const gen = await ep({ ...params });
294
+ return metadataThenStream(gen, candidate, routeSelection.routeName);
295
+ } catch (e) {
296
+ lastErr = e;
297
+ const { message: errMsg, statusCode: errStatus } = extractUpstreamError(e);
298
+ logger.warn(
299
+ {
300
+ route: routeSelection.routeName,
301
+ model: candidate,
302
+ err: errMsg,
303
+ ...(errStatus && { status: errStatus }),
304
+ },
305
+ "[router] candidate failed"
306
+ );
307
+ continue;
308
+ }
309
+ }
310
+
311
+ // Exhausted all candidates — throw to signal upstream failure
312
+ // Forward the upstream error to the client
313
+ const { message, statusCode } = extractUpstreamError(lastErr);
314
+ throw statusCode ? new HTTPError(message, statusCode) : new Error(message);
315
+ };
316
+ }
@@ -0,0 +1,28 @@
1
+ import { config } from "$lib/server/config";
2
+ import type { ProcessedModel } from "../models";
3
+
4
+ /**
5
+ * Returns the configured multimodal model when it exists and is valid.
6
+ * - Requires LLM_ROUTER_MULTIMODAL_MODEL to be set (id or name).
7
+ * - Ignores router aliases and non-multimodal models.
8
+ */
9
+ export function findConfiguredMultimodalModel(
10
+ models: ProcessedModel[] | undefined
11
+ ): ProcessedModel | undefined {
12
+ const preferredModelId = (config.LLM_ROUTER_MULTIMODAL_MODEL || "").trim();
13
+ if (!preferredModelId || !models?.length) return undefined;
14
+
15
+ return models.find(
16
+ (candidate) =>
17
+ (candidate.id === preferredModelId || candidate.name === preferredModelId) &&
18
+ !candidate.isRouter &&
19
+ candidate.multimodal
20
+ );
21
+ }
22
+
23
+ export function getConfiguredMultimodalModelId(
24
+ models: ProcessedModel[] | undefined
25
+ ): string | undefined {
26
+ const model = findConfiguredMultimodalModel(models);
27
+ return model?.id ?? model?.name;
28
+ }
@@ -0,0 +1,49 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { config } from "$lib/server/config";
3
+ import type { Route } from "./types";
4
+
5
+ let ROUTES: Route[] = [];
6
+ let loaded = false;
7
+
8
+ export async function loadPolicy(): Promise<Route[]> {
9
+ const path = config.LLM_ROUTER_ROUTES_PATH;
10
+ const text = await readFile(path, "utf8");
11
+ const arr = JSON.parse(text) as Route[];
12
+ if (!Array.isArray(arr)) {
13
+ throw new Error("Routes config must be a flat array of routes");
14
+ }
15
+ const seen = new Set<string>();
16
+ for (const r of arr) {
17
+ if (!r?.name || !r?.description || !r?.primary_model) {
18
+ throw new Error(`Invalid route entry: ${JSON.stringify(r)}`);
19
+ }
20
+ if (seen.has(r.name)) {
21
+ throw new Error(`Duplicate route name: ${r.name}`);
22
+ }
23
+ seen.add(r.name);
24
+ }
25
+ ROUTES = arr;
26
+ loaded = true;
27
+ return ROUTES;
28
+ }
29
+
30
+ export async function getRoutes(): Promise<Route[]> {
31
+ if (!loaded) await loadPolicy();
32
+ return ROUTES;
33
+ }
34
+
35
+ export function resolveRouteModels(
36
+ routeName: string,
37
+ routes: Route[],
38
+ fallbackModel: string
39
+ ): { candidates: string[] } {
40
+ if (routeName === "arch_router_failure") {
41
+ return { candidates: [fallbackModel] };
42
+ }
43
+ const sel =
44
+ routes.find((r) => r.name === routeName) ||
45
+ routes.find((r) => r.name === "casual_conversation");
46
+ if (!sel) return { candidates: [fallbackModel] };
47
+ const fallbacks = Array.isArray(sel.fallback_models) ? sel.fallback_models : [];
48
+ return { candidates: [sel.primary_model, ...fallbacks] };
49
+ }
@@ -0,0 +1,51 @@
1
+ import { config } from "$lib/server/config";
2
+ import { logger } from "$lib/server/logger";
3
+ import type { ProcessedModel } from "../models";
4
+
5
+ export const ROUTER_TOOLS_ROUTE = "agentic";
6
+
7
+ type LocalsWithMcp = App.Locals & {
8
+ mcp?: {
9
+ selectedServers?: unknown[];
10
+ selectedServerNames?: unknown[];
11
+ };
12
+ };
13
+
14
+ export function isRouterToolsBypassEnabled(): boolean {
15
+ return (config.LLM_ROUTER_ENABLE_TOOLS || "").toLowerCase() === "true";
16
+ }
17
+
18
+ export function hasActiveToolsSelection(locals: App.Locals | undefined): boolean {
19
+ try {
20
+ const reqMcp = (locals as LocalsWithMcp | undefined)?.mcp;
21
+ const byConfig =
22
+ Array.isArray(reqMcp?.selectedServers) && (reqMcp?.selectedServers?.length ?? 0) > 0;
23
+ const byName =
24
+ Array.isArray(reqMcp?.selectedServerNames) && (reqMcp?.selectedServerNames?.length ?? 0) > 0;
25
+ return Boolean(byConfig || byName);
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ export function pickToolsCapableModel(
32
+ models: ProcessedModel[] | undefined
33
+ ): ProcessedModel | undefined {
34
+ const preferredRaw = (config as unknown as Record<string, string>).LLM_ROUTER_TOOLS_MODEL;
35
+ const preferred = preferredRaw?.trim();
36
+ if (!preferred) {
37
+ logger.warn("[router] tools bypass requested but LLM_ROUTER_TOOLS_MODEL is not set");
38
+ return undefined;
39
+ }
40
+ if (!models?.length) return undefined;
41
+ const found = models.find((m) => m.id === preferred || m.name === preferred);
42
+ if (!found) {
43
+ logger.warn(
44
+ { configuredModel: preferred },
45
+ "[router] configured tools model not found; falling back to Arch routing"
46
+ );
47
+ return undefined;
48
+ }
49
+ logger.info({ model: found.id ?? found.name }, "[router] using configured tools model");
50
+ return found;
51
+ }
@@ -0,0 +1,21 @@
1
+ export interface Route {
2
+ name: string;
3
+ description: string;
4
+ primary_model: string;
5
+ fallback_models?: string[];
6
+ }
7
+
8
+ export interface RouteConfig {
9
+ name: string;
10
+ description: string;
11
+ }
12
+
13
+ export interface RouteSelection {
14
+ routeName: string;
15
+ error?: {
16
+ message: string;
17
+ statusCode?: number;
18
+ };
19
+ }
20
+
21
+ export const ROUTER_FAILURE = "arch_router_failure";