@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,1236 @@
1
+ # ADR-029: HuggingFace Chat UI on Cloud Run — chat.conveyorclaims.ai
2
+
3
+ ## Status
4
+ Implemented (2026-02-26), Updated (2026-03-04)
5
+
6
+ ## Date
7
+ 2026-02-26
8
+
9
+ ## Deployed Services
10
+
11
+ | Service | URL | Status |
12
+ |---------|-----|--------|
13
+ | **HF Chat UI** | https://hf-chat-ui-245235083640.us-central1.run.app | Live |
14
+ | **Custom Domain** | https://chat.conveyorclaims.ai | Live (SSL: Google Trust Services) |
15
+ | **MCP Bridge** | https://mcp-bridge-hwqrrwrlna-uc.a.run.app | Live (5 tools) |
16
+
17
+ ## Context
18
+
19
+ The current chat system (`extensions-cloudrun/apps/chat-system`) is a custom React + Vite SPA backed by Gemini. While it serves internal workflow needs well (ADR-014, ADR-024, ADR-027), we need a **production-grade, multi-model chat interface** at `chat.conveyorclaims.ai` that:
20
+
21
+ 1. Exposes **GPT-5 family models** (gpt-5, gpt-5-mini, gpt-5-nano, gpt-5-pro, gpt-5.1, gpt-5.2) plus multi-provider models (Google Gemini, Anthropic Claude) using **existing Google Secret Manager keys**
22
+ 2. Integrates with **existing Cloud Functions** (airtable-agent, db-query-agent, simulation-agent, case-manager, workflow-search) via MCP tool calling
23
+ 3. Connects to **ruvector-postgres** (10.128.0.2) for vector search over workflow documents (384d all-MiniLM-L6-v2 embeddings, 311 chunks) — all tool/data operations go through PostgreSQL, NOT MongoDB
24
+ 4. Provides conversation persistence, authentication, and a polished UI out of the box
25
+ 5. Deploys as a new Cloud Run service alongside the existing chat-system — no disruption
26
+
27
+ ### Database Strategy: Hybrid PostgreSQL + MongoDB
28
+
29
+ HuggingFace Chat UI **requires MongoDB** for its internal persistence layer (conversations, users, sessions, assistants). This cannot be swapped for PostgreSQL without forking the project. However, **all business data and tool operations** route through ruvector-postgres via the MCP Bridge:
30
+
31
+ | Layer | Database | Purpose |
32
+ |-------|----------|---------|
33
+ | **Chat UI internals** | MongoDB (lightweight sidecar or Atlas free tier) | Conversations, user sessions, assistant configs |
34
+ | **Business data & tools** | ruvector-postgres (10.128.0.2) | Workflow search, case data, analytics, embeddings |
35
+ | **AI provider keys** | Google Secret Manager | `openai-api-key`, `anthropic-api-key`, `google-api-key` |
36
+
37
+ MongoDB handles only what Chat UI needs internally. All the **real work** — workflow search, case management, analytics, simulations — flows through the existing ruvector-postgres via MCP tools. The MongoDB instance can run as a sidecar container on the same Cloud Run service using the bundled `chat-ui-db` image, requiring **zero additional infrastructure**.
38
+
39
+ ### Multi-Provider Strategy via Google Secret Manager
40
+
41
+ All AI provider API keys already exist in Google Secret Manager (ADR-004). Chat UI will pull these at runtime:
42
+
43
+ | Secret ID | Provider | Models |
44
+ |-----------|----------|--------|
45
+ | `openai-api-key` | OpenAI | GPT-5.2, GPT-5, GPT-5-mini, GPT-5-nano, GPT-4o, o3 |
46
+ | `anthropic-api-key` | Anthropic | Claude (when credits refilled) |
47
+ | `google-api-key` | Google | Gemini 2.5 Pro/Flash (when key renewed) |
48
+
49
+ ### Why HuggingFace Chat UI
50
+
51
+ [HuggingFace Chat UI](https://github.com/huggingface/chat-ui) (Apache 2.0, 10,400+ GitHub stars) is the open-source codebase powering HuggingChat. It provides:
52
+
53
+ - **Native OpenAI-compatible API support** — connects directly to `api.openai.com/v1`, auto-discovers all available models
54
+ - **MCP (Model Context Protocol) tool calling** — exposes external APIs as callable tools from within chat
55
+ - **Multi-model selector** — users pick from GPT-5, GPT-5-mini, GPT-4o, etc. in a dropdown
56
+ - **Smart routing ("Omni")** — auto-selects the best model per query
57
+ - **Built-in web search + RAG** — retrieval-augmented generation with search grounding
58
+ - **MongoDB-backed persistence** — conversation history, user sessions, assistants (bundled sidecar option eliminates external dependency)
59
+ - **OpenID Connect auth** — Google OAuth integration
60
+ - **SvelteKit SSR** — fast, server-rendered UI with streaming responses
61
+ - **Docker-ready** — pre-built images at `ghcr.io/huggingface/chat-ui`
62
+ - **Whisper voice transcription** — speech-to-text input
63
+
64
+ This eliminates months of custom UI development while providing a superior chat experience.
65
+
66
+ ### Why NOT Modify the Existing Chat System
67
+
68
+ | Factor | Existing Chat System | HuggingFace Chat UI |
69
+ |--------|---------------------|-------------------|
70
+ | AI Provider | Gemini-only (tightly coupled) | Any OpenAI-compatible API |
71
+ | Model switching | None (ADR-028 proposes abstraction) | Built-in multi-model selector |
72
+ | Conversation persistence | LocalStorage only | MongoDB sidecar + ruvector-postgres for tools |
73
+ | Tool calling | Custom FunctionExecutor | MCP standard protocol |
74
+ | Authentication | Custom Google OAuth | OpenID Connect (standard) |
75
+ | Voice input | None | Whisper transcription |
76
+ | Web search | None | Built-in RAG |
77
+ | Maintenance burden | Custom React/Vite SPA | Community-maintained OSS |
78
+
79
+ The existing chat system continues serving its current role. This ADR creates a **parallel, GPT-5-powered interface** at a separate domain.
80
+
81
+ ## Decision
82
+
83
+ Deploy HuggingFace Chat UI as a new Cloud Run service (`hf-chat-ui`) with:
84
+ - GPT-5 model family via OpenAI API
85
+ - Custom MCP server bridging to existing Cloud Functions
86
+ - MongoDB Atlas for conversation persistence
87
+ - Google OAuth via OpenID Connect
88
+ - Custom domain mapping to `chat.conveyorclaims.ai`
89
+ - VPC connector for ruvector-postgres access
90
+
91
+ ---
92
+
93
+ ## Architecture
94
+
95
+ ```
96
+ ┌─────────────────────────────┐
97
+ │ chat.conveyorclaims.ai │
98
+ │ (Cloud Run Domain Mapping) │
99
+ └──────────────┬──────────────┘
100
+ │ HTTPS
101
+
102
+ ┌───────────────────────────────────────────────────────────────────────┐
103
+ │ Cloud Run: hf-chat-ui │
104
+ │ ghcr.io/huggingface/chat-ui-db │
105
+ │ Port 3000, 2Gi RAM, 2 CPU │
106
+ │ us-central1, VPC: conveyor-connector │
107
+ │ │
108
+ │ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌───────────┐ │
109
+ │ │ SvelteKit │ │ MCP Client │ │ Multi-LLM │ │ MongoDB │ │
110
+ │ │ Frontend │ │ (Tool Call) │ │ Provider │ │ Sidecar │ │
111
+ │ └──────┬──────┘ └──────┬───────┘ └──────┬──────┘ └───────────┘ │
112
+ │ │ │ │ │
113
+ └─────────┼────────────────┼──────────────────┼─────────────────────────┘
114
+ │ │ │
115
+ │ │ ┌───────┼───────────────┐
116
+ │ │ │ │ │
117
+ │ ▼ ▼ ▼ ▼
118
+ │ ┌──────────────┐ ┌──────┐ ┌────────┐ ┌─────────┐
119
+ │ │ MCP Bridge │ │OpenAI│ │ Google │ │Anthropic│
120
+ │ │ (Cloud Run) │ │ API │ │Gemini │ │ Claude │
121
+ │ │ │ │ │ │ API │ │ API │
122
+ │ │ Routes to: │ │gpt-5 │ │gemini │ │claude │
123
+ │ │ Cloud Fns + │ │gpt-5m│ │2.5-pro │ │sonnet-4 │
124
+ │ │ ruvector-pg │ │gpt-4o│ │2.5-fl │ │ │
125
+ │ └──────┬───────┘ │o3 │ │ │ │ │
126
+ │ │ └──────┘ └────────┘ └─────────┘
127
+ │ ▼ Keys from Google Secret Manager
128
+ │ ┌───────────────────────────────────┐
129
+ │ │ Existing Cloud Functions │
130
+ │ │ (No Changes Required) │
131
+ │ │ │
132
+ │ │ • airtable-agent │
133
+ │ │ • db-query-agent │
134
+ │ │ • case-manager │
135
+ │ │ • simulation-agent │
136
+ │ │ • workflow-search │
137
+ │ └───────────────┬───────────────────┘
138
+ │ │ VPC (10.128.0.0/20)
139
+ │ ▼
140
+ │ ┌───────────────────────────────────┐
141
+ │ │ ruvector-postgres VM │
142
+ └─▶│ 10.128.0.2:5432 │
143
+ │ PostgreSQL 17.7 + ruvector │
144
+ │ │
145
+ │ PRIMARY DATA STORE: │
146
+ │ • workflow_chunks (311 rows) │
147
+ │ • embeddings (320 vectors, 384d) │
148
+ │ • HNSW index (m=16, ef=64) │
149
+ │ • Case data, analytics, metrics │
150
+ └───────────────────────────────────┘
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Implementation
156
+
157
+ ### Phase 1: MongoDB Sidecar (Bundled with Chat UI)
158
+
159
+ HuggingFace Chat UI requires MongoDB for internal persistence (conversations, users, sessions). Rather than adding an external MongoDB dependency, we use the **bundled `chat-ui-db` image** which includes MongoDB as a sidecar process. Data is persisted via a Cloud Run volume mount.
160
+
161
+ **Why sidecar, not Atlas:**
162
+ - Zero additional infrastructure or accounts
163
+ - No network latency (localhost connection)
164
+ - All business data still lives in ruvector-postgres via MCP tools
165
+ - MongoDB only stores lightweight chat UI metadata
166
+ - If we outgrow this, upgrade to Atlas later (just change `MONGODB_URL`)
167
+
168
+ **Configuration:**
169
+ ```ini
170
+ # Bundled MongoDB uses local storage — no connection string needed
171
+ # The chat-ui-db image starts MongoDB internally on localhost:27017
172
+ MONGODB_URL=mongodb://localhost:27017
173
+ MONGODB_DB_NAME=conveyor-chat
174
+ ```
175
+
176
+ **Volume mount for persistence** (Cloud Run 2nd gen):
177
+ ```bash
178
+ # Data persists across container restarts via /data volume
179
+ # The chat-ui-db image stores MongoDB data at /data/db
180
+ ```
181
+
182
+ **Upgrade path:** If conversation volume grows beyond what a sidecar can handle, switch to MongoDB Atlas by updating `MONGODB_URL` in Secret Manager — zero code changes.
183
+
184
+ ### Why MongoDB Cannot Be Avoided
185
+
186
+ HuggingFace Chat UI is **hardcoded to MongoDB** — its data layer uses MongoDB queries, aggregations, and GridFS throughout the SvelteKit backend. Replacing it with PostgreSQL would require forking the entire project. The sidecar approach (`chat-ui-db` image) bundles MongoDB **inside the same container**, so:
187
+
188
+ - No external MongoDB service to manage
189
+ - No additional infrastructure cost
190
+ - No MongoDB Atlas account needed
191
+ - Data lives on the container's ephemeral storage (conversations are lightweight and regenerable)
192
+ - All **business-critical data** (cases, workflows, embeddings, analytics) stays in ruvector-postgres
193
+
194
+ Think of MongoDB here as an internal implementation detail of Chat UI — like SQLite in a desktop app. The user never interacts with it directly. Ruvector-postgres remains the **single source of truth** for all Conveyor data.
195
+
196
+ ---
197
+
198
+ ### Phase 2: MCP Bridge Server
199
+
200
+ The MCP Bridge Server exposes existing Cloud Functions as MCP-compatible tools that Chat UI can call. This is a lightweight Node.js service deployed as a separate Cloud Run service.
201
+
202
+ **File: `infrastructure/gcp/mcp-bridge/index.js`**
203
+
204
+ ```javascript
205
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
206
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
207
+ import express from "express";
208
+ import { z } from "zod";
209
+
210
+ const CLOUD_FUNCTIONS = {
211
+ airtable: "https://airtable-agent-hwqrrwrlna-uc.a.run.app",
212
+ dbQuery: "https://db-query-agent-hwqrrwrlna-uc.a.run.app",
213
+ caseManager: "https://case-manager-hwqrrwrlna-uc.a.run.app",
214
+ simulation: "https://simulation-agent-hwqrrwrlna-uc.a.run.app",
215
+ workflowSearch: "https://us-central1-new-project-473022.cloudfunctions.net/workflow-search",
216
+ };
217
+
218
+ const server = new McpServer({
219
+ name: "conveyor-tools",
220
+ version: "1.0.0",
221
+ });
222
+
223
+ // Tool: Search workflow documents (vector search via ruvector-postgres)
224
+ server.tool(
225
+ "search_workflows",
226
+ "Search CLG workflow procedures, FAQs, and case management steps using semantic search. Returns relevant workflow steps for a given query.",
227
+ {
228
+ query: z.string().describe("Natural language query about workflow procedures"),
229
+ limit: z.number().optional().default(5).describe("Max results to return"),
230
+ },
231
+ async ({ query, limit }) => {
232
+ const resp = await fetch(CLOUD_FUNCTIONS.workflowSearch, {
233
+ method: "POST",
234
+ headers: { "Content-Type": "application/json" },
235
+ body: JSON.stringify({ action: "search", query, limit }),
236
+ });
237
+ const data = await resp.json();
238
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
239
+ }
240
+ );
241
+
242
+ // Tool: Query database analytics
243
+ server.tool(
244
+ "query_database",
245
+ "Run analytics queries against the PostgreSQL database. Supports case metrics, revenue forecasts, and trend analysis.",
246
+ {
247
+ query: z.string().describe("Natural language analytics query"),
248
+ type: z.enum(["metrics", "forecast", "trend", "custom"]).optional().default("metrics"),
249
+ },
250
+ async ({ query, type }) => {
251
+ const resp = await fetch(CLOUD_FUNCTIONS.dbQuery, {
252
+ method: "POST",
253
+ headers: { "Content-Type": "application/json" },
254
+ body: JSON.stringify({ query, type }),
255
+ });
256
+ const data = await resp.json();
257
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
258
+ }
259
+ );
260
+
261
+ // Tool: Case management operations
262
+ server.tool(
263
+ "manage_case",
264
+ "Look up case status, get next steps, list cases, or perform case management operations via Airtable.",
265
+ {
266
+ action: z.enum(["status", "list", "next_steps", "update"]).describe("Case action"),
267
+ caseId: z.string().optional().describe("Case ID (e.g., C-02420)"),
268
+ filters: z.record(z.string()).optional().describe("Filter criteria for list action"),
269
+ },
270
+ async ({ action, caseId, filters }) => {
271
+ const resp = await fetch(CLOUD_FUNCTIONS.caseManager, {
272
+ method: "POST",
273
+ headers: { "Content-Type": "application/json" },
274
+ body: JSON.stringify({ action, caseId, filters }),
275
+ });
276
+ const data = await resp.json();
277
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
278
+ }
279
+ );
280
+
281
+ // Tool: Run RL simulations
282
+ server.tool(
283
+ "run_simulation",
284
+ "Run reinforcement learning strategy simulations for case settlement optimization. Uses Q-learning and Monte Carlo methods.",
285
+ {
286
+ scenario: z.string().describe("Simulation scenario description"),
287
+ episodes: z.number().optional().default(1000).describe("Number of simulation episodes"),
288
+ strategy: z.enum(["q_learning", "monte_carlo", "policy_gradient"]).optional().default("q_learning"),
289
+ },
290
+ async ({ scenario, episodes, strategy }) => {
291
+ const resp = await fetch(CLOUD_FUNCTIONS.simulation, {
292
+ method: "POST",
293
+ headers: { "Content-Type": "application/json" },
294
+ body: JSON.stringify({ scenario, episodes, strategy }),
295
+ });
296
+ const data = await resp.json();
297
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
298
+ }
299
+ );
300
+
301
+ // Tool: Airtable CRUD
302
+ server.tool(
303
+ "airtable_query",
304
+ "Query or update Airtable records. Supports listing cases, clients, carriers, and performing CRUD operations.",
305
+ {
306
+ action: z.enum(["list", "get", "create", "update"]).describe("CRUD action"),
307
+ table: z.string().describe("Airtable table name (e.g., Cases, Clients, Carriers)"),
308
+ recordId: z.string().optional().describe("Record ID for get/update"),
309
+ filters: z.record(z.string()).optional().describe("Filter criteria"),
310
+ fields: z.record(z.unknown()).optional().describe("Fields for create/update"),
311
+ },
312
+ async ({ action, table, recordId, filters, fields }) => {
313
+ const resp = await fetch(CLOUD_FUNCTIONS.airtable, {
314
+ method: "POST",
315
+ headers: { "Content-Type": "application/json" },
316
+ body: JSON.stringify({ action, table, recordId, filters, fields }),
317
+ });
318
+ const data = await resp.json();
319
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
320
+ }
321
+ );
322
+
323
+ // Express HTTP transport
324
+ const app = express();
325
+
326
+ app.post("/mcp", async (req, res) => {
327
+ const transport = new StreamableHTTPServerTransport("/mcp");
328
+ await server.connect(transport);
329
+ await transport.handleRequest(req, res);
330
+ });
331
+
332
+ app.get("/health", (_, res) => res.json({ status: "ok" }));
333
+
334
+ app.listen(3001, () => console.log("MCP Bridge running on :3001"));
335
+ ```
336
+
337
+ **Deploy:**
338
+ ```bash
339
+ gcloud run deploy mcp-bridge \
340
+ --source=infrastructure/gcp/mcp-bridge \
341
+ --platform=managed \
342
+ --region=us-central1 \
343
+ --port=3001 \
344
+ --memory=512Mi \
345
+ --cpu=1 \
346
+ --min-instances=0 \
347
+ --max-instances=5 \
348
+ --vpc-connector=conveyor-connector \
349
+ --allow-unauthenticated
350
+ ```
351
+
352
+ ---
353
+
354
+ ### Phase 3: MCP Tool Servers (3 Sources)
355
+
356
+ Chat UI supports multiple MCP servers simultaneously. We configure **three** to give GPT-5 full access to Conveyor's data ecosystem:
357
+
358
+ #### MCP Server 1: Conveyor Bridge (Custom — Cloud Functions + ruvector-postgres)
359
+
360
+ The custom MCP Bridge from Phase 2. Provides 5 tools:
361
+
362
+ | Tool | Backend | Purpose |
363
+ |------|---------|---------|
364
+ | `search_workflows` | workflow-search → ruvector-postgres | Semantic search over CLG workflow docs (311 chunks, 384d HNSW) |
365
+ | `query_database` | db-query-agent → ruvector-postgres | SQL analytics, revenue forecasts, trend analysis |
366
+ | `manage_case` | case-manager → Airtable | Case status lookup, next steps, updates |
367
+ | `run_simulation` | simulation-agent | RL strategy simulations (Q-learning, Monte Carlo) |
368
+ | `airtable_query` | airtable-agent → Airtable | Generic Airtable CRUD across all tables |
369
+
370
+ #### MCP Server 2: Official Airtable MCP
371
+
372
+ [Airtable's official MCP server](https://support.airtable.com/docs/using-the-airtable-mcp-server) provides **direct base access** — no custom bridge needed. This gives GPT-5 full schema awareness and natural language querying.
373
+
374
+ **Capabilities:**
375
+ - List all bases, tables, fields, and views
376
+ - Read, create, update, delete records
377
+ - Search records with filters
378
+ - Schema inspection (field types, options, linked records)
379
+ - No additional infrastructure — hosted by Airtable
380
+
381
+ **Secret:** `airtable-api-key` (already in Google Secret Manager)
382
+
383
+ ```
384
+ URL: https://mcp.airtable.com/v0/mcp
385
+ Auth: Bearer ${AIRTABLE_API_KEY}
386
+ ```
387
+
388
+ > **Why both Airtable MCP AND the Conveyor Bridge airtable tool?** The official Airtable MCP gives raw CRUD access — GPT-5 can browse schemas and build ad-hoc queries. The Conveyor Bridge `manage_case` tool provides **structured, pre-built** case management workflows. Users benefit from both: exploration via Airtable MCP, workflow-guided operations via the bridge.
389
+
390
+ #### MCP Server 3: Google Drive MCP
391
+
392
+ [Google's official MCP for Drive](https://cloud.google.com/blog/products/ai-machine-learning/announcing-official-mcp-support-for-google-services) provides access to the CLG Workflow shared drive documents.
393
+
394
+ **Capabilities:**
395
+ - Search files across Drive (including shared drives)
396
+ - Read document contents (Docs, Sheets, Slides)
397
+ - List files in folders
398
+ - Read Google Sheets cells and ranges
399
+ - Access the 🔴CLG Workflow shared drive (0AMTB1wrVg9HLUk9PVA)
400
+
401
+ **Secrets:** `google-client-id`, `google-client-secret` (both in Secret Manager)
402
+
403
+ ```
404
+ URL: https://mcp.googleapis.com/v1/drive
405
+ Auth: OAuth2 service account or user token
406
+ ```
407
+
408
+ > **Why both Google Drive MCP AND the workflow-search tool?** The workflow-search tool provides **vector-indexed semantic search** (HNSW, <50ms) over pre-chunked workflow documents. The Google Drive MCP provides **raw file access** — read any document, list folders, access spreadsheets. Use workflow-search for "what's the process for X?" and Google Drive MCP for "show me the intake form template."
409
+
410
+ #### Combined Tool Landscape
411
+
412
+ ```
413
+ ┌─────────────────────────────────────────────────────────────────┐
414
+ │ HF Chat UI — MCP Clients │
415
+ │ │
416
+ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
417
+ │ │ Conveyor Bridge │ │ Airtable MCP │ │ Google Drive MCP│ │
418
+ │ │ (Custom) │ │ (Official) │ │ (Google) │ │
419
+ │ │ │ │ │ │ │ │
420
+ │ │ • search_wf │ │ • list_bases │ │ • search_files │ │
421
+ │ │ • query_db │ │ • list_tables │ │ • read_doc │ │
422
+ │ │ • manage_case │ │ • read_records │ │ • list_folder │ │
423
+ │ │ • run_sim │ │ • create_record│ │ • read_sheets │ │
424
+ │ │ • airtable_query │ │ • update_record│ │ • get_metadata │ │
425
+ │ │ │ │ • search │ │ │ │
426
+ │ └────────┬─────────┘ └───────┬────────┘ └───────┬─────────┘ │
427
+ │ │ │ │ │
428
+ └───────────┼────────────────────┼────────────────────┼─────────────┘
429
+ │ │ │
430
+ ▼ ▼ ▼
431
+ Cloud Functions + Airtable API Google Drive API
432
+ ruvector-postgres (airtable.com) (googleapis.com)
433
+ ```
434
+
435
+ ---
436
+
437
+ ### Phase 4: Multi-Provider Model Configuration
438
+
439
+ All API keys are pulled from **Google Secret Manager** at runtime via Cloud Run `--set-secrets`. The MODELS environment variable configures multi-provider access.
440
+
441
+ #### Secrets Used (all already exist in Secret Manager)
442
+
443
+ | Secret ID | Env Var | Provider |
444
+ |-----------|---------|----------|
445
+ | `openai-api-key` | `OPENAI_API_KEY` | OpenAI (GPT-5 family) |
446
+ | `anthropic-api-key` | `ANTHROPIC_API_KEY` | Anthropic (Claude) |
447
+ | `google-api-key` | `GOOGLE_API_KEY` | Google (Gemini) |
448
+
449
+ #### Model Lineup
450
+
451
+ ```ini
452
+ MODELS=`[
453
+ {
454
+ "name": "gpt-5.2",
455
+ "id": "gpt-5.2",
456
+ "displayName": "GPT-5.2 (Latest)",
457
+ "description": "OpenAI's latest flagship model. Best for complex reasoning and analysis.",
458
+ "supportsTools": true,
459
+ "parameters": {
460
+ "temperature": 0.7,
461
+ "max_new_tokens": 4096
462
+ },
463
+ "endpoints": [{
464
+ "type": "openai",
465
+ "baseURL": "https://api.openai.com/v1"
466
+ }]
467
+ },
468
+ {
469
+ "name": "gpt-5.2-pro",
470
+ "id": "gpt-5.2-pro",
471
+ "displayName": "GPT-5.2 Pro",
472
+ "description": "Pro tier with extended reasoning. Best for complex case analysis.",
473
+ "supportsTools": true,
474
+ "parameters": {
475
+ "temperature": 0.5,
476
+ "max_new_tokens": 8192
477
+ },
478
+ "endpoints": [{
479
+ "type": "openai",
480
+ "baseURL": "https://api.openai.com/v1"
481
+ }]
482
+ },
483
+ {
484
+ "name": "gpt-5",
485
+ "id": "gpt-5",
486
+ "displayName": "GPT-5",
487
+ "description": "Strong general-purpose reasoning. Good balance of speed and quality.",
488
+ "supportsTools": true,
489
+ "parameters": {
490
+ "temperature": 0.7,
491
+ "max_new_tokens": 4096
492
+ },
493
+ "endpoints": [{
494
+ "type": "openai",
495
+ "baseURL": "https://api.openai.com/v1"
496
+ }]
497
+ },
498
+ {
499
+ "name": "gpt-5-mini",
500
+ "id": "gpt-5-mini",
501
+ "displayName": "GPT-5 Mini",
502
+ "description": "Fast and cost-effective. Great for FAQ lookups and simple workflow queries.",
503
+ "supportsTools": true,
504
+ "parameters": {
505
+ "temperature": 0.7,
506
+ "max_new_tokens": 4096
507
+ },
508
+ "endpoints": [{
509
+ "type": "openai",
510
+ "baseURL": "https://api.openai.com/v1"
511
+ }]
512
+ },
513
+ {
514
+ "name": "gpt-5-nano",
515
+ "id": "gpt-5-nano",
516
+ "displayName": "GPT-5 Nano",
517
+ "description": "Ultra-fast for simple queries. Lowest cost per token.",
518
+ "supportsTools": true,
519
+ "parameters": {
520
+ "temperature": 0.7,
521
+ "max_new_tokens": 2048
522
+ },
523
+ "endpoints": [{
524
+ "type": "openai",
525
+ "baseURL": "https://api.openai.com/v1"
526
+ }]
527
+ },
528
+ {
529
+ "name": "gpt-4o",
530
+ "id": "gpt-4o",
531
+ "displayName": "GPT-4o (Multimodal)",
532
+ "description": "Multimodal model. Upload images of documents, forms, or damage photos.",
533
+ "multimodal": true,
534
+ "supportsTools": true,
535
+ "parameters": {
536
+ "temperature": 0.5,
537
+ "max_new_tokens": 4096
538
+ },
539
+ "endpoints": [{
540
+ "type": "openai",
541
+ "baseURL": "https://api.openai.com/v1"
542
+ }]
543
+ },
544
+ {
545
+ "name": "o3",
546
+ "id": "o3",
547
+ "displayName": "o3 (Reasoning)",
548
+ "description": "Advanced reasoning model. Best for complex legal/financial analysis.",
549
+ "supportsTools": false,
550
+ "parameters": {
551
+ "max_new_tokens": 4096
552
+ },
553
+ "endpoints": [{
554
+ "type": "openai",
555
+ "baseURL": "https://api.openai.com/v1"
556
+ }]
557
+ },
558
+ {
559
+ "name": "gemini-2.5-pro",
560
+ "id": "gemini-2.5-pro",
561
+ "displayName": "Gemini 2.5 Pro (Google)",
562
+ "description": "Google's most capable model. Already used in the existing chat system.",
563
+ "supportsTools": true,
564
+ "parameters": {
565
+ "temperature": 0.7,
566
+ "max_new_tokens": 4096
567
+ },
568
+ "endpoints": [{
569
+ "type": "openai",
570
+ "baseURL": "https://generativelanguage.googleapis.com/v1beta/openai",
571
+ "apiKey": "${GOOGLE_API_KEY}"
572
+ }]
573
+ },
574
+ {
575
+ "name": "gemini-2.5-flash",
576
+ "id": "gemini-2.5-flash",
577
+ "displayName": "Gemini 2.5 Flash (Google)",
578
+ "description": "Google's fast model. Good for quick workflow lookups.",
579
+ "supportsTools": true,
580
+ "parameters": {
581
+ "temperature": 0.7,
582
+ "max_new_tokens": 4096
583
+ },
584
+ "endpoints": [{
585
+ "type": "openai",
586
+ "baseURL": "https://generativelanguage.googleapis.com/v1beta/openai",
587
+ "apiKey": "${GOOGLE_API_KEY}"
588
+ }]
589
+ },
590
+ {
591
+ "name": "claude-sonnet-4",
592
+ "id": "claude-sonnet-4",
593
+ "displayName": "Claude Sonnet 4 (Anthropic)",
594
+ "description": "Anthropic's balanced model. Strong instruction following and coding.",
595
+ "supportsTools": true,
596
+ "parameters": {
597
+ "temperature": 0.7,
598
+ "max_new_tokens": 4096
599
+ },
600
+ "endpoints": [{
601
+ "type": "openai",
602
+ "baseURL": "https://api.anthropic.com/v1",
603
+ "apiKey": "${ANTHROPIC_API_KEY}",
604
+ "defaultHeaders": {
605
+ "anthropic-version": "2023-06-01"
606
+ }
607
+ }]
608
+ }
609
+ ]`
610
+ ```
611
+
612
+ > **Note:** Google and Anthropic keys are currently expired/out of credits (tested 2026-02-26). Models will show as unavailable until keys are renewed. OpenAI GPT-5 models are **confirmed working** with $100 balance. Chat UI gracefully handles unavailable providers — users simply see those models greyed out.
613
+
614
+ ---
615
+
616
+ ### Phase 4: Chat UI Cloud Run Deployment
617
+
618
+ #### 4a. Secrets Setup (All Already Exist)
619
+
620
+ All required secrets already exist in Google Secret Manager (verified 2026-02-26). Just verify access:
621
+
622
+ ```bash
623
+ # All 8 secrets needed for hf-chat-ui
624
+ SECRETS=(
625
+ openai-api-key # GPT-5 models
626
+ anthropic-api-key # Claude models
627
+ google-api-key # Gemini models
628
+ airtable-api-key # Airtable MCP
629
+ airtable-base-id # Airtable base reference
630
+ google-client-id # Google OAuth + Drive MCP
631
+ google-client-secret # Google OAuth + Drive MCP
632
+ gemini-api-key # Backup Gemini key
633
+ )
634
+
635
+ # Verify all secrets exist
636
+ for secret in "${SECRETS[@]}"; do
637
+ echo -n "$secret: "
638
+ gcloud secrets versions access latest --secret="$secret" \
639
+ --project=new-project-473022 2>/dev/null | head -c 12 && echo "... ✓" || echo "MISSING"
640
+ done
641
+
642
+ # Grant access to compute service account
643
+ for secret in "${SECRETS[@]}"; do
644
+ gcloud secrets add-iam-policy-binding "$secret" \
645
+ --project=new-project-473022 \
646
+ --member="serviceAccount:245235083640-compute@developer.gserviceaccount.com" \
647
+ --role="roles/secretmanager.secretAccessor" \
648
+ --quiet 2>/dev/null || true
649
+ done
650
+ ```
651
+
652
+ **Secrets inventory for this deployment:**
653
+
654
+ | Secret | Purpose | Status |
655
+ |--------|---------|--------|
656
+ | `openai-api-key` | GPT-5 model access | Active ($100 balance) |
657
+ | `anthropic-api-key` | Claude model access | Needs credits |
658
+ | `google-api-key` | Gemini model access | Needs renewal |
659
+ | `airtable-api-key` | Airtable MCP direct access | Active |
660
+ | `airtable-base-id` | Airtable base reference | Active |
661
+ | `google-client-id` | Google OAuth + Drive MCP | Active |
662
+ | `google-client-secret` | Google OAuth + Drive MCP | Active |
663
+ | `gemini-api-key` | Backup Gemini key | Active |
664
+
665
+ #### 4b. Environment File
666
+
667
+ **File: `infrastructure/gcp/hf-chat-ui/.env.production`**
668
+
669
+ ```ini
670
+ # ── Model Provider ──────────────────────────────────────
671
+ OPENAI_BASE_URL=https://api.openai.com/v1
672
+ # OPENAI_API_KEY injected from Secret Manager
673
+
674
+ # ── Database ────────────────────────────────────────────
675
+ # MONGODB_URL injected from Secret Manager
676
+ MONGODB_DB_NAME=conveyor-chat
677
+
678
+ # ── Branding ────────────────────────────────────────────
679
+ PUBLIC_APP_NAME=Conveyor AI
680
+ PUBLIC_APP_DESCRIPTION=Insurance Case Management & Revenue Operations Assistant powered by GPT-5
681
+ PUBLIC_ORIGIN=https://chat.conveyorclaims.ai
682
+
683
+ # ── Authentication (Google OAuth) ───────────────────────
684
+ OPENID_PROVIDER_URL=https://accounts.google.com
685
+ OPENID_CLIENT_ID=245235083640-gkbo4otq57lqeisuigcat0bg037f49oc.apps.googleusercontent.com
686
+ # OPENID_CLIENT_SECRET injected from Secret Manager
687
+ OPENID_SCOPES=openid profile email
688
+ OPENID_NAME_CLAIM=name
689
+ COOKIE_SECURE=true
690
+ COOKIE_SAMESITE=lax
691
+
692
+ # ── MCP Tools (3 servers: Custom Bridge + Airtable + Google Drive) ──
693
+ MCP_SERVERS=`[
694
+ {
695
+ "name": "Conveyor Tools",
696
+ "description": "Workflow search, DB analytics, case management, simulations via ruvector-postgres and Cloud Functions",
697
+ "url": "https://mcp-bridge-hwqrrwrlna-uc.a.run.app/mcp"
698
+ },
699
+ {
700
+ "name": "Airtable",
701
+ "description": "Direct Airtable base access — browse tables, search records, create/update cases, view schemas",
702
+ "url": "https://mcp.airtable.com/v0/mcp",
703
+ "headers": {
704
+ "Authorization": "Bearer ${AIRTABLE_API_KEY}"
705
+ }
706
+ },
707
+ {
708
+ "name": "Google Drive",
709
+ "description": "Search and read CLG Workflow documents, forms, and templates from Google Drive shared folders",
710
+ "url": "https://mcp.googleapis.com/v1/drive",
711
+ "headers": {
712
+ "Authorization": "Bearer ${GOOGLE_DRIVE_TOKEN}"
713
+ }
714
+ }
715
+ ]`
716
+ MCP_TOOL_TIMEOUT_MS=30000
717
+
718
+ # ── Smart Router ────────────────────────────────────────
719
+ LLM_ROUTER_FALLBACK_MODEL=gpt-5
720
+ LLM_ROUTER_ENABLE_TOOLS=true
721
+ LLM_ROUTER_TOOLS_MODEL=gpt-5.2
722
+ PUBLIC_LLM_ROUTER_DISPLAY_NAME=Auto (Omni)
723
+ PUBLIC_LLM_ROUTER_ALIAS_ID=omni
724
+
725
+ # ── Voice ───────────────────────────────────────────────
726
+ TRANSCRIPTION_MODEL=openai/whisper-large-v3-turbo
727
+
728
+ # ── Web Search ──────────────────────────────────────────
729
+ USE_LOCAL_WEBSEARCH=true
730
+
731
+ # ── Features ────────────────────────────────────────────
732
+ LLM_SUMMARIZATION=true
733
+ ENABLE_DATA_EXPORT=true
734
+ ALLOW_IFRAME=false
735
+
736
+ # ── Rate Limits ─────────────────────────────────────────
737
+ USAGE_LIMITS={"messagesPerMinute": 20, "conversations": 100, "tools": 50}
738
+
739
+ # ── System Prompt (Conveyor Identity) ───────────────────
740
+ TASK_MODEL=gpt-5-mini
741
+ ```
742
+
743
+ #### 4c. Cloud Build Configuration
744
+
745
+ **File: `infrastructure/gcp/hf-chat-ui/cloudbuild.yaml`**
746
+
747
+ ```yaml
748
+ steps:
749
+ # Step 1: Pull the pre-built HuggingFace Chat UI image
750
+ - name: 'gcr.io/cloud-builders/docker'
751
+ args: ['pull', 'ghcr.io/huggingface/chat-ui:latest']
752
+
753
+ # Step 2: Tag for GCR
754
+ - name: 'gcr.io/cloud-builders/docker'
755
+ args: [
756
+ 'tag',
757
+ 'ghcr.io/huggingface/chat-ui:latest',
758
+ 'gcr.io/${PROJECT_ID}/hf-chat-ui:${_VERSION}'
759
+ ]
760
+
761
+ # Step 3: Push versioned tag
762
+ - name: 'gcr.io/cloud-builders/docker'
763
+ args: ['push', 'gcr.io/${PROJECT_ID}/hf-chat-ui:${_VERSION}']
764
+
765
+ # Step 4: Push latest tag
766
+ - name: 'gcr.io/cloud-builders/docker'
767
+ args: [
768
+ 'tag',
769
+ 'gcr.io/${PROJECT_ID}/hf-chat-ui:${_VERSION}',
770
+ 'gcr.io/${PROJECT_ID}/hf-chat-ui:latest'
771
+ ]
772
+ - name: 'gcr.io/cloud-builders/docker'
773
+ args: ['push', 'gcr.io/${PROJECT_ID}/hf-chat-ui:latest']
774
+
775
+ # Step 5: Deploy to Cloud Run
776
+ - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
777
+ entrypoint: gcloud
778
+ args: [
779
+ 'run', 'deploy', 'hf-chat-ui',
780
+ '--image', 'gcr.io/${PROJECT_ID}/hf-chat-ui:${_VERSION}',
781
+ '--platform', 'managed',
782
+ '--region', 'us-central1',
783
+ '--port', '3000',
784
+ '--memory', '2Gi',
785
+ '--cpu', '2',
786
+ '--min-instances', '0',
787
+ '--max-instances', '10',
788
+ '--timeout', '300',
789
+ '--vpc-connector', 'conveyor-connector',
790
+ '--allow-unauthenticated',
791
+ '--set-env-vars', 'OPENAI_BASE_URL=https://api.openai.com/v1,MONGODB_DB_NAME=conveyor-chat,PUBLIC_APP_NAME=Conveyor AI,PUBLIC_ORIGIN=https://chat.conveyorclaims.ai,LLM_SUMMARIZATION=true,ENABLE_DATA_EXPORT=true',
792
+ '--set-secrets', 'OPENAI_API_KEY=openai-api-key:latest,ANTHROPIC_API_KEY=anthropic-api-key:latest,GOOGLE_API_KEY=google-api-key:latest,AIRTABLE_API_KEY=airtable-api-key:latest,GOOGLE_CLIENT_ID=google-client-id:latest,GOOGLE_CLIENT_SECRET=google-client-secret:latest',
793
+ ]
794
+
795
+ substitutions:
796
+ _VERSION: 'v1'
797
+
798
+ options:
799
+ logging: CLOUD_LOGGING_ONLY
800
+ timeout: 600s
801
+ ```
802
+
803
+ ---
804
+
805
+ ### Phase 5: Custom Domain Mapping
806
+
807
+ #### 5a. Map `chat.conveyorclaims.ai` to Cloud Run
808
+
809
+ ```bash
810
+ # Verify domain ownership (one-time)
811
+ gcloud domains verify conveyorclaims.ai --project=new-project-473022
812
+
813
+ # Map custom domain to the Cloud Run service
814
+ gcloud run domain-mappings create \
815
+ --service=hf-chat-ui \
816
+ --domain=chat.conveyorclaims.ai \
817
+ --region=us-central1 \
818
+ --project=new-project-473022
819
+ ```
820
+
821
+ #### 5b. DNS Configuration
822
+
823
+ Add these DNS records at your domain registrar for `conveyorclaims.ai`:
824
+
825
+ | Type | Name | Value |
826
+ |------|------|-------|
827
+ | CNAME | `chat` | `ghs.googlehosted.com.` |
828
+
829
+ Google manages the SSL certificate automatically. Provisioning takes 15-30 minutes after DNS propagation.
830
+
831
+ #### 5c. Google OAuth Redirect URI
832
+
833
+ Add `https://chat.conveyorclaims.ai/login/callback` to the authorized redirect URIs in the Google Cloud Console:
834
+
835
+ ```
836
+ Console → APIs & Services → Credentials → OAuth 2.0 Client ID
837
+ → Authorized redirect URIs → Add:
838
+ https://chat.conveyorclaims.ai/login/callback
839
+ ```
840
+
841
+ ---
842
+
843
+ ### Phase 6: System Prompt Configuration
844
+
845
+ Create a custom assistant in the Chat UI that embeds Conveyor's identity and formatting rules (from ADR-027):
846
+
847
+ ```json
848
+ {
849
+ "name": "Conveyor AI",
850
+ "preprompt": "You are Conveyor AI, an Insurance Case Management & Revenue Operations Assistant for CLG (Claims Litigation Group).\n\n## Your Capabilities\n- Case management: Look up case status, next steps, due dates, assigned roles\n- Workflow guidance: Step-by-step procedures from CLG workflow documents\n- Revenue forecasting: Analytics and trend analysis\n- Strategy optimization: RL-based settlement strategy simulations\n- Airtable operations: Query and update case records\n\n## Response Style\n- Start conversationally: 'Great question —', 'Yes —', 'Got it —'\n- Use emoji markers: ✅ ❌ ⚠️ 🔑 💰 📌 for scannability\n- Bold field names: **Next Steps**, **Case Status**, **RS Due Date**\n- End with a key takeaway: 🔑 or 🧠 summary\n- Offer proactive follow-up: 'If you want, I can also...'\n- NEVER expose: similarity scores, chunk IDs, function names, JSON, silo numbers\n- ALWAYS attribute sources by document name: 'Referrals Workflow', 'FAQ's'\n\n## Available Tools\nYou have access to Conveyor Tools via MCP. Use them to:\n- search_workflows: Search CLG workflow procedures and FAQs\n- query_database: Run analytics against PostgreSQL\n- manage_case: Look up or update case status via Airtable\n- run_simulation: Run RL strategy simulations\n- airtable_query: Direct Airtable CRUD operations",
851
+ "model": "gpt-5.2"
852
+ }
853
+ ```
854
+
855
+ This can be set as the default assistant via MongoDB or via the `ASSISTANTS` environment variable.
856
+
857
+ ---
858
+
859
+ ## Deployment Runbook
860
+
861
+ ### Quick Deploy (4 commands)
862
+
863
+ All secrets already exist in Google Secret Manager. No new secrets needed.
864
+
865
+ ```bash
866
+ # 1. Deploy Chat UI to Cloud Run (bundled MongoDB sidecar via chat-ui-db image)
867
+ gcloud run deploy hf-chat-ui \
868
+ --image=ghcr.io/huggingface/chat-ui-db:latest \
869
+ --platform=managed \
870
+ --region=us-central1 \
871
+ --port=3000 \
872
+ --memory=2Gi \
873
+ --cpu=2 \
874
+ --min-instances=1 \
875
+ --max-instances=10 \
876
+ --timeout=300 \
877
+ --vpc-connector=conveyor-connector \
878
+ --allow-unauthenticated \
879
+ --set-env-vars="OPENAI_BASE_URL=https://api.openai.com/v1,MONGODB_URL=mongodb://localhost:27017,MONGODB_DB_NAME=conveyor-chat,PUBLIC_APP_NAME=Conveyor AI,PUBLIC_ORIGIN=https://chat.conveyorclaims.ai,LLM_SUMMARIZATION=true,ENABLE_DATA_EXPORT=true,ALLOW_IFRAME=false,USE_LOCAL_WEBSEARCH=true" \
880
+ --set-secrets="OPENAI_API_KEY=openai-api-key:latest,ANTHROPIC_API_KEY=anthropic-api-key:latest,GOOGLE_API_KEY=google-api-key:latest,AIRTABLE_API_KEY=airtable-api-key:latest,GOOGLE_CLIENT_ID=google-client-id:latest,GOOGLE_CLIENT_SECRET=google-client-secret:latest" \
881
+ --project=new-project-473022
882
+
883
+ # 2. Deploy MCP Bridge (connects Chat UI tools to existing Cloud Functions + ruvector-postgres)
884
+ gcloud run deploy mcp-bridge \
885
+ --source=infrastructure/gcp/mcp-bridge \
886
+ --platform=managed \
887
+ --region=us-central1 \
888
+ --port=3001 \
889
+ --memory=512Mi \
890
+ --cpu=1 \
891
+ --vpc-connector=conveyor-connector \
892
+ --allow-unauthenticated \
893
+ --project=new-project-473022
894
+
895
+ # 3. Map custom domain
896
+ gcloud run domain-mappings create \
897
+ --service=hf-chat-ui \
898
+ --domain=chat.conveyorclaims.ai \
899
+ --region=us-central1 \
900
+ --project=new-project-473022
901
+
902
+ # 4. Add DNS CNAME record at registrar
903
+ # chat.conveyorclaims.ai → ghs.googlehosted.com.
904
+ ```
905
+
906
+ ---
907
+
908
+ ## Cost Estimate
909
+
910
+ | Component | Monthly Cost |
911
+ |-----------|-------------|
912
+ | **Cloud Run (hf-chat-ui + MongoDB sidecar)** | ~$8-30 (min-instances=1 for MongoDB persistence) |
913
+ | **Cloud Run (mcp-bridge)** | ~$2-10 (lightweight, auto-scales to 0) |
914
+ | **MongoDB** | $0 (bundled sidecar, no external service) |
915
+ | **ruvector-postgres** | $0 (already running for existing services) |
916
+ | **OpenAI API (GPT-5)** | Variable — depends on usage |
917
+ | **Google/Anthropic APIs** | Variable — uses existing Secret Manager keys |
918
+ | **SSL Certificate** | $0 (Google-managed) |
919
+ | **Custom Domain** | $0 (CNAME mapping is free) |
920
+ | **Total Infrastructure** | ~$10-40/month + AI provider usage |
921
+
922
+ ---
923
+
924
+ ## Consequences
925
+
926
+ ### Positive
927
+ - **Immediate GPT-5 access** — no custom UI development needed
928
+ - **Multi-model selection** — users choose GPT-5, GPT-5-mini, GPT-4o, o3, etc.
929
+ - **MCP tool integration** — reuses all existing Cloud Functions without modification
930
+ - **Production-grade** — conversation history, auth, streaming, voice input out of the box
931
+ - **Community maintained** — 10,400+ stars, active development by HuggingFace
932
+ - **Zero disruption** — existing chat system continues operating independently
933
+ - **Cost effective** — MongoDB sidecar eliminates external DB cost, ruvector-postgres already running
934
+ - **Multi-provider resilience** — if one AI provider is down, users switch to another
935
+
936
+ ### Negative
937
+ - **SvelteKit, not React** — different tech stack from existing chat system; team needs familiarity
938
+ - **MongoDB sidecar** — Chat UI requires MongoDB internally; sidecar approach means min-instances=1 for data persistence (Cloud Run stateless otherwise)
939
+ - **Less control** — upstream UI changes may require adaptation; customization is via env vars and assistants, not code
940
+ - **MCP bridge overhead** — extra network hop for tool calls (mitigated by Cloud Run co-location)
941
+
942
+ ### Risks & Mitigations
943
+ | Risk | Mitigation |
944
+ |------|-----------|
945
+ | MongoDB sidecar data loss on scale-to-zero | Set min-instances=1; conversations are recoverable (AI can regenerate) |
946
+ | OpenAI API costs spike | Set `USAGE_LIMITS` to cap messages per minute; use gpt-5-nano for simple queries |
947
+ | HuggingFace Chat UI breaking changes | Pin to specific image tag, test before upgrading |
948
+ | MCP bridge latency | Co-locate in us-central1, same VPC as Cloud Functions |
949
+ | Custom domain SSL delay | Allow 24h for certificate provisioning |
950
+ | Provider key expiration | All keys in Secret Manager — rotate without redeployment |
951
+
952
+ ---
953
+
954
+ ## Updated Architecture Diagram (Full System)
955
+
956
+ ```
957
+ ┌──────────────────────────────────────────────────────────────────────────────────┐
958
+ │ GOOGLE CLOUD PLATFORM │
959
+ │ Project: new-project-473022 │
960
+ ├──────────────────────────────────────────────────────────────────────────────────┤
961
+ │ │
962
+ │ ┌─────────────────────────────────────────────────────────────────────────────┐ │
963
+ │ │ VPC Network (conveyor-vpc) │ │
964
+ │ │ │ │
965
+ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │
966
+ │ │ │ Cloud Run Services │ │ │
967
+ │ │ │ │ │ │
968
+ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │
969
+ │ │ │ │ hf-chat-ui │ │ chat-system │ │ mcp-bridge │ │ │ │
970
+ │ │ │ │ (NEW) │ │ (existing) │ │ (NEW) │ │ │ │
971
+ │ │ │ │ │ │ │ │ │ │ │ │
972
+ │ │ │ │ SvelteKit │ │ React+Vite │ │ MCP Server │ │ │ │
973
+ │ │ │ │ GPT-5 models │ │ Gemini │ │ Tool bridge │ │ │ │
974
+ │ │ │ │ Port 3000 │ │ Port 8080 │ │ Port 3001 │ │ │ │
975
+ │ │ │ └──────┬───────┘ └──────────────┘ └──────┬───────┘ │ │ │
976
+ │ │ │ │ │ │ │ │
977
+ │ │ │ │chat.conveyorclaims.ai │ │ │ │
978
+ │ │ └─────────┼─────────────────────────────────────┼──────────────┘ │ │
979
+ │ │ │ │ │ │
980
+ │ │ ┌────────┼─────────────────────────────────────┼───────────────────┐ │ │
981
+ │ │ │ │ Cloud Functions │ │ │ │
982
+ │ │ │ │ │ │ │ │
983
+ │ │ │ │ • airtable-agent ◄─────────────────┤ │ │ │
984
+ │ │ │ │ • db-query-agent ◄─────────────────┤ │ │ │
985
+ │ │ │ │ • case-manager ◄─────────────────┤ │ │ │
986
+ │ │ │ │ • simulation-agent◄─────────────────┤ │ │ │
987
+ │ │ │ │ • workflow-search ◄─────────────────┘ │ │ │
988
+ │ │ │ │ │ │ │
989
+ │ │ └────────┼──────────────────────────────────────────────────────────┘ │ │
990
+ │ │ │ │ │
991
+ │ │ ┌────────▼─────────┐ │ │
992
+ │ │ │ ruvector-postgres│ │ │
993
+ │ │ │ 10.128.0.2:5432 │ │ │
994
+ │ │ │ PostgreSQL 17.7 │ │ │
995
+ │ │ │ ruvector 2.0.1 │ │ │
996
+ │ │ └──────────────────┘ │ │
997
+ │ └───────────────────────────────────────────────────────────────────────────────┘ │
998
+ │ │
999
+ │ ┌───────────────────────────┐ ┌───────────────────────────────────┐ │
1000
+ │ │ Secret Manager │ │ AI Providers (Multi-Provider) │ │
1001
+ │ │ • openai-api-key │ │ • OpenAI → GPT-5 family │ │
1002
+ │ │ • anthropic-api-key │ │ • Google → Gemini 2.5 │ │
1003
+ │ │ • google-api-key │ │ • Anthropic → Claude Sonnet 4 │ │
1004
+ │ │ • airtable-api-key │ └───────────────────────────────────┘ │
1005
+ │ │ • ruvector-db-password │ │
1006
+ │ └───────────────────────────┘ │
1007
+ └─────────────────────────────────────────────────────────────────────────────────────┘
1008
+ ```
1009
+
1010
+ ---
1011
+
1012
+ ## Service Inventory (Post-Implementation)
1013
+
1014
+ | Service | Domain | Purpose | Tools/Models |
1015
+ |---------|--------|---------|--------------|
1016
+ | **hf-chat-ui** (NEW) | `chat.conveyorclaims.ai` | Multi-provider chat with 3 MCP tool servers | GPT-5.2, GPT-5, GPT-5-mini, GPT-4o, o3, Gemini 2.5, Claude Sonnet 4 |
1017
+ | **mcp-bridge** (NEW) | internal | Custom MCP → Cloud Functions + ruvector-postgres | 5 tools (search, query, case, sim, airtable) |
1018
+ | **Airtable MCP** (external) | `mcp.airtable.com` | Official Airtable direct access | Schema browse, CRUD, search |
1019
+ | **Google Drive MCP** (external) | `mcp.googleapis.com` | Official Google Drive access | File search, doc read, sheets |
1020
+ | **chat-system** (existing) | `chat-system-*.run.app` | Gemini-powered workflow chat | gemini-2.5-pro/flash |
1021
+ | **mcp-server** (existing) | `mcp-server-*.run.app` | General MCP server | N/A |
1022
+
1023
+ ---
1024
+
1025
+ ## Timeline
1026
+
1027
+ | Phase | Duration | Deliverable |
1028
+ |-------|----------|-------------|
1029
+ | Phase 1: MongoDB Atlas | 1 hour | Free cluster + secret in Secret Manager |
1030
+ | Phase 2: MCP Bridge | 2-3 hours | Cloud Run service with 5 tools |
1031
+ | Phase 3: Model Config | 30 min | MODELS env var with 7 GPT-5 variants |
1032
+ | Phase 4: Chat UI Deploy | 1-2 hours | Cloud Run service from pre-built image |
1033
+ | Phase 5: Domain Mapping | 1-24 hours | `chat.conveyorclaims.ai` live (DNS propagation) |
1034
+ | Phase 6: System Prompt | 30 min | Default Conveyor AI assistant |
1035
+ | **Total** | **~1 day** | Full deployment |
1036
+
1037
+ ---
1038
+
1039
+ ## Next Steps
1040
+
1041
+ 1. **Approve this ADR** and proceed to Phase 1 (MongoDB Atlas)
1042
+ 2. Build and deploy the MCP Bridge server (Phase 2)
1043
+ 3. Deploy Chat UI with GPT-5 models (Phases 3-4)
1044
+ 4. Configure DNS and custom domain (Phase 5)
1045
+ 5. Test end-to-end: model selection → tool calling → workflow search → response
1046
+ 6. Configure Conveyor AI assistant with system prompt (Phase 6)
1047
+ 7. Update ADR-028 to reference this parallel deployment
1048
+
1049
+ ---
1050
+
1051
+ ## Post-Deployment Updates (2026-03-03)
1052
+
1053
+ ### Update 1: Google OIDC Authentication
1054
+
1055
+ Added Google OAuth login to restrict access to authenticated users only.
1056
+
1057
+ **Configuration approach:** HF Chat UI reads OIDC settings from the `DOTENV_LOCAL` environment variable, which acts as an in-memory `.env.local` file. Individual `OPENID_*` env vars are NOT read by Chat UI — they must be inside `DOTENV_LOCAL`.
1058
+
1059
+ **OAuth client:** `245235083640-gkbo4otq57lqeisuigcat0bg037f49oc.apps.googleusercontent.com` (Web Application type)
1060
+
1061
+ **Secret:** `google-client-secret` in Secret Manager (version 2) — `GOCSPX-QzuZ-...`
1062
+
1063
+ **Redirect URI:** `https://chat.conveyorclaims.ai/login/callback` (added manually in Google Cloud Console → APIs & Services → Credentials)
1064
+
1065
+ **OIDC env vars added to DOTENV_LOCAL:**
1066
+ ```ini
1067
+ OPENID_PROVIDER_URL=https://accounts.google.com
1068
+ OPENID_CLIENT_ID=245235083640-gkbo4otq57lqeisuigcat0bg037f49oc.apps.googleusercontent.com
1069
+ OPENID_SCOPES=openid profile email
1070
+ OPENID_NAME_CLAIM=name
1071
+ COOKIE_SECURE=true
1072
+ COOKIE_SAMESITE=lax
1073
+ ```
1074
+
1075
+ **Key lesson:** IAP OAuth clients (`*-9lt8...`) cannot be used for custom web OIDC flows — they are locked to IAP-specific redirect patterns. Only standard Web Application OAuth clients work.
1076
+
1077
+ **Files modified:**
1078
+ - `infrastructure/gcp/hf-chat-ui/update-preprompt.js` — added OIDC vars to DOTENV_LOCAL output
1079
+ - `infrastructure/gcp/hf-chat-ui/cloudbuild.yaml` — added OIDC env vars + `OPENID_CLIENT_SECRET` secret binding
1080
+ - `infrastructure/gcp/hf-chat-ui/deploy.sh` — added OIDC env vars + secret binding
1081
+
1082
+ ### Update 2: Branded Welcome Animation
1083
+
1084
+ Replaced the default HuggingFace `omni-welcome.gif` with a branded "Conveyor AI" animated GIF matching the Three.js `AnimatedBackground.tsx` aesthetic from the existing chat system.
1085
+
1086
+ **Design:**
1087
+ - 480x320px, 90 frames (3s @ 30fps), ~1.75 MB
1088
+ - Dark background `#0d0d1a`
1089
+ - Rotating wireframe geometric shapes (icosahedron + octahedron) in cyan/blue/indigo
1090
+ - Scattered glowing dots matching blue-500/sky-500/indigo-500 palette
1091
+ - "Conveyor AI" text centered with subtle glow effect
1092
+
1093
+ **Implementation:**
1094
+ - `infrastructure/gcp/hf-chat-ui/generate-welcome.cjs` — Node.js script using `canvas` + `gif-encoder-2` (`.cjs` extension required because root `package.json` has `"type": "module"`)
1095
+ - `infrastructure/gcp/hf-chat-ui/Dockerfile` — extends `ghcr.io/huggingface/chat-ui-db:latest`, copies branded GIF to `/app/build/client/chatui/omni-welcome.gif` and `/app/static/chatui/omni-welcome.gif`
1096
+ - `infrastructure/gcp/hf-chat-ui/cloudbuild.yaml` — changed from pull+tag to Docker build with custom Dockerfile
1097
+
1098
+ ### Update 3: MCP Bridge Tool Mapping Fixes
1099
+
1100
+ Fixed all 5 tool-to-Cloud-Function mappings in the MCP Bridge. Every tool was sending incorrect or missing parameters to its backend Cloud Function.
1101
+
1102
+ | Tool | Issue | Fix |
1103
+ |------|-------|-----|
1104
+ | `search_workflows` | Was working | No change needed |
1105
+ | `query_database` | Missing `action` field entirely | Added `action: "nl_query"` |
1106
+ | `manage_case` | Sent `status` as action, backend expects `get` | Map `status` → `get`, `next_steps` → `get` |
1107
+ | `run_simulation` | Missing `action` field, wrong field names | Added `action: "run_qlearning"`, mapped `scenario` → `caseType`, `episodes` → `iterations` |
1108
+ | `airtable_query` | Wrong field name `table` (backend expects `tableName`), wrong action names | Map `list` → `query`, `get` → `get_case_status`, `create`/`update` → `upsert` |
1109
+
1110
+ **File modified:** `infrastructure/gcp/mcp-bridge/index.js`
1111
+
1112
+ ### Update 4: Natural Language to SQL (db-query-agent)
1113
+
1114
+ Added `nl_query` action to the db-query-agent Cloud Function. This enables natural language questions like "How many cases were opened this month?" to be converted to SQL via Gemini.
1115
+
1116
+ **Flow:** Natural language → Gemini generates SQL → validate (no DROP/DELETE) → execute against ruvector-postgres → return results
1117
+
1118
+ **File modified:** `infrastructure/gcp/functions/db-query-agent/index.js`
1119
+
1120
+ ### Update 5: Multi-Provider Chat Completions Proxy
1121
+
1122
+ Added an OpenAI-compatible `/chat/completions` proxy to the MCP Bridge that routes requests to the correct AI provider based on model name. This enables HF Chat UI to use `OPENAI_BASE_URL` pointing to the MCP Bridge, which then routes:
1123
+ - `gpt-*`, `o*-*` models → OpenAI API
1124
+ - `gemini-*` models → Google Generative Language API
1125
+
1126
+ Also added `/models` endpoint returning only the curated model list (7 models) instead of the full OpenAI model catalog (114+ models).
1127
+
1128
+ **File modified:** `infrastructure/gcp/mcp-bridge/index.js`
1129
+
1130
+ ### Deployment Status (2026-03-03)
1131
+
1132
+ | Component | Deployed? | Notes |
1133
+ |-----------|-----------|-------|
1134
+ | HF Chat UI (with OIDC + branded GIF) | Yes | Custom Docker image with Dockerfile |
1135
+ | MCP Bridge (with tool fixes + proxy) | Yes | All 5 tools validated working |
1136
+ | db-query-agent (with nl_query) | Yes | Entry point: `dbQueryAgent` |
1137
+
1138
+ ---
1139
+
1140
+ ## Post-Deployment Updates (2026-03-04)
1141
+
1142
+ ### Update 6: Server-Side API Key Fix
1143
+
1144
+ Fixed 401 errors where the MCP Bridge was forwarding the user's Google OAuth token to OpenAI instead of using the server-side API key.
1145
+
1146
+ **Root cause:** `getKey: (req) => req.headers.authorization?.replace("Bearer ", "") || process.env.OPENAI_API_KEY` extracted the OIDC session token `ya29.A0A...` and sent it to OpenAI.
1147
+
1148
+ **Fix:** Changed to `getKey: () => process.env.OPENAI_API_KEY` — always use server-side key. Added `OPENAI_API_KEY=openai-api-key:latest` to MCP bridge `cloudbuild.yaml` `--set-secrets`.
1149
+
1150
+ ### Update 7: Airtable Table Name Mapping
1151
+
1152
+ Added `TABLE_MAP` to the MCP Bridge to translate friendly table names to actual Airtable table names. The LLM sends `"table": "Cases"` but Airtable expects `"All Cases (dev)"`.
1153
+
1154
+ | Friendly Name | Actual Airtable Name |
1155
+ |---------------|---------------------|
1156
+ | Cases | All Cases (dev) |
1157
+ | Managed Cases | Managed Cases (dev) |
1158
+ | Clients / Contacts | Contacts |
1159
+ | Carriers / Partners | Co-Counsel & Referral Partners |
1160
+ | Users | Conveyor Users |
1161
+ | Invoices | Invoices |
1162
+ | Payments | Payments |
1163
+ | Emails | Emails |
1164
+
1165
+ ### Update 8: Case Search by Number and Client Name
1166
+
1167
+ Enhanced `airtable_query` tool to support searching by case number or client name instead of only listing all records.
1168
+
1169
+ - Added `search` action and `search` parameter to tool schema
1170
+ - Case number patterns (e.g., `C-01748`) route to `get_case_status` for precise lookup
1171
+ - Name searches use `query` with `{search: searchTerm}` for fuzzy matching
1172
+ - `manage_case` status/next_steps now route to airtable-agent's `get_case_status` for better results
1173
+
1174
+ ### Update 9: Table-Aware Search Formula
1175
+
1176
+ Fixed "Unknown field names" errors when searching non-case tables. The airtable-agent search formula previously hardcoded `{Case Number}` which doesn't exist in tables like `Co-Counsel & Referral Partners`.
1177
+
1178
+ **Fix:** Added `TABLE_SEARCH_FIELDS` map in `airtable-agent/index.js`:
1179
+
1180
+ | Table | Search Fields |
1181
+ |-------|--------------|
1182
+ | All Cases (dev) | Case Number |
1183
+ | Contacts | Full Name, Email |
1184
+ | Co-Counsel & Referral Partners | Partner Name |
1185
+ | Invoices | Invoice Number, Reference Number |
1186
+ | Conveyor Users | Full Name, Email Address |
1187
+
1188
+ ### Update 10: Multi-Provider Model Catalog (17 Models)
1189
+
1190
+ Expanded from 7 models to 17 models across 6 providers. Gemini 2.5 Pro set as default (first position).
1191
+
1192
+ | Provider | Route | Models |
1193
+ |----------|-------|--------|
1194
+ | Google (direct) | Gemini API | Gemini 2.5 Pro (Default), Gemini 2.5 Flash |
1195
+ | OpenAI (direct) | OpenAI API | GPT-5.2 Pro, GPT-5, GPT-5 Mini, GPT-4o, o4-mini |
1196
+ | Anthropic | OpenRouter | Claude Sonnet 4.6, Claude Opus 4.6 |
1197
+ | Google next-gen | OpenRouter | Gemini 3 Pro Preview, Gemini 3 Flash Preview |
1198
+ | DeepSeek | OpenRouter | DeepSeek V3.2 |
1199
+ | Mistral | OpenRouter | Mistral Large, Devstral |
1200
+ | xAI | OpenRouter | Grok 4.1 Fast |
1201
+ | OpenAI latest | OpenRouter | GPT-5.3 Chat, GPT-5.3 Codex |
1202
+
1203
+ **MCP Bridge routing logic:** Models with `/` in the name (e.g., `anthropic/claude-sonnet-4.6`) route to OpenRouter. Models starting with `gemini-` route to Google direct. All others route to OpenAI direct.
1204
+
1205
+ ### Update 11: Docker-Baked Configuration
1206
+
1207
+ Moved MODELS config from Cloud Run env vars to Docker image `.env.local` file. The full MODELS JSON with 17 model preprompts exceeds the 32KB Cloud Run env var limit.
1208
+
1209
+ **Architecture:** `update-preprompt.js` generates `dotenv-local.txt` → Dockerfile copies to `/app/.env.local` → HF Chat UI reads at startup. Cloud Run env vars provide secrets only (API keys via Secret Manager).
1210
+
1211
+ ### Update 12: PWA Icon and Session Cookies
1212
+
1213
+ - Added 144x144 PNG icon to Dockerfile (fixes `/chat/chatui/icon-144x144.png` 404)
1214
+ - Added `COOKIE_MAX_AGE=604800` (7-day sessions) to reduce OAuth redirect frequency
1215
+
1216
+ ### Deployment Status (2026-03-04)
1217
+
1218
+ | Component | Version | Status |
1219
+ |-----------|---------|--------|
1220
+ | HF Chat UI | hf-chat-ui-00026 | Live — 17 models, OIDC, branded GIF, PWA icon |
1221
+ | MCP Bridge | v2026030419xx | Live — OpenRouter routing, table mapping, search |
1222
+ | airtable-agent | Gen2 | Live — table-aware search formula |
1223
+ | db-query-agent | Gen2 | Live — nl_query action |
1224
+
1225
+ ---
1226
+
1227
+ ## Related ADRs
1228
+
1229
+ | ADR | Relationship |
1230
+ |-----|-------------|
1231
+ | ADR-014 | Existing chat system architecture (continues independently) |
1232
+ | ADR-015 | Cloud Functions reused via MCP Bridge |
1233
+ | ADR-022 | Workflow documents in ruvector-postgres searched via tools |
1234
+ | ADR-024 | Workflow context injection pattern adapted for MCP tools |
1235
+ | ADR-027 | Response formatting rules carried into system prompt |
1236
+ | ADR-028 | OpenAI GPT-5 integration in existing chat system (complementary) |