@revealui/ai 0.1.0

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 (397) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.commercial +112 -0
  3. package/README.md +314 -0
  4. package/dist/a2a/card.d.ts +26 -0
  5. package/dist/a2a/card.d.ts.map +1 -0
  6. package/dist/a2a/card.js +173 -0
  7. package/dist/a2a/handler.d.ts +26 -0
  8. package/dist/a2a/handler.d.ts.map +1 -0
  9. package/dist/a2a/handler.js +170 -0
  10. package/dist/a2a/index.d.ts +10 -0
  11. package/dist/a2a/index.d.ts.map +1 -0
  12. package/dist/a2a/index.js +9 -0
  13. package/dist/a2a/task-store.d.ts +42 -0
  14. package/dist/a2a/task-store.d.ts.map +1 -0
  15. package/dist/a2a/task-store.js +99 -0
  16. package/dist/audit/emitter.d.ts +34 -0
  17. package/dist/audit/emitter.d.ts.map +1 -0
  18. package/dist/audit/emitter.js +34 -0
  19. package/dist/audit/index.d.ts +44 -0
  20. package/dist/audit/index.d.ts.map +1 -0
  21. package/dist/audit/index.js +48 -0
  22. package/dist/audit/observer.d.ts +108 -0
  23. package/dist/audit/observer.d.ts.map +1 -0
  24. package/dist/audit/observer.js +271 -0
  25. package/dist/audit/policy.d.ts +70 -0
  26. package/dist/audit/policy.d.ts.map +1 -0
  27. package/dist/audit/policy.js +209 -0
  28. package/dist/audit/store.d.ts +42 -0
  29. package/dist/audit/store.d.ts.map +1 -0
  30. package/dist/audit/store.js +80 -0
  31. package/dist/audit/types.d.ts +169 -0
  32. package/dist/audit/types.d.ts.map +1 -0
  33. package/dist/audit/types.js +80 -0
  34. package/dist/client/hooks/index.d.ts +22 -0
  35. package/dist/client/hooks/index.d.ts.map +1 -0
  36. package/dist/client/hooks/index.js +21 -0
  37. package/dist/client/hooks/useAgentContext.d.ts +30 -0
  38. package/dist/client/hooks/useAgentContext.d.ts.map +1 -0
  39. package/dist/client/hooks/useAgentContext.js +161 -0
  40. package/dist/client/hooks/useAgentEvents.d.ts +126 -0
  41. package/dist/client/hooks/useAgentEvents.d.ts.map +1 -0
  42. package/dist/client/hooks/useAgentEvents.js +232 -0
  43. package/dist/client/hooks/useAgentStream.d.ts +44 -0
  44. package/dist/client/hooks/useAgentStream.d.ts.map +1 -0
  45. package/dist/client/hooks/useAgentStream.js +101 -0
  46. package/dist/client/hooks/useEpisodicMemory.d.ts +25 -0
  47. package/dist/client/hooks/useEpisodicMemory.d.ts.map +1 -0
  48. package/dist/client/hooks/useEpisodicMemory.js +174 -0
  49. package/dist/client/hooks/useWorkingMemory.d.ts +57 -0
  50. package/dist/client/hooks/useWorkingMemory.d.ts.map +1 -0
  51. package/dist/client/hooks/useWorkingMemory.js +276 -0
  52. package/dist/client/index.d.ts +14 -0
  53. package/dist/client/index.d.ts.map +1 -0
  54. package/dist/client/index.js +13 -0
  55. package/dist/embeddings/index.d.ts +51 -0
  56. package/dist/embeddings/index.d.ts.map +1 -0
  57. package/dist/embeddings/index.js +73 -0
  58. package/dist/index.d.ts +83 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +103 -0
  61. package/dist/inference/context-assembly.d.ts +27 -0
  62. package/dist/inference/context-assembly.d.ts.map +1 -0
  63. package/dist/inference/context-assembly.js +81 -0
  64. package/dist/inference/overflow-compressor.d.ts +17 -0
  65. package/dist/inference/overflow-compressor.d.ts.map +1 -0
  66. package/dist/inference/overflow-compressor.js +40 -0
  67. package/dist/inference/runRag.d.ts +35 -0
  68. package/dist/inference/runRag.d.ts.map +1 -0
  69. package/dist/inference/runRag.js +53 -0
  70. package/dist/ingestion/bm25.d.ts +29 -0
  71. package/dist/ingestion/bm25.d.ts.map +1 -0
  72. package/dist/ingestion/bm25.js +161 -0
  73. package/dist/ingestion/cms-indexer.d.ts +39 -0
  74. package/dist/ingestion/cms-indexer.d.ts.map +1 -0
  75. package/dist/ingestion/cms-indexer.js +74 -0
  76. package/dist/ingestion/file-parsers.d.ts +51 -0
  77. package/dist/ingestion/file-parsers.d.ts.map +1 -0
  78. package/dist/ingestion/file-parsers.js +247 -0
  79. package/dist/ingestion/hybrid-search.d.ts +22 -0
  80. package/dist/ingestion/hybrid-search.d.ts.map +1 -0
  81. package/dist/ingestion/hybrid-search.js +63 -0
  82. package/dist/ingestion/index.d.ts +9 -0
  83. package/dist/ingestion/index.d.ts.map +1 -0
  84. package/dist/ingestion/index.js +8 -0
  85. package/dist/ingestion/pipeline.d.ts +35 -0
  86. package/dist/ingestion/pipeline.d.ts.map +1 -0
  87. package/dist/ingestion/pipeline.js +114 -0
  88. package/dist/ingestion/rag-vector-service.d.ts +34 -0
  89. package/dist/ingestion/rag-vector-service.d.ts.map +1 -0
  90. package/dist/ingestion/rag-vector-service.js +98 -0
  91. package/dist/ingestion/reranker.d.ts +10 -0
  92. package/dist/ingestion/reranker.d.ts.map +1 -0
  93. package/dist/ingestion/reranker.js +41 -0
  94. package/dist/ingestion/text-splitter.d.ts +25 -0
  95. package/dist/ingestion/text-splitter.d.ts.map +1 -0
  96. package/dist/ingestion/text-splitter.js +119 -0
  97. package/dist/llm/cache-utils.d.ts +146 -0
  98. package/dist/llm/cache-utils.d.ts.map +1 -0
  99. package/dist/llm/cache-utils.js +204 -0
  100. package/dist/llm/client.d.ts +134 -0
  101. package/dist/llm/client.d.ts.map +1 -0
  102. package/dist/llm/client.js +497 -0
  103. package/dist/llm/key-validator.d.ts +25 -0
  104. package/dist/llm/key-validator.d.ts.map +1 -0
  105. package/dist/llm/key-validator.js +101 -0
  106. package/dist/llm/provider-health.d.ts +40 -0
  107. package/dist/llm/provider-health.d.ts.map +1 -0
  108. package/dist/llm/provider-health.js +97 -0
  109. package/dist/llm/providers/anthropic.d.ts +31 -0
  110. package/dist/llm/providers/anthropic.d.ts.map +1 -0
  111. package/dist/llm/providers/anthropic.js +248 -0
  112. package/dist/llm/providers/base.d.ts +111 -0
  113. package/dist/llm/providers/base.d.ts.map +1 -0
  114. package/dist/llm/providers/base.js +6 -0
  115. package/dist/llm/providers/groq.d.ts +23 -0
  116. package/dist/llm/providers/groq.d.ts.map +1 -0
  117. package/dist/llm/providers/groq.js +27 -0
  118. package/dist/llm/providers/ollama.d.ts +27 -0
  119. package/dist/llm/providers/ollama.d.ts.map +1 -0
  120. package/dist/llm/providers/ollama.js +48 -0
  121. package/dist/llm/providers/openai.d.ts +19 -0
  122. package/dist/llm/providers/openai.d.ts.map +1 -0
  123. package/dist/llm/providers/openai.js +245 -0
  124. package/dist/llm/providers/vultr.d.ts +18 -0
  125. package/dist/llm/providers/vultr.d.ts.map +1 -0
  126. package/dist/llm/providers/vultr.js +168 -0
  127. package/dist/llm/response-cache.d.ts +166 -0
  128. package/dist/llm/response-cache.d.ts.map +1 -0
  129. package/dist/llm/response-cache.js +233 -0
  130. package/dist/llm/semantic-cache.d.ts +179 -0
  131. package/dist/llm/semantic-cache.d.ts.map +1 -0
  132. package/dist/llm/semantic-cache.js +306 -0
  133. package/dist/llm/server.d.ts +14 -0
  134. package/dist/llm/server.d.ts.map +1 -0
  135. package/dist/llm/server.js +15 -0
  136. package/dist/llm/token-counter.d.ts +48 -0
  137. package/dist/llm/token-counter.d.ts.map +1 -0
  138. package/dist/llm/token-counter.js +77 -0
  139. package/dist/llm/workspace-provider-config.d.ts +38 -0
  140. package/dist/llm/workspace-provider-config.d.ts.map +1 -0
  141. package/dist/llm/workspace-provider-config.js +47 -0
  142. package/dist/memory/agent/context-manager.d.ts +148 -0
  143. package/dist/memory/agent/context-manager.d.ts.map +1 -0
  144. package/dist/memory/agent/context-manager.js +284 -0
  145. package/dist/memory/agent/index.d.ts +7 -0
  146. package/dist/memory/agent/index.d.ts.map +1 -0
  147. package/dist/memory/agent/index.js +6 -0
  148. package/dist/memory/crdt/index.d.ts +13 -0
  149. package/dist/memory/crdt/index.d.ts.map +1 -0
  150. package/dist/memory/crdt/index.js +12 -0
  151. package/dist/memory/crdt/lww-register.d.ts +108 -0
  152. package/dist/memory/crdt/lww-register.d.ts.map +1 -0
  153. package/dist/memory/crdt/lww-register.js +169 -0
  154. package/dist/memory/crdt/or-set.d.ts +141 -0
  155. package/dist/memory/crdt/or-set.d.ts.map +1 -0
  156. package/dist/memory/crdt/or-set.js +291 -0
  157. package/dist/memory/crdt/pn-counter.d.ts +116 -0
  158. package/dist/memory/crdt/pn-counter.d.ts.map +1 -0
  159. package/dist/memory/crdt/pn-counter.js +174 -0
  160. package/dist/memory/crdt/vector-clock.d.ts +115 -0
  161. package/dist/memory/crdt/vector-clock.d.ts.map +1 -0
  162. package/dist/memory/crdt/vector-clock.js +179 -0
  163. package/dist/memory/errors/index.d.ts +56 -0
  164. package/dist/memory/errors/index.d.ts.map +1 -0
  165. package/dist/memory/errors/index.js +85 -0
  166. package/dist/memory/index.d.ts +21 -0
  167. package/dist/memory/index.d.ts.map +1 -0
  168. package/dist/memory/index.js +20 -0
  169. package/dist/memory/persistence/crdt-persistence.d.ts +85 -0
  170. package/dist/memory/persistence/crdt-persistence.d.ts.map +1 -0
  171. package/dist/memory/persistence/crdt-persistence.js +204 -0
  172. package/dist/memory/persistence/index.d.ts +7 -0
  173. package/dist/memory/persistence/index.d.ts.map +1 -0
  174. package/dist/memory/persistence/index.js +6 -0
  175. package/dist/memory/preferences/index.d.ts +7 -0
  176. package/dist/memory/preferences/index.d.ts.map +1 -0
  177. package/dist/memory/preferences/index.js +6 -0
  178. package/dist/memory/preferences/user-preferences-manager.d.ts +133 -0
  179. package/dist/memory/preferences/user-preferences-manager.d.ts.map +1 -0
  180. package/dist/memory/preferences/user-preferences-manager.js +342 -0
  181. package/dist/memory/services/index.d.ts +8 -0
  182. package/dist/memory/services/index.d.ts.map +1 -0
  183. package/dist/memory/services/index.js +6 -0
  184. package/dist/memory/services/node-id-service.d.ts +75 -0
  185. package/dist/memory/services/node-id-service.d.ts.map +1 -0
  186. package/dist/memory/services/node-id-service.js +190 -0
  187. package/dist/memory/stores/episodic-memory.d.ts +182 -0
  188. package/dist/memory/stores/episodic-memory.d.ts.map +1 -0
  189. package/dist/memory/stores/episodic-memory.js +378 -0
  190. package/dist/memory/stores/index.d.ts +16 -0
  191. package/dist/memory/stores/index.d.ts.map +1 -0
  192. package/dist/memory/stores/index.js +15 -0
  193. package/dist/memory/stores/procedural-memory.d.ts +89 -0
  194. package/dist/memory/stores/procedural-memory.d.ts.map +1 -0
  195. package/dist/memory/stores/procedural-memory.js +152 -0
  196. package/dist/memory/stores/semantic-memory.d.ts +92 -0
  197. package/dist/memory/stores/semantic-memory.d.ts.map +1 -0
  198. package/dist/memory/stores/semantic-memory.js +155 -0
  199. package/dist/memory/stores/working-memory.d.ts +225 -0
  200. package/dist/memory/stores/working-memory.d.ts.map +1 -0
  201. package/dist/memory/stores/working-memory.js +336 -0
  202. package/dist/memory/utils/deep-clone.d.ts +10 -0
  203. package/dist/memory/utils/deep-clone.d.ts.map +1 -0
  204. package/dist/memory/utils/deep-clone.js +9 -0
  205. package/dist/memory/utils/index.d.ts +8 -0
  206. package/dist/memory/utils/index.d.ts.map +1 -0
  207. package/dist/memory/utils/index.js +7 -0
  208. package/dist/memory/utils/logger.d.ts +21 -0
  209. package/dist/memory/utils/logger.d.ts.map +1 -0
  210. package/dist/memory/utils/logger.js +62 -0
  211. package/dist/memory/utils/sql-helpers.d.ts +97 -0
  212. package/dist/memory/utils/sql-helpers.d.ts.map +1 -0
  213. package/dist/memory/utils/sql-helpers.js +214 -0
  214. package/dist/memory/utils/validation.d.ts +62 -0
  215. package/dist/memory/utils/validation.d.ts.map +1 -0
  216. package/dist/memory/utils/validation.js +244 -0
  217. package/dist/memory/vector/index.d.ts +12 -0
  218. package/dist/memory/vector/index.d.ts.map +1 -0
  219. package/dist/memory/vector/index.js +14 -0
  220. package/dist/memory/vector/vector-memory-service.d.ts +88 -0
  221. package/dist/memory/vector/vector-memory-service.d.ts.map +1 -0
  222. package/dist/memory/vector/vector-memory-service.js +335 -0
  223. package/dist/observability/logger.d.ts +79 -0
  224. package/dist/observability/logger.d.ts.map +1 -0
  225. package/dist/observability/logger.js +165 -0
  226. package/dist/observability/metrics.d.ts +43 -0
  227. package/dist/observability/metrics.d.ts.map +1 -0
  228. package/dist/observability/metrics.js +197 -0
  229. package/dist/observability/query.d.ts +150 -0
  230. package/dist/observability/query.d.ts.map +1 -0
  231. package/dist/observability/query.js +339 -0
  232. package/dist/observability/types.d.ts +140 -0
  233. package/dist/observability/types.d.ts.map +1 -0
  234. package/dist/observability/types.js +6 -0
  235. package/dist/orchestration/agent.d.ts +98 -0
  236. package/dist/orchestration/agent.d.ts.map +1 -0
  237. package/dist/orchestration/agent.js +6 -0
  238. package/dist/orchestration/defaults.d.ts +21 -0
  239. package/dist/orchestration/defaults.d.ts.map +1 -0
  240. package/dist/orchestration/defaults.js +22 -0
  241. package/dist/orchestration/memory-integration.d.ts +58 -0
  242. package/dist/orchestration/memory-integration.d.ts.map +1 -0
  243. package/dist/orchestration/memory-integration.js +130 -0
  244. package/dist/orchestration/orchestrator.d.ts +67 -0
  245. package/dist/orchestration/orchestrator.d.ts.map +1 -0
  246. package/dist/orchestration/orchestrator.js +174 -0
  247. package/dist/orchestration/runtime.d.ts +82 -0
  248. package/dist/orchestration/runtime.d.ts.map +1 -0
  249. package/dist/orchestration/runtime.js +251 -0
  250. package/dist/orchestration/streaming-runtime.d.ts +36 -0
  251. package/dist/orchestration/streaming-runtime.d.ts.map +1 -0
  252. package/dist/orchestration/streaming-runtime.js +175 -0
  253. package/dist/orchestration/ticket-agent.d.ts +70 -0
  254. package/dist/orchestration/ticket-agent.d.ts.map +1 -0
  255. package/dist/orchestration/ticket-agent.js +146 -0
  256. package/dist/skills/activation/index.d.ts +7 -0
  257. package/dist/skills/activation/index.d.ts.map +1 -0
  258. package/dist/skills/activation/index.js +6 -0
  259. package/dist/skills/activation/skill-activator.d.ts +68 -0
  260. package/dist/skills/activation/skill-activator.d.ts.map +1 -0
  261. package/dist/skills/activation/skill-activator.js +224 -0
  262. package/dist/skills/catalog/catalog-search.d.ts +55 -0
  263. package/dist/skills/catalog/catalog-search.d.ts.map +1 -0
  264. package/dist/skills/catalog/catalog-search.js +111 -0
  265. package/dist/skills/catalog/catalog-types.d.ts +81 -0
  266. package/dist/skills/catalog/catalog-types.d.ts.map +1 -0
  267. package/dist/skills/catalog/catalog-types.js +66 -0
  268. package/dist/skills/catalog/index.d.ts +9 -0
  269. package/dist/skills/catalog/index.d.ts.map +1 -0
  270. package/dist/skills/catalog/index.js +7 -0
  271. package/dist/skills/catalog/vercel-catalog.d.ts +42 -0
  272. package/dist/skills/catalog/vercel-catalog.d.ts.map +1 -0
  273. package/dist/skills/catalog/vercel-catalog.js +189 -0
  274. package/dist/skills/compat/index.d.ts +9 -0
  275. package/dist/skills/compat/index.d.ts.map +1 -0
  276. package/dist/skills/compat/index.js +8 -0
  277. package/dist/skills/compat/skill-enhancer.d.ts +37 -0
  278. package/dist/skills/compat/skill-enhancer.d.ts.map +1 -0
  279. package/dist/skills/compat/skill-enhancer.js +76 -0
  280. package/dist/skills/compat/tool-mapper.d.ts +61 -0
  281. package/dist/skills/compat/tool-mapper.d.ts.map +1 -0
  282. package/dist/skills/compat/tool-mapper.js +168 -0
  283. package/dist/skills/compat/vercel-compat.d.ts +33 -0
  284. package/dist/skills/compat/vercel-compat.d.ts.map +1 -0
  285. package/dist/skills/compat/vercel-compat.js +132 -0
  286. package/dist/skills/index.d.ts +40 -0
  287. package/dist/skills/index.d.ts.map +1 -0
  288. package/dist/skills/index.js +47 -0
  289. package/dist/skills/integration/agent-skill-provider.d.ts +94 -0
  290. package/dist/skills/integration/agent-skill-provider.d.ts.map +1 -0
  291. package/dist/skills/integration/agent-skill-provider.js +161 -0
  292. package/dist/skills/integration/index.d.ts +7 -0
  293. package/dist/skills/integration/index.d.ts.map +1 -0
  294. package/dist/skills/integration/index.js +6 -0
  295. package/dist/skills/loader/github-loader.d.ts +61 -0
  296. package/dist/skills/loader/github-loader.d.ts.map +1 -0
  297. package/dist/skills/loader/github-loader.js +176 -0
  298. package/dist/skills/loader/index.d.ts +10 -0
  299. package/dist/skills/loader/index.d.ts.map +1 -0
  300. package/dist/skills/loader/index.js +9 -0
  301. package/dist/skills/loader/local-loader.d.ts +56 -0
  302. package/dist/skills/loader/local-loader.d.ts.map +1 -0
  303. package/dist/skills/loader/local-loader.js +186 -0
  304. package/dist/skills/loader/vercel-loader.d.ts +64 -0
  305. package/dist/skills/loader/vercel-loader.d.ts.map +1 -0
  306. package/dist/skills/loader/vercel-loader.js +313 -0
  307. package/dist/skills/loader/vercel-types.d.ts +64 -0
  308. package/dist/skills/loader/vercel-types.d.ts.map +1 -0
  309. package/dist/skills/loader/vercel-types.js +55 -0
  310. package/dist/skills/parser/index.d.ts +7 -0
  311. package/dist/skills/parser/index.d.ts.map +1 -0
  312. package/dist/skills/parser/index.js +6 -0
  313. package/dist/skills/parser/skill-md-parser.d.ts +64 -0
  314. package/dist/skills/parser/skill-md-parser.d.ts.map +1 -0
  315. package/dist/skills/parser/skill-md-parser.js +242 -0
  316. package/dist/skills/registry/index.d.ts +7 -0
  317. package/dist/skills/registry/index.d.ts.map +1 -0
  318. package/dist/skills/registry/index.js +6 -0
  319. package/dist/skills/registry/skill-registry.d.ts +133 -0
  320. package/dist/skills/registry/skill-registry.d.ts.map +1 -0
  321. package/dist/skills/registry/skill-registry.js +373 -0
  322. package/dist/skills/types.d.ts +216 -0
  323. package/dist/skills/types.d.ts.map +1 -0
  324. package/dist/skills/types.js +176 -0
  325. package/dist/templates/agent-spec.d.ts +138 -0
  326. package/dist/templates/agent-spec.d.ts.map +1 -0
  327. package/dist/templates/agent-spec.js +138 -0
  328. package/dist/templates/index.d.ts +56 -0
  329. package/dist/templates/index.d.ts.map +1 -0
  330. package/dist/templates/index.js +58 -0
  331. package/dist/templates/prompt-spec.d.ts +140 -0
  332. package/dist/templates/prompt-spec.d.ts.map +1 -0
  333. package/dist/templates/prompt-spec.js +210 -0
  334. package/dist/templates/skill-spec.d.ts +106 -0
  335. package/dist/templates/skill-spec.d.ts.map +1 -0
  336. package/dist/templates/skill-spec.js +119 -0
  337. package/dist/tools/base.d.ts +74 -0
  338. package/dist/tools/base.d.ts.map +1 -0
  339. package/dist/tools/base.js +6 -0
  340. package/dist/tools/cms/collection-tools.d.ts +36 -0
  341. package/dist/tools/cms/collection-tools.d.ts.map +1 -0
  342. package/dist/tools/cms/collection-tools.js +178 -0
  343. package/dist/tools/cms/factory.d.ts +89 -0
  344. package/dist/tools/cms/factory.d.ts.map +1 -0
  345. package/dist/tools/cms/factory.js +462 -0
  346. package/dist/tools/cms/global-tools.d.ts +21 -0
  347. package/dist/tools/cms/global-tools.d.ts.map +1 -0
  348. package/dist/tools/cms/global-tools.js +92 -0
  349. package/dist/tools/cms/index.d.ts +11 -0
  350. package/dist/tools/cms/index.d.ts.map +1 -0
  351. package/dist/tools/cms/index.js +11 -0
  352. package/dist/tools/cms/media-tools.d.ts +31 -0
  353. package/dist/tools/cms/media-tools.d.ts.map +1 -0
  354. package/dist/tools/cms/media-tools.js +140 -0
  355. package/dist/tools/cms/user-tools.d.ts +31 -0
  356. package/dist/tools/cms/user-tools.d.ts.map +1 -0
  357. package/dist/tools/cms/user-tools.js +135 -0
  358. package/dist/tools/deduplicator.d.ts +19 -0
  359. package/dist/tools/deduplicator.d.ts.map +1 -0
  360. package/dist/tools/deduplicator.js +53 -0
  361. package/dist/tools/document-summarizer.d.ts +11 -0
  362. package/dist/tools/document-summarizer.d.ts.map +1 -0
  363. package/dist/tools/document-summarizer.js +82 -0
  364. package/dist/tools/mcp-adapter.d.ts +66 -0
  365. package/dist/tools/mcp-adapter.d.ts.map +1 -0
  366. package/dist/tools/mcp-adapter.js +152 -0
  367. package/dist/tools/memory/index.d.ts +3 -0
  368. package/dist/tools/memory/index.d.ts.map +1 -0
  369. package/dist/tools/memory/index.js +1 -0
  370. package/dist/tools/memory/store-memory.d.ts +39 -0
  371. package/dist/tools/memory/store-memory.d.ts.map +1 -0
  372. package/dist/tools/memory/store-memory.js +94 -0
  373. package/dist/tools/registry.d.ts +14 -0
  374. package/dist/tools/registry.d.ts.map +1 -0
  375. package/dist/tools/registry.js +48 -0
  376. package/dist/tools/ticket-tools.d.ts +31 -0
  377. package/dist/tools/ticket-tools.d.ts.map +1 -0
  378. package/dist/tools/ticket-tools.js +74 -0
  379. package/dist/tools/web/duck-duck-go.d.ts +52 -0
  380. package/dist/tools/web/duck-duck-go.d.ts.map +1 -0
  381. package/dist/tools/web/duck-duck-go.js +202 -0
  382. package/dist/tools/web/exa.d.ts +34 -0
  383. package/dist/tools/web/exa.d.ts.map +1 -0
  384. package/dist/tools/web/exa.js +80 -0
  385. package/dist/tools/web/index.d.ts +6 -0
  386. package/dist/tools/web/index.d.ts.map +1 -0
  387. package/dist/tools/web/index.js +4 -0
  388. package/dist/tools/web/scraper.d.ts +9 -0
  389. package/dist/tools/web/scraper.d.ts.map +1 -0
  390. package/dist/tools/web/scraper.js +118 -0
  391. package/dist/tools/web/tavily.d.ts +32 -0
  392. package/dist/tools/web/tavily.d.ts.map +1 -0
  393. package/dist/tools/web/tavily.js +73 -0
  394. package/dist/tools/web/types.d.ts +31 -0
  395. package/dist/tools/web/types.d.ts.map +1 -0
  396. package/dist/tools/web/types.js +9 -0
  397. package/package.json +143 -0
@@ -0,0 +1,497 @@
1
+ /**
2
+ * Unified LLM Client
3
+ *
4
+ * Single interface for all LLM providers with fallback and rate limiting
5
+ */
6
+ // =============================================================================
7
+ // Log redaction
8
+ // =============================================================================
9
+ const SENSITIVE_KEYS = new Set([
10
+ 'apiKey',
11
+ 'api_key',
12
+ 'authorization',
13
+ 'Authorization',
14
+ 'x-ai-api-key',
15
+ 'X-AI-Api-Key',
16
+ 'token',
17
+ 'secret',
18
+ 'password',
19
+ 'encryptedKey',
20
+ 'encrypted_key',
21
+ ]);
22
+ /**
23
+ * Redact sensitive fields before passing an object to a logger.
24
+ * Replaces API keys, tokens, and authorization headers with `[REDACTED]`.
25
+ * Recurses into nested plain objects; leaves arrays and primitives as-is.
26
+ */
27
+ export function redactSensitiveFields(obj) {
28
+ const result = {};
29
+ for (const [key, value] of Object.entries(obj)) {
30
+ if (SENSITIVE_KEYS.has(key)) {
31
+ result[key] = '[REDACTED]';
32
+ }
33
+ else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
34
+ result[key] = redactSensitiveFields(value);
35
+ }
36
+ else {
37
+ result[key] = value;
38
+ }
39
+ }
40
+ return result;
41
+ }
42
+ import { decryptApiKey } from '@revealui/db/crypto';
43
+ import { tenantProviderConfigs, userApiKeys } from '@revealui/db/schema';
44
+ import { and, eq } from 'drizzle-orm';
45
+ import { AnthropicProvider } from './providers/anthropic.js';
46
+ import { GroqProvider } from './providers/groq.js';
47
+ import { OllamaProvider } from './providers/ollama.js';
48
+ import { OpenAIProvider } from './providers/openai.js';
49
+ import { VultrProvider } from './providers/vultr.js';
50
+ import { ResponseCache } from './response-cache.js';
51
+ import { SemanticCache, } from './semantic-cache.js';
52
+ import { estimateRequest as _estimateRequestTokens } from './token-counter.js';
53
+ export class LLMClient {
54
+ provider;
55
+ fallbackProvider;
56
+ config;
57
+ rateLimitState;
58
+ responseCache;
59
+ semanticCache;
60
+ healthMonitor;
61
+ /** Tracks the last resolved API key so we only recreate the provider when it changes */
62
+ currentApiKey;
63
+ constructor(config) {
64
+ this.config = config;
65
+ this.currentApiKey = config.apiKey;
66
+ this.rateLimitState = {
67
+ requests: [],
68
+ dailyRequests: 0,
69
+ lastReset: Date.now(),
70
+ };
71
+ // Initialize response cache if enabled
72
+ if (config.enableResponseCache) {
73
+ this.responseCache = new ResponseCache(config.responseCacheOptions);
74
+ }
75
+ // Initialize semantic cache if enabled
76
+ if (config.enableSemanticCache) {
77
+ this.semanticCache = new SemanticCache(config.semanticCacheOptions);
78
+ }
79
+ // Wire health monitor if provided
80
+ this.healthMonitor = config.healthMonitor;
81
+ // Create primary provider
82
+ this.provider = this.createProvider(config.provider, {
83
+ apiKey: config.apiKey,
84
+ baseURL: config.baseURL,
85
+ model: config.model,
86
+ temperature: config.temperature,
87
+ maxTokens: config.maxTokens,
88
+ });
89
+ // Create fallback provider if specified
90
+ if (config.fallbackProvider) {
91
+ this.fallbackProvider = this.createProvider(config.fallbackProvider, {
92
+ apiKey: config.apiKey, // Note: In practice, you'd want separate API keys
93
+ baseURL: config.baseURL,
94
+ model: config.model,
95
+ temperature: config.temperature,
96
+ maxTokens: config.maxTokens,
97
+ });
98
+ }
99
+ }
100
+ createProvider(type, config) {
101
+ switch (type) {
102
+ case 'openai':
103
+ return new OpenAIProvider(config);
104
+ case 'anthropic':
105
+ return new AnthropicProvider({
106
+ ...config,
107
+ enableCacheByDefault: this.config.enableCacheByDefault,
108
+ });
109
+ case 'vultr':
110
+ return new VultrProvider(config);
111
+ case 'groq':
112
+ return new GroqProvider(config);
113
+ case 'ollama':
114
+ return new OllamaProvider(config);
115
+ default:
116
+ throw new Error(`Unknown provider type: ${String(type)}`);
117
+ }
118
+ }
119
+ /**
120
+ * Re-resolve the API key via apiKeyFn (if configured) and recreate the provider
121
+ * when the key has changed. No-op if apiKeyFn is not set.
122
+ */
123
+ async refreshProviderIfNeeded() {
124
+ if (!this.config.apiKeyFn)
125
+ return;
126
+ const newKey = await this.config.apiKeyFn();
127
+ if (newKey === this.currentApiKey)
128
+ return;
129
+ this.currentApiKey = newKey;
130
+ const providerConfig = {
131
+ apiKey: newKey,
132
+ baseURL: this.config.baseURL,
133
+ model: this.config.model,
134
+ temperature: this.config.temperature,
135
+ maxTokens: this.config.maxTokens,
136
+ };
137
+ this.provider = this.createProvider(this.config.provider, providerConfig);
138
+ if (this.config.fallbackProvider) {
139
+ this.fallbackProvider = this.createProvider(this.config.fallbackProvider, providerConfig);
140
+ }
141
+ }
142
+ checkRateLimit() {
143
+ const now = Date.now();
144
+ const { rateLimit } = this.config;
145
+ if (!rateLimit) {
146
+ return true;
147
+ }
148
+ // Reset daily counter if needed
149
+ if (now - this.rateLimitState.lastReset > 24 * 60 * 60 * 1000) {
150
+ this.rateLimitState.dailyRequests = 0;
151
+ this.rateLimitState.lastReset = now;
152
+ }
153
+ // Check per-minute limit
154
+ if (rateLimit.requestsPerMinute) {
155
+ const oneMinuteAgo = now - 60 * 1000;
156
+ this.rateLimitState.requests = this.rateLimitState.requests.filter((time) => time > oneMinuteAgo);
157
+ if (this.rateLimitState.requests.length >= rateLimit.requestsPerMinute) {
158
+ return false;
159
+ }
160
+ }
161
+ // Check daily limit
162
+ if (rateLimit.requestsPerDay) {
163
+ if (this.rateLimitState.dailyRequests >= rateLimit.requestsPerDay) {
164
+ return false;
165
+ }
166
+ }
167
+ return true;
168
+ }
169
+ recordRequest() {
170
+ const now = Date.now();
171
+ this.rateLimitState.requests.push(now);
172
+ this.rateLimitState.dailyRequests++;
173
+ }
174
+ async chat(messages, options) {
175
+ await this.refreshProviderIfNeeded();
176
+ // Check semantic cache first (if enabled)
177
+ // Semantic cache is more powerful - matches similar queries, not just exact matches
178
+ if (this.semanticCache) {
179
+ const query = this.semanticCache.extractQuery(messages);
180
+ const cached = await this.semanticCache.get(query);
181
+ if (cached) {
182
+ // Semantic cache hit - return immediately without API call
183
+ return {
184
+ content: cached.response,
185
+ role: 'assistant',
186
+ finishReason: 'stop',
187
+ usage: cached.usage
188
+ ? {
189
+ ...cached.usage,
190
+ // Mark as cached for monitoring
191
+ cacheReadTokens: cached.usage.totalTokens,
192
+ }
193
+ : undefined,
194
+ };
195
+ }
196
+ }
197
+ // Check response cache (if enabled and semantic cache didn't hit)
198
+ if (this.responseCache) {
199
+ const cacheKey = this.responseCache.getCacheKey(messages, {
200
+ temperature: options?.temperature,
201
+ maxTokens: options?.maxTokens,
202
+ tools: options?.tools,
203
+ model: this.config.model,
204
+ });
205
+ const cached = this.responseCache.get(cacheKey);
206
+ if (cached) {
207
+ // Cache hit - return immediately without API call
208
+ return {
209
+ ...cached,
210
+ usage: cached.usage
211
+ ? {
212
+ ...cached.usage,
213
+ // Mark as cached for monitoring
214
+ cacheReadTokens: cached.usage.totalTokens,
215
+ }
216
+ : undefined,
217
+ };
218
+ }
219
+ // Cache miss - proceed with API call
220
+ }
221
+ if (!this.checkRateLimit()) {
222
+ throw new Error('Rate limit exceeded');
223
+ }
224
+ const callStart = Date.now();
225
+ try {
226
+ this.recordRequest();
227
+ const response = await this.provider.chat(messages, options);
228
+ this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart);
229
+ // Store in semantic cache (if enabled)
230
+ if (this.semanticCache) {
231
+ const query = this.semanticCache.extractQuery(messages);
232
+ await this.semanticCache.set(query, response.content, response.usage);
233
+ }
234
+ // Store in response cache (if enabled)
235
+ if (this.responseCache) {
236
+ const cacheKey = this.responseCache.getCacheKey(messages, {
237
+ temperature: options?.temperature,
238
+ maxTokens: options?.maxTokens,
239
+ tools: options?.tools,
240
+ model: this.config.model,
241
+ });
242
+ this.responseCache.set(cacheKey, {
243
+ content: response.content,
244
+ role: response.role,
245
+ finishReason: response.finishReason,
246
+ toolCalls: response.toolCalls,
247
+ timestamp: Date.now(),
248
+ usage: response.usage,
249
+ });
250
+ }
251
+ return response;
252
+ }
253
+ catch (error) {
254
+ this.healthMonitor?.recordCall(this.config.provider, Date.now() - callStart, error instanceof Error ? error : new Error(String(error)));
255
+ // Try fallback if available
256
+ if (this.fallbackProvider && this.config.fallbackProvider) {
257
+ const fallbackStart = Date.now();
258
+ try {
259
+ const fallbackResponse = await this.fallbackProvider.chat(messages, options);
260
+ this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart);
261
+ return fallbackResponse;
262
+ }
263
+ catch (fallbackError) {
264
+ this.healthMonitor?.recordCall(this.config.fallbackProvider, Date.now() - fallbackStart, fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
265
+ throw new Error(`Both primary and fallback providers failed: ${error instanceof Error ? error.message : String(error)}`);
266
+ }
267
+ }
268
+ throw error;
269
+ }
270
+ }
271
+ async embed(text, options) {
272
+ await this.refreshProviderIfNeeded();
273
+ if (!this.checkRateLimit()) {
274
+ throw new Error('Rate limit exceeded');
275
+ }
276
+ try {
277
+ this.recordRequest();
278
+ return await this.provider.embed(text, options);
279
+ }
280
+ catch (error) {
281
+ // Try fallback if available
282
+ if (this.fallbackProvider) {
283
+ try {
284
+ return await this.fallbackProvider.embed(text, options);
285
+ }
286
+ catch {
287
+ throw new Error(`Both primary and fallback providers failed: ${error instanceof Error ? error.message : String(error)}`);
288
+ }
289
+ }
290
+ throw error;
291
+ }
292
+ }
293
+ async *stream(messages, options) {
294
+ await this.refreshProviderIfNeeded();
295
+ // Note: Streaming is not cached (can't cache partial responses)
296
+ if (!this.checkRateLimit()) {
297
+ throw new Error('Rate limit exceeded');
298
+ }
299
+ try {
300
+ this.recordRequest();
301
+ yield* this.provider.stream(messages, options);
302
+ }
303
+ catch (error) {
304
+ // Try fallback if available
305
+ if (this.fallbackProvider) {
306
+ try {
307
+ yield* this.fallbackProvider.stream(messages, options);
308
+ }
309
+ catch {
310
+ throw new Error(`Both primary and fallback providers failed: ${error instanceof Error ? error.message : String(error)}`);
311
+ }
312
+ }
313
+ else {
314
+ throw error;
315
+ }
316
+ }
317
+ }
318
+ /**
319
+ * Estimate token count and cost for a set of messages using the configured model.
320
+ * Uses a heuristic (~4 chars/token). Useful for pre-flight cost checks.
321
+ */
322
+ estimateRequest(messages) {
323
+ return _estimateRequestTokens(messages, this.config.model ?? '');
324
+ }
325
+ /**
326
+ * Get the provider health monitor if one was configured.
327
+ */
328
+ getHealthMonitor() {
329
+ return this.healthMonitor;
330
+ }
331
+ /**
332
+ * Get response cache statistics
333
+ *
334
+ * @returns Cache stats or undefined if caching is disabled
335
+ */
336
+ getResponseCacheStats() {
337
+ return this.responseCache?.getStats();
338
+ }
339
+ /**
340
+ * Clear response cache
341
+ */
342
+ clearResponseCache() {
343
+ this.responseCache?.clear();
344
+ }
345
+ /**
346
+ * Get semantic cache statistics
347
+ *
348
+ * @returns Semantic cache stats or undefined if caching is disabled
349
+ */
350
+ getSemanticCacheStats() {
351
+ return this.semanticCache?.getStats();
352
+ }
353
+ /**
354
+ * Clear semantic cache
355
+ */
356
+ clearSemanticCache() {
357
+ this.semanticCache?.resetStats();
358
+ }
359
+ }
360
+ /**
361
+ * Create an LLM client from environment variables.
362
+ *
363
+ * When LLM_PROVIDER is not set, auto-detects the provider by checking env vars
364
+ * in priority order: GROQ_API_KEY → OLLAMA_BASE_URL → ANTHROPIC_API_KEY.
365
+ *
366
+ * GROQ and Ollama are preferred — they are free-tier and BYOK-friendly.
367
+ * OpenAI is not in the auto-detection chain (no revenue yet — see LLM provider policy).
368
+ * To use OpenAI, set LLM_PROVIDER=openai explicitly.
369
+ *
370
+ * Provider defaults:
371
+ * groq → llama-3.3-70b-versatile
372
+ * ollama → llama3.2:3b
373
+ */
374
+ export function createLLMClientFromEnv() {
375
+ // Auto-detect provider when LLM_PROVIDER is not explicitly set
376
+ let provider;
377
+ if (process.env.LLM_PROVIDER) {
378
+ provider = process.env.LLM_PROVIDER;
379
+ }
380
+ else if (process.env.GROQ_API_KEY) {
381
+ provider = 'groq';
382
+ }
383
+ else if (process.env.OLLAMA_BASE_URL) {
384
+ provider = 'ollama';
385
+ }
386
+ else if (process.env.ANTHROPIC_API_KEY) {
387
+ provider = 'anthropic';
388
+ }
389
+ else {
390
+ // No provider configured — throw a clear error. OpenAI is intentionally excluded from
391
+ // auto-detection (no revenue yet). Set LLM_PROVIDER=openai explicitly if needed.
392
+ throw new Error('No LLM provider configured. Set one of: GROQ_API_KEY (recommended), ' +
393
+ 'OLLAMA_BASE_URL (local), or ANTHROPIC_API_KEY. ' +
394
+ 'Alternatively, set LLM_PROVIDER explicitly.');
395
+ }
396
+ let apiKey;
397
+ let baseURL;
398
+ let defaultModel;
399
+ if (provider === 'openai') {
400
+ apiKey = process.env.OPENAI_API_KEY;
401
+ baseURL = process.env.OPENAI_BASE_URL;
402
+ }
403
+ else if (provider === 'anthropic') {
404
+ apiKey = process.env.ANTHROPIC_API_KEY;
405
+ baseURL = process.env.ANTHROPIC_BASE_URL;
406
+ }
407
+ else if (provider === 'vultr') {
408
+ apiKey = process.env.VULTR_API_KEY;
409
+ baseURL = process.env.VULTR_BASE_URL;
410
+ }
411
+ else if (provider === 'huggingface') {
412
+ apiKey = process.env.HF_TOKEN;
413
+ baseURL = process.env.HF_MODEL_URL;
414
+ }
415
+ else if (provider === 'groq') {
416
+ apiKey = process.env.GROQ_API_KEY;
417
+ baseURL = process.env.GROQ_BASE_URL;
418
+ defaultModel = 'llama-3.3-70b-versatile';
419
+ }
420
+ else if (provider === 'ollama') {
421
+ apiKey = 'ollama'; // Ollama ignores the API key
422
+ baseURL = process.env.OLLAMA_BASE_URL;
423
+ defaultModel = 'llama3.2:3b';
424
+ }
425
+ if (!apiKey) {
426
+ throw new Error(`API key not found for provider "${provider}". Set the corresponding env var ` +
427
+ `(GROQ_API_KEY, OLLAMA_BASE_URL, ANTHROPIC_API_KEY, or OPENAI_API_KEY).`);
428
+ }
429
+ return new LLMClient({
430
+ provider,
431
+ apiKey,
432
+ baseURL,
433
+ model: process.env.LLM_MODEL ?? defaultModel,
434
+ temperature: process.env.LLM_TEMPERATURE ? parseFloat(process.env.LLM_TEMPERATURE) : undefined,
435
+ maxTokens: process.env.LLM_MAX_TOKENS ? parseInt(process.env.LLM_MAX_TOKENS, 10) : undefined,
436
+ enableCacheByDefault: process.env.LLM_ENABLE_CACHE === 'true' || process.env.ANTHROPIC_ENABLE_CACHE === 'true',
437
+ enableResponseCache: process.env.LLM_ENABLE_RESPONSE_CACHE === 'true' ||
438
+ process.env.RESPONSE_CACHE_ENABLED === 'true',
439
+ enableSemanticCache: process.env.LLM_ENABLE_SEMANTIC_CACHE === 'true' ||
440
+ process.env.SEMANTIC_CACHE_ENABLED === 'true',
441
+ });
442
+ }
443
+ /**
444
+ * Create an LLM client using a user's stored BYOK API key.
445
+ *
446
+ * Looks up the user's preferred provider from `tenant_provider_configs`
447
+ * (falling back to the first key in `user_api_keys`), decrypts the key
448
+ * with AES-256-GCM, and returns a configured LLMClient.
449
+ *
450
+ * Returns `null` if the user has no stored keys (callers should fall back
451
+ * to `createLLMClientFromEnv()` or return a 402/feature-unavailable error).
452
+ *
453
+ * @param userId - The user's ID from the `users` table
454
+ * @param db - A Drizzle NeonDB client instance
455
+ */
456
+ export async function createLLMClientForUser(userId, db, auditStore) {
457
+ // Find the user's preferred provider config
458
+ const [preferredConfig] = await db
459
+ .select()
460
+ .from(tenantProviderConfigs)
461
+ .where(and(eq(tenantProviderConfigs.userId, userId), eq(tenantProviderConfigs.isDefault, true)))
462
+ .limit(1);
463
+ // Find the matching API key (preferred provider, or any available key)
464
+ const keyQuery = db
465
+ .select()
466
+ .from(userApiKeys)
467
+ .where(preferredConfig
468
+ ? and(eq(userApiKeys.userId, userId), eq(userApiKeys.provider, preferredConfig.provider))
469
+ : eq(userApiKeys.userId, userId))
470
+ .limit(1);
471
+ const [keyRow] = await keyQuery;
472
+ if (!keyRow)
473
+ return null;
474
+ const plaintext = decryptApiKey(keyRow.encryptedKey);
475
+ const provider = keyRow.provider;
476
+ const model = preferredConfig?.model ?? undefined;
477
+ // Fire-and-forget: record when this key was last used (best-effort, never blocks)
478
+ db.update(userApiKeys)
479
+ .set({ lastUsedAt: new Date() })
480
+ .where(eq(userApiKeys.id, keyRow.id))
481
+ .catch(() => undefined);
482
+ // Fire-and-forget: emit BYOK audit event if an audit store is wired up
483
+ if (auditStore) {
484
+ auditStore
485
+ .append({
486
+ id: crypto.randomUUID(),
487
+ timestamp: new Date(),
488
+ eventType: 'byok:key:accessed',
489
+ severity: 'info',
490
+ agentId: 'system',
491
+ payload: { userId, provider, keyId: keyRow.id },
492
+ policyViolations: [],
493
+ })
494
+ .catch(() => undefined);
495
+ }
496
+ return new LLMClient({ provider, apiKey: plaintext, model });
497
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * BYOK Provider Key Validator
3
+ *
4
+ * Validates API keys against their provider before storage.
5
+ * Uses the cheapest available endpoint for each provider — typically a
6
+ * models list (read-only, no token cost). Falls back gracefully when the
7
+ * provider is unreachable so that network failures don't block key storage.
8
+ */
9
+ export type ProviderValidationResult = {
10
+ valid: true;
11
+ } | {
12
+ valid: false;
13
+ error: string;
14
+ };
15
+ /**
16
+ * Validate an API key against the given provider.
17
+ *
18
+ * Unreachable providers return `{ valid: true }` with a warning comment so
19
+ * that network outages (especially for Ollama) never block key storage.
20
+ *
21
+ * @param provider - One of the allowed BYOK provider identifiers
22
+ * @param apiKey - The plaintext key to probe
23
+ */
24
+ export declare function validateProviderKey(provider: string, apiKey: string): Promise<ProviderValidationResult>;
25
+ //# sourceMappingURL=key-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-validator.d.ts","sourceRoot":"","sources":["../../src/llm/key-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,wBAAwB,GAAG;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAexF;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,wBAAwB,CAAC,CAuEnC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * BYOK Provider Key Validator
3
+ *
4
+ * Validates API keys against their provider before storage.
5
+ * Uses the cheapest available endpoint for each provider — typically a
6
+ * models list (read-only, no token cost). Falls back gracefully when the
7
+ * provider is unreachable so that network failures don't block key storage.
8
+ */
9
+ /** Timeout for validation probes (ms). Kept short to avoid blocking the request. */
10
+ const VALIDATION_TIMEOUT_MS = 4_000;
11
+ async function probeFetch(url, init) {
12
+ const controller = new AbortController();
13
+ const timeoutId = setTimeout(() => controller.abort(), VALIDATION_TIMEOUT_MS);
14
+ try {
15
+ return await fetch(url, { ...init, signal: controller.signal });
16
+ }
17
+ finally {
18
+ clearTimeout(timeoutId);
19
+ }
20
+ }
21
+ /**
22
+ * Validate an API key against the given provider.
23
+ *
24
+ * Unreachable providers return `{ valid: true }` with a warning comment so
25
+ * that network outages (especially for Ollama) never block key storage.
26
+ *
27
+ * @param provider - One of the allowed BYOK provider identifiers
28
+ * @param apiKey - The plaintext key to probe
29
+ */
30
+ export async function validateProviderKey(provider, apiKey) {
31
+ try {
32
+ switch (provider) {
33
+ case 'groq': {
34
+ const res = await probeFetch('https://api.groq.com/openai/v1/models', {
35
+ headers: { Authorization: `Bearer ${apiKey}` },
36
+ });
37
+ if (res.ok)
38
+ return { valid: true };
39
+ if (res.status === 401)
40
+ return { valid: false, error: 'Invalid Groq API key' };
41
+ // Any other non-OK status (429, 500 etc.) — treat as reachable but unknown
42
+ return { valid: false, error: `Groq validation failed: HTTP ${res.status}` };
43
+ }
44
+ case 'anthropic': {
45
+ // Anthropic has no free read endpoint. Validate by key format only.
46
+ // Valid Anthropic keys start with "sk-ant-api".
47
+ if (!apiKey.startsWith('sk-ant-api')) {
48
+ return { valid: false, error: 'Anthropic API key must start with "sk-ant-api"' };
49
+ }
50
+ return { valid: true };
51
+ }
52
+ case 'openai': {
53
+ // Validate by format — keys start with "sk-"
54
+ // (Per LLM policy, OpenAI API calls are blocked until we have revenue.)
55
+ if (!apiKey.startsWith('sk-')) {
56
+ return { valid: false, error: 'OpenAI API key must start with "sk-"' };
57
+ }
58
+ return { valid: true };
59
+ }
60
+ case 'huggingface': {
61
+ const res = await probeFetch('https://huggingface.co/api/whoami-v2', {
62
+ headers: { Authorization: `Bearer ${apiKey}` },
63
+ });
64
+ if (res.ok)
65
+ return { valid: true };
66
+ if (res.status === 401 || res.status === 403) {
67
+ return { valid: false, error: 'Invalid HuggingFace token' };
68
+ }
69
+ return { valid: false, error: `HuggingFace validation failed: HTTP ${res.status}` };
70
+ }
71
+ case 'vultr': {
72
+ // Vultr Serverless Inference API (OpenAI-compatible)
73
+ const res = await probeFetch('https://api.vultrinference.com/v1/models', {
74
+ headers: { Authorization: `Bearer ${apiKey}` },
75
+ });
76
+ if (res.ok)
77
+ return { valid: true };
78
+ if (res.status === 401)
79
+ return { valid: false, error: 'Invalid Vultr API key' };
80
+ return { valid: false, error: `Vultr validation failed: HTTP ${res.status}` };
81
+ }
82
+ case 'ollama': {
83
+ // Ollama is local — we cannot reliably probe it from the server.
84
+ // Accept the key as-is (Ollama doesn't use API keys anyway).
85
+ return { valid: true };
86
+ }
87
+ default:
88
+ // Unknown provider — skip validation
89
+ return { valid: true };
90
+ }
91
+ }
92
+ catch (err) {
93
+ // Network error (AbortError, DNS failure, etc.) — don't block storage
94
+ if (err instanceof Error && err.name === 'AbortError') {
95
+ // Timeout — provider unreachable, proceed with storage
96
+ return { valid: true };
97
+ }
98
+ // Other network errors (ECONNREFUSED, etc.) — proceed with storage
99
+ return { valid: true };
100
+ }
101
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Provider Health Monitor
3
+ *
4
+ * Sliding window of last 100 calls per provider.
5
+ * Tracks latency (p50) and error rate to rank fallback candidates.
6
+ *
7
+ * AnythingLLM lesson: no health checks → silent failures cascade.
8
+ */
9
+ import type { LLMProviderType } from './client.js';
10
+ export interface ProviderHealth {
11
+ provider: LLMProviderType;
12
+ /** healthy | degraded | unhealthy */
13
+ status: 'healthy' | 'degraded' | 'unhealthy';
14
+ latencyP50Ms: number;
15
+ errorRate: number;
16
+ sampleCount: number;
17
+ }
18
+ export declare class ProviderHealthMonitor {
19
+ private windows;
20
+ private getWindow;
21
+ /**
22
+ * Record a completed LLM API call.
23
+ */
24
+ recordCall(provider: LLMProviderType, latencyMs: number, error?: Error | null): void;
25
+ /**
26
+ * Get health metrics for a provider.
27
+ * Returns 'healthy' with 0 latency if no calls recorded yet.
28
+ */
29
+ getHealth(provider: LLMProviderType): ProviderHealth;
30
+ /**
31
+ * Pick the best provider from a set of candidates.
32
+ * Prefers healthy > degraded > unhealthy, then by p50 latency.
33
+ */
34
+ getBestProvider(candidates: LLMProviderType[]): LLMProviderType;
35
+ /**
36
+ * Reset health data for a provider (useful for testing).
37
+ */
38
+ reset(provider: LLMProviderType): void;
39
+ }
40
+ //# sourceMappingURL=provider-health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-health.d.ts","sourceRoot":"","sources":["../../src/llm/provider-health.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,CAAA;IACzB,qCAAqC;IACrC,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAA;IAC5C,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAqBD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,OAAO,CAAgD;IAE/D,OAAO,CAAC,SAAS;IAQjB;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI;IAUpF;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc;IA4BpD;;;OAGG;IACH,eAAe,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,eAAe;IAkB/D;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;CAGvC"}