@things-factory/board-ai 10.0.0-beta.108

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 (329) hide show
  1. package/PLAN-styling-tools.md +1253 -0
  2. package/client/components/board-ai-chat.test.ts +149 -0
  3. package/client/components/board-ai-chat.ts +2574 -0
  4. package/client/components/chat-echo-dedup.test.ts +116 -0
  5. package/client/components/chat-echo-dedup.ts +89 -0
  6. package/client/components/chat-input-builder.ts +47 -0
  7. package/client/components/default-catalog-entries.test.ts +59 -0
  8. package/client/components/default-catalog-entries.ts +100 -0
  9. package/client/components/default-slash-templates.test.ts +55 -0
  10. package/client/components/default-slash-templates.ts +129 -0
  11. package/client/components/markdown.test.ts +518 -0
  12. package/client/components/markdown.ts +508 -0
  13. package/client/components/mention-popup-helpers.ts +433 -0
  14. package/client/components/mention-popup.test.ts +588 -0
  15. package/client/components/mention-popup.ts +238 -0
  16. package/client/index.ts +11 -0
  17. package/client/tsconfig.json +13 -0
  18. package/client/utils/board-edit-patch.ts +374 -0
  19. package/config/config.development.js +43 -0
  20. package/config/config.production.js +15 -0
  21. package/dist-client/client/components/board-ai-chat.d.ts +277 -0
  22. package/dist-client/client/components/board-ai-chat.js +2444 -0
  23. package/dist-client/client/components/board-ai-chat.js.map +1 -0
  24. package/dist-client/client/components/board-ai-chat.test.d.ts +1 -0
  25. package/dist-client/client/components/board-ai-chat.test.js +138 -0
  26. package/dist-client/client/components/board-ai-chat.test.js.map +1 -0
  27. package/dist-client/client/components/chat-echo-dedup.d.ts +45 -0
  28. package/dist-client/client/components/chat-echo-dedup.js +58 -0
  29. package/dist-client/client/components/chat-echo-dedup.js.map +1 -0
  30. package/dist-client/client/components/chat-echo-dedup.test.d.ts +1 -0
  31. package/dist-client/client/components/chat-echo-dedup.test.js +101 -0
  32. package/dist-client/client/components/chat-echo-dedup.test.js.map +1 -0
  33. package/dist-client/client/components/chat-input-builder.d.ts +39 -0
  34. package/dist-client/client/components/chat-input-builder.js +26 -0
  35. package/dist-client/client/components/chat-input-builder.js.map +1 -0
  36. package/dist-client/client/components/default-catalog-entries.d.ts +15 -0
  37. package/dist-client/client/components/default-catalog-entries.js +86 -0
  38. package/dist-client/client/components/default-catalog-entries.js.map +1 -0
  39. package/dist-client/client/components/default-catalog-entries.test.d.ts +1 -0
  40. package/dist-client/client/components/default-catalog-entries.test.js +52 -0
  41. package/dist-client/client/components/default-catalog-entries.test.js.map +1 -0
  42. package/dist-client/client/components/default-slash-templates.d.ts +17 -0
  43. package/dist-client/client/components/default-slash-templates.js +113 -0
  44. package/dist-client/client/components/default-slash-templates.js.map +1 -0
  45. package/dist-client/client/components/default-slash-templates.test.d.ts +1 -0
  46. package/dist-client/client/components/default-slash-templates.test.js +51 -0
  47. package/dist-client/client/components/default-slash-templates.test.js.map +1 -0
  48. package/dist-client/client/components/markdown.d.ts +60 -0
  49. package/dist-client/client/components/markdown.js +422 -0
  50. package/dist-client/client/components/markdown.js.map +1 -0
  51. package/dist-client/client/components/markdown.test.d.ts +1 -0
  52. package/dist-client/client/components/markdown.test.js +418 -0
  53. package/dist-client/client/components/markdown.test.js.map +1 -0
  54. package/dist-client/client/components/mention-popup-helpers.d.ts +165 -0
  55. package/dist-client/client/components/mention-popup-helpers.js +330 -0
  56. package/dist-client/client/components/mention-popup-helpers.js.map +1 -0
  57. package/dist-client/client/components/mention-popup.d.ts +35 -0
  58. package/dist-client/client/components/mention-popup.js +223 -0
  59. package/dist-client/client/components/mention-popup.js.map +1 -0
  60. package/dist-client/client/components/mention-popup.test.d.ts +1 -0
  61. package/dist-client/client/components/mention-popup.test.js +474 -0
  62. package/dist-client/client/components/mention-popup.test.js.map +1 -0
  63. package/dist-client/client/index.d.ts +11 -0
  64. package/dist-client/client/index.js +12 -0
  65. package/dist-client/client/index.js.map +1 -0
  66. package/dist-client/client/utils/board-edit-patch.d.ts +157 -0
  67. package/dist-client/client/utils/board-edit-patch.js +284 -0
  68. package/dist-client/client/utils/board-edit-patch.js.map +1 -0
  69. package/dist-client/server/service/agentic-loop.d.ts +87 -0
  70. package/dist-client/server/service/agentic-loop.js +276 -0
  71. package/dist-client/server/service/agentic-loop.js.map +1 -0
  72. package/dist-client/server/service/assistant.d.ts +84 -0
  73. package/dist-client/server/service/assistant.js +2220 -0
  74. package/dist-client/server/service/assistant.js.map +1 -0
  75. package/dist-client/server/service/catalog-resolver.d.ts +18 -0
  76. package/dist-client/server/service/catalog-resolver.js +93 -0
  77. package/dist-client/server/service/catalog-resolver.js.map +1 -0
  78. package/dist-client/server/service/component-tree.d.ts +68 -0
  79. package/dist-client/server/service/component-tree.js +68 -0
  80. package/dist-client/server/service/component-tree.js.map +1 -0
  81. package/dist-client/server/service/mention-resolver.d.ts +96 -0
  82. package/dist-client/server/service/mention-resolver.js +172 -0
  83. package/dist-client/server/service/mention-resolver.js.map +1 -0
  84. package/dist-client/server/service/prefab-registry.d.ts +73 -0
  85. package/dist-client/server/service/prefab-registry.js +252 -0
  86. package/dist-client/server/service/prefab-registry.js.map +1 -0
  87. package/dist-client/server/service/styling/clear-tools.d.ts +15 -0
  88. package/dist-client/server/service/styling/clear-tools.js +50 -0
  89. package/dist-client/server/service/styling/clear-tools.js.map +1 -0
  90. package/dist-client/server/service/styling/color-utils.d.ts +43 -0
  91. package/dist-client/server/service/styling/color-utils.js +205 -0
  92. package/dist-client/server/service/styling/color-utils.js.map +1 -0
  93. package/dist-client/server/service/styling/copy-tools.d.ts +27 -0
  94. package/dist-client/server/service/styling/copy-tools.js +78 -0
  95. package/dist-client/server/service/styling/copy-tools.js.map +1 -0
  96. package/dist-client/server/service/styling/effect-tools.d.ts +45 -0
  97. package/dist-client/server/service/styling/effect-tools.js +85 -0
  98. package/dist-client/server/service/styling/effect-tools.js.map +1 -0
  99. package/dist-client/server/service/styling/fill-tools.d.ts +60 -0
  100. package/dist-client/server/service/styling/fill-tools.js +226 -0
  101. package/dist-client/server/service/styling/fill-tools.js.map +1 -0
  102. package/dist-client/server/service/styling/material-tools.d.ts +32 -0
  103. package/dist-client/server/service/styling/material-tools.js +88 -0
  104. package/dist-client/server/service/styling/material-tools.js.map +1 -0
  105. package/dist-client/server/service/styling/read-tools.d.ts +34 -0
  106. package/dist-client/server/service/styling/read-tools.js +42 -0
  107. package/dist-client/server/service/styling/read-tools.js.map +1 -0
  108. package/dist-client/server/service/styling/registry.d.ts +91 -0
  109. package/dist-client/server/service/styling/registry.js +345 -0
  110. package/dist-client/server/service/styling/registry.js.map +1 -0
  111. package/dist-client/server/service/styling/stroke-tools.d.ts +35 -0
  112. package/dist-client/server/service/styling/stroke-tools.js +94 -0
  113. package/dist-client/server/service/styling/stroke-tools.js.map +1 -0
  114. package/dist-client/server/service/styling/text-tools.d.ts +32 -0
  115. package/dist-client/server/service/styling/text-tools.js +101 -0
  116. package/dist-client/server/service/styling/text-tools.js.map +1 -0
  117. package/dist-client/server/service/types.d.ts +265 -0
  118. package/dist-client/server/service/types.js +2 -0
  119. package/dist-client/server/service/types.js.map +1 -0
  120. package/dist-client/server/service/validation/board-model-schema.d.ts +8311 -0
  121. package/dist-client/server/service/validation/board-model-schema.js +402 -0
  122. package/dist-client/server/service/validation/board-model-schema.js.map +1 -0
  123. package/dist-client/server/service/validation/tool-validation.d.ts +31 -0
  124. package/dist-client/server/service/validation/tool-validation.js +193 -0
  125. package/dist-client/server/service/validation/tool-validation.js.map +1 -0
  126. package/dist-client/tsconfig.tsbuildinfo +1 -0
  127. package/dist-server/index.d.ts +23 -0
  128. package/dist-server/index.js +27 -0
  129. package/dist-server/index.js.map +1 -0
  130. package/dist-server/service/agentic-loop.d.ts +87 -0
  131. package/dist-server/service/agentic-loop.js +280 -0
  132. package/dist-server/service/agentic-loop.js.map +1 -0
  133. package/dist-server/service/apply-patch.d.ts +47 -0
  134. package/dist-server/service/apply-patch.js +369 -0
  135. package/dist-server/service/apply-patch.js.map +1 -0
  136. package/dist-server/service/assistant.d.ts +84 -0
  137. package/dist-server/service/assistant.js +2237 -0
  138. package/dist-server/service/assistant.js.map +1 -0
  139. package/dist-server/service/board-ai-resolver.d.ts +54 -0
  140. package/dist-server/service/board-ai-resolver.js +409 -0
  141. package/dist-server/service/board-ai-resolver.js.map +1 -0
  142. package/dist-server/service/catalog-resolver.d.ts +18 -0
  143. package/dist-server/service/catalog-resolver.js +98 -0
  144. package/dist-server/service/catalog-resolver.js.map +1 -0
  145. package/dist-server/service/chat-message/chat-message-broadcast.d.ts +13 -0
  146. package/dist-server/service/chat-message/chat-message-broadcast.js +13 -0
  147. package/dist-server/service/chat-message/chat-message-broadcast.js.map +1 -0
  148. package/dist-server/service/chat-message/chat-message-publish.d.ts +6 -0
  149. package/dist-server/service/chat-message/chat-message-publish.js +27 -0
  150. package/dist-server/service/chat-message/chat-message-publish.js.map +1 -0
  151. package/dist-server/service/chat-message/chat-message-subscription.d.ts +8 -0
  152. package/dist-server/service/chat-message/chat-message-subscription.js +53 -0
  153. package/dist-server/service/chat-message/chat-message-subscription.js.map +1 -0
  154. package/dist-server/service/chat-message/chat-message.d.ts +56 -0
  155. package/dist-server/service/chat-message/chat-message.js +149 -0
  156. package/dist-server/service/chat-message/chat-message.js.map +1 -0
  157. package/dist-server/service/chat-message/index.d.ts +5 -0
  158. package/dist-server/service/chat-message/index.js +12 -0
  159. package/dist-server/service/chat-message/index.js.map +1 -0
  160. package/dist-server/service/chat-session/chat-session.d.ts +31 -0
  161. package/dist-server/service/chat-session/chat-session.js +131 -0
  162. package/dist-server/service/chat-session/chat-session.js.map +1 -0
  163. package/dist-server/service/chat-session/index.d.ts +3 -0
  164. package/dist-server/service/chat-session/index.js +7 -0
  165. package/dist-server/service/chat-session/index.js.map +1 -0
  166. package/dist-server/service/chat-session-participant/chat-session-participant.d.ts +30 -0
  167. package/dist-server/service/chat-session-participant/chat-session-participant.js +112 -0
  168. package/dist-server/service/chat-session-participant/chat-session-participant.js.map +1 -0
  169. package/dist-server/service/chat-session-participant/index.d.ts +4 -0
  170. package/dist-server/service/chat-session-participant/index.js +10 -0
  171. package/dist-server/service/chat-session-participant/index.js.map +1 -0
  172. package/dist-server/service/chat-session-participant/invite-notification.d.ts +27 -0
  173. package/dist-server/service/chat-session-participant/invite-notification.js +30 -0
  174. package/dist-server/service/chat-session-participant/invite-notification.js.map +1 -0
  175. package/dist-server/service/chat-session-participant/presence.d.ts +4 -0
  176. package/dist-server/service/chat-session-participant/presence.js +22 -0
  177. package/dist-server/service/chat-session-participant/presence.js.map +1 -0
  178. package/dist-server/service/chat-session-resolver.d.ts +20 -0
  179. package/dist-server/service/chat-session-resolver.js +356 -0
  180. package/dist-server/service/chat-session-resolver.js.map +1 -0
  181. package/dist-server/service/component-tree.d.ts +68 -0
  182. package/dist-server/service/component-tree.js +73 -0
  183. package/dist-server/service/component-tree.js.map +1 -0
  184. package/dist-server/service/index.d.ts +20 -0
  185. package/dist-server/service/index.js +34 -0
  186. package/dist-server/service/index.js.map +1 -0
  187. package/dist-server/service/mention-marker.d.ts +78 -0
  188. package/dist-server/service/mention-marker.js +162 -0
  189. package/dist-server/service/mention-marker.js.map +1 -0
  190. package/dist-server/service/mention-resolver.d.ts +96 -0
  191. package/dist-server/service/mention-resolver.js +177 -0
  192. package/dist-server/service/mention-resolver.js.map +1 -0
  193. package/dist-server/service/patch-entry/board-patch-broadcast.d.ts +13 -0
  194. package/dist-server/service/patch-entry/board-patch-broadcast.js +13 -0
  195. package/dist-server/service/patch-entry/board-patch-broadcast.js.map +1 -0
  196. package/dist-server/service/patch-entry/board-patch-subscription.d.ts +12 -0
  197. package/dist-server/service/patch-entry/board-patch-subscription.js +63 -0
  198. package/dist-server/service/patch-entry/board-patch-subscription.js.map +1 -0
  199. package/dist-server/service/patch-entry/index.d.ts +4 -0
  200. package/dist-server/service/patch-entry/index.js +12 -0
  201. package/dist-server/service/patch-entry/index.js.map +1 -0
  202. package/dist-server/service/patch-entry/patch-entry.d.ts +16 -0
  203. package/dist-server/service/patch-entry/patch-entry.js +96 -0
  204. package/dist-server/service/patch-entry/patch-entry.js.map +1 -0
  205. package/dist-server/service/prefab-registry.d.ts +73 -0
  206. package/dist-server/service/prefab-registry.js +258 -0
  207. package/dist-server/service/prefab-registry.js.map +1 -0
  208. package/dist-server/service/styling/clear-tools.d.ts +15 -0
  209. package/dist-server/service/styling/clear-tools.js +53 -0
  210. package/dist-server/service/styling/clear-tools.js.map +1 -0
  211. package/dist-server/service/styling/color-utils.d.ts +43 -0
  212. package/dist-server/service/styling/color-utils.js +210 -0
  213. package/dist-server/service/styling/color-utils.js.map +1 -0
  214. package/dist-server/service/styling/copy-tools.d.ts +27 -0
  215. package/dist-server/service/styling/copy-tools.js +81 -0
  216. package/dist-server/service/styling/copy-tools.js.map +1 -0
  217. package/dist-server/service/styling/effect-tools.d.ts +45 -0
  218. package/dist-server/service/styling/effect-tools.js +90 -0
  219. package/dist-server/service/styling/effect-tools.js.map +1 -0
  220. package/dist-server/service/styling/fill-tools.d.ts +60 -0
  221. package/dist-server/service/styling/fill-tools.js +229 -0
  222. package/dist-server/service/styling/fill-tools.js.map +1 -0
  223. package/dist-server/service/styling/material-tools.d.ts +32 -0
  224. package/dist-server/service/styling/material-tools.js +92 -0
  225. package/dist-server/service/styling/material-tools.js.map +1 -0
  226. package/dist-server/service/styling/read-tools.d.ts +34 -0
  227. package/dist-server/service/styling/read-tools.js +45 -0
  228. package/dist-server/service/styling/read-tools.js.map +1 -0
  229. package/dist-server/service/styling/registry.d.ts +91 -0
  230. package/dist-server/service/styling/registry.js +352 -0
  231. package/dist-server/service/styling/registry.js.map +1 -0
  232. package/dist-server/service/styling/stroke-tools.d.ts +35 -0
  233. package/dist-server/service/styling/stroke-tools.js +97 -0
  234. package/dist-server/service/styling/stroke-tools.js.map +1 -0
  235. package/dist-server/service/styling/text-tools.d.ts +32 -0
  236. package/dist-server/service/styling/text-tools.js +105 -0
  237. package/dist-server/service/styling/text-tools.js.map +1 -0
  238. package/dist-server/service/subscribe-auth.d.ts +14 -0
  239. package/dist-server/service/subscribe-auth.js +33 -0
  240. package/dist-server/service/subscribe-auth.js.map +1 -0
  241. package/dist-server/service/types.d.ts +265 -0
  242. package/dist-server/service/types.js +3 -0
  243. package/dist-server/service/types.js.map +1 -0
  244. package/dist-server/service/validation/board-model-schema.d.ts +8311 -0
  245. package/dist-server/service/validation/board-model-schema.js +409 -0
  246. package/dist-server/service/validation/board-model-schema.js.map +1 -0
  247. package/dist-server/service/validation/tool-validation.d.ts +31 -0
  248. package/dist-server/service/validation/tool-validation.js +196 -0
  249. package/dist-server/service/validation/tool-validation.js.map +1 -0
  250. package/dist-server/tsconfig.tsbuildinfo +1 -0
  251. package/package.json +48 -0
  252. package/server/index.ts +23 -0
  253. package/server/service/agentic-loop.test.ts +705 -0
  254. package/server/service/agentic-loop.ts +431 -0
  255. package/server/service/apply-patch-drift.test.ts +829 -0
  256. package/server/service/apply-patch.test.ts +757 -0
  257. package/server/service/apply-patch.ts +411 -0
  258. package/server/service/assistant-integration.test.ts +922 -0
  259. package/server/service/assistant-llm-smoke.test.ts +296 -0
  260. package/server/service/assistant.test.ts +2034 -0
  261. package/server/service/assistant.ts +2378 -0
  262. package/server/service/board-ai-resolver.ts +407 -0
  263. package/server/service/catalog-resolver.test.ts +137 -0
  264. package/server/service/catalog-resolver.ts +102 -0
  265. package/server/service/chat-message/chat-message-broadcast.integration.test.ts +73 -0
  266. package/server/service/chat-message/chat-message-broadcast.test.ts +40 -0
  267. package/server/service/chat-message/chat-message-broadcast.ts +27 -0
  268. package/server/service/chat-message/chat-message-publish.ts +22 -0
  269. package/server/service/chat-message/chat-message-subscription.ts +41 -0
  270. package/server/service/chat-message/chat-message.ts +169 -0
  271. package/server/service/chat-message/index.ts +12 -0
  272. package/server/service/chat-session/chat-session.ts +130 -0
  273. package/server/service/chat-session/index.ts +5 -0
  274. package/server/service/chat-session-participant/chat-session-participant.ts +110 -0
  275. package/server/service/chat-session-participant/index.ts +6 -0
  276. package/server/service/chat-session-participant/invite-notification.test.ts +30 -0
  277. package/server/service/chat-session-participant/invite-notification.ts +48 -0
  278. package/server/service/chat-session-participant/presence.test.ts +39 -0
  279. package/server/service/chat-session-participant/presence.ts +21 -0
  280. package/server/service/chat-session-resolver.ts +312 -0
  281. package/server/service/component-tree.test.ts +248 -0
  282. package/server/service/component-tree.ts +135 -0
  283. package/server/service/index.ts +32 -0
  284. package/server/service/mention-marker.extract.test.ts +61 -0
  285. package/server/service/mention-marker.test.ts +228 -0
  286. package/server/service/mention-marker.ts +184 -0
  287. package/server/service/mention-resolver.test.ts +273 -0
  288. package/server/service/mention-resolver.ts +264 -0
  289. package/server/service/patch-entry/board-patch-broadcast.ts +24 -0
  290. package/server/service/patch-entry/board-patch-subscription.test.ts +40 -0
  291. package/server/service/patch-entry/board-patch-subscription.ts +50 -0
  292. package/server/service/patch-entry/index.ts +12 -0
  293. package/server/service/patch-entry/patch-entry.ts +89 -0
  294. package/server/service/prefab-registry.test.ts +172 -0
  295. package/server/service/prefab-registry.ts +332 -0
  296. package/server/service/styling/clear-tools.test.ts +164 -0
  297. package/server/service/styling/clear-tools.ts +69 -0
  298. package/server/service/styling/color-utils.test.ts +130 -0
  299. package/server/service/styling/color-utils.ts +226 -0
  300. package/server/service/styling/copy-tools.test.ts +152 -0
  301. package/server/service/styling/copy-tools.ts +115 -0
  302. package/server/service/styling/effect-tools.test.ts +277 -0
  303. package/server/service/styling/effect-tools.ts +124 -0
  304. package/server/service/styling/fill-tools.test.ts +331 -0
  305. package/server/service/styling/fill-tools.ts +293 -0
  306. package/server/service/styling/material-tools.test.ts +294 -0
  307. package/server/service/styling/material-tools.ts +114 -0
  308. package/server/service/styling/read-tools.test.ts +78 -0
  309. package/server/service/styling/read-tools.ts +67 -0
  310. package/server/service/styling/registry.test.ts +203 -0
  311. package/server/service/styling/registry.ts +423 -0
  312. package/server/service/styling/stroke-tools.test.ts +245 -0
  313. package/server/service/styling/stroke-tools.ts +145 -0
  314. package/server/service/styling/text-tools.test.ts +253 -0
  315. package/server/service/styling/text-tools.ts +131 -0
  316. package/server/service/styling/tier1-tools.test.ts +300 -0
  317. package/server/service/subscribe-auth.test.ts +57 -0
  318. package/server/service/subscribe-auth.ts +34 -0
  319. package/server/service/types.ts +235 -0
  320. package/server/service/validation/board-model-schema.test.ts +778 -0
  321. package/server/service/validation/board-model-schema.ts +446 -0
  322. package/server/service/validation/tool-validation.ts +228 -0
  323. package/things-factory.config.js +1 -0
  324. package/translations/en.json +49 -0
  325. package/translations/ja.json +48 -0
  326. package/translations/ko.json +49 -0
  327. package/translations/ms.json +48 -0
  328. package/translations/zh.json +48 -0
  329. package/tsconfig.json +9 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * 실제 LLM 통합 스모크 테스트 (gated).
3
+ *
4
+ * 단위 / 통합 / 호스트 dispatcher 테스트 (3-layer 자동화) 가 mock 으로 잡지 못하는
5
+ * 한 가지: **실제 LLM 이 우리 tool catalog 를 보고 사용자 의도에 맞는 tool 을
6
+ * 선택하는가**. mock 으로는 tool 선택 결정을 시뮬레이션할 수 없다.
7
+ *
8
+ * 정책:
9
+ * - **Default 비활성** — RUN_LLM_SMOKE=1 환경변수 + provider API key 필요.
10
+ * `yarn test` 매 실행마다 돌리면 비용 누적 + 시간 소요 + 외부 의존성.
11
+ * - 릴리즈 게이트 / nightly cron 에서만 실행 권장 (수동: `yarn test:llm-smoke`)
12
+ * - 검증은 **느슨한** 매칭 — LLM 응답은 비결정적이라 tool 이름 집합에 포함되는지만
13
+ * 확인. 정확한 인자값은 검증하지 않는다.
14
+ * - 실패 시 toolUsages 콘솔 출력해서 어떤 tool 을 골랐는지 디버그 가능.
15
+ *
16
+ * Provider 자동 선택 우선순위:
17
+ * 1. LLM_SMOKE_PROVIDER 명시 (anthropic | gemini | openai)
18
+ * 2. 환경 키 보유 순서: ANTHROPIC_API_KEY → GEMINI_API_KEY → OPENAI_API_KEY
19
+ */
20
+ import {
21
+ createAIClient,
22
+ listRegisteredProviders,
23
+ type AIClient
24
+ } from '@things-factory/ai-client-base'
25
+ import { DefaultBoardAIAssistant } from './assistant'
26
+ import type { ChatOptions } from './types'
27
+
28
+ // providers 자동 등록 — import 만으로 registerAIProvider 발동
29
+ import '@things-factory/ai-client-base'
30
+
31
+ const RUN_FLAG = process.env.RUN_LLM_SMOKE === '1'
32
+
33
+ interface ProviderResolution {
34
+ provider: 'anthropic' | 'gemini' | 'openai'
35
+ apiKey: string
36
+ }
37
+
38
+ function resolveProvider(): ProviderResolution | null {
39
+ const explicit = process.env.LLM_SMOKE_PROVIDER as 'anthropic' | 'gemini' | 'openai' | undefined
40
+ const candidates: Array<{ provider: 'anthropic' | 'gemini' | 'openai'; envKey: string }> = [
41
+ { provider: 'anthropic', envKey: 'ANTHROPIC_API_KEY' },
42
+ { provider: 'gemini', envKey: 'GEMINI_API_KEY' },
43
+ { provider: 'openai', envKey: 'OPENAI_API_KEY' }
44
+ ]
45
+ if (explicit) {
46
+ const match = candidates.find(c => c.provider === explicit)
47
+ if (match && process.env[match.envKey]) {
48
+ return { provider: match.provider, apiKey: process.env[match.envKey]! }
49
+ }
50
+ return null
51
+ }
52
+ for (const c of candidates) {
53
+ if (process.env[c.envKey]) {
54
+ return { provider: c.provider, apiKey: process.env[c.envKey]! }
55
+ }
56
+ }
57
+ return null
58
+ }
59
+
60
+ const resolved = RUN_FLAG ? resolveProvider() : null
61
+ const ENABLED = !!resolved
62
+ const describeIf = ENABLED ? describe : describe.skip
63
+
64
+ const COMMON_OPTIONS: ChatOptions = {
65
+ knownTypes: ['rect', 'circle', 'text', 'gauge-circle'],
66
+ // 토큰 절약 + 빠른 응답
67
+ maxTokens: 1024,
68
+ temperature: 0.1
69
+ }
70
+
71
+ function makeClient(): AIClient {
72
+ // import 만으로는 jest 모듈 캐시 / hoisting 이슈로 registry 가 비어있을 수 있어
73
+ // 호출 시점에 등록 확인.
74
+ if (listRegisteredProviders().length === 0) {
75
+ // 강제 재로딩
76
+ require('@things-factory/ai-client-base')
77
+ }
78
+ return createAIClient({
79
+ provider: resolved!.provider,
80
+ apiKey: resolved!.apiKey
81
+ })
82
+ }
83
+
84
+ const TIMEOUT_MS = 90_000 // 다중 turn agentic loop 고려, 90초
85
+
86
+ const dumpUsages = (usages: any) => {
87
+ if (usages) console.log(' toolUsages:', JSON.stringify(usages.map((u: any) => ({ name: u.name, kind: u.kind })), null, 2))
88
+ }
89
+
90
+ describeIf('LLM 스모크 — 실제 모델 tool 선택 검증', () => {
91
+ let client: AIClient
92
+ beforeAll(() => {
93
+ client = makeClient()
94
+ // eslint-disable-next-line no-console
95
+ console.log(`[llm-smoke] provider=${resolved!.provider} client.id=${client.id}`)
96
+ })
97
+
98
+ test(
99
+ '"안녕" → text-only (tool 호출 없음)',
100
+ async () => {
101
+ const a = new DefaultBoardAIAssistant(client)
102
+ const r = await a.chat([{ role: 'user', content: '안녕' }], undefined, COMMON_OPTIONS)
103
+ expect(r.reply).toBeTruthy()
104
+ expect(r.patch).toBeUndefined()
105
+ expect(r.toolUsages ?? []).toHaveLength(0)
106
+ },
107
+ TIMEOUT_MS
108
+ )
109
+
110
+ test(
111
+ '"rect 하나 추가" → addComponent 호출',
112
+ async () => {
113
+ const a = new DefaultBoardAIAssistant(client)
114
+ const r = await a.chat(
115
+ [{ role: 'user', content: 'rect 하나 추가해줘' }],
116
+ { width: 1000, height: 600, components: [] } as any,
117
+ COMMON_OPTIONS
118
+ )
119
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
120
+ dumpUsages(r.toolUsages)
121
+ expect(names).toContain('addComponent')
122
+ expect(r.patch).toBeDefined()
123
+ expect(r.patch!.ops.some(op => op.op === 'add')).toBe(true)
124
+ },
125
+ TIMEOUT_MS
126
+ )
127
+
128
+ test(
129
+ '"선택한 컴포넌트 빨간색" + selectedRefids → modifyComponentByRefid',
130
+ async () => {
131
+ const a = new DefaultBoardAIAssistant(client)
132
+ const r = await a.chat(
133
+ [{ role: 'user', content: '선택한 컴포넌트를 빨간색으로 바꿔줘' }],
134
+ {
135
+ width: 1000,
136
+ height: 600,
137
+ components: [{ refid: 35, type: 'rect', left: 10, top: 20, fillStyle: '#fff' }]
138
+ } as any,
139
+ { ...COMMON_OPTIONS, selectedRefids: [35] }
140
+ )
141
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
142
+ dumpUsages(r.toolUsages)
143
+ // 받아들일 수 있는 경로: 직접 modify 또는 먼저 read 후 modify
144
+ expect(names).toContain('modifyComponentByRefid')
145
+ expect(r.patch?.ops.some(op => op.op === 'modify')).toBe(true)
146
+ },
147
+ TIMEOUT_MS
148
+ )
149
+
150
+ test(
151
+ '"왼쪽으로 정렬" + 다중 selection → alignComponents',
152
+ async () => {
153
+ const a = new DefaultBoardAIAssistant(client)
154
+ const r = await a.chat(
155
+ [{ role: 'user', content: '선택한 것들을 왼쪽으로 정렬해줘' }],
156
+ {
157
+ width: 1000,
158
+ height: 600,
159
+ components: [
160
+ { refid: 1, type: 'rect', left: 10, top: 0, width: 100, height: 50 },
161
+ { refid: 2, type: 'rect', left: 80, top: 100, width: 100, height: 50 }
162
+ ]
163
+ } as any,
164
+ { ...COMMON_OPTIONS, selectedRefids: [1, 2] }
165
+ )
166
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
167
+ dumpUsages(r.toolUsages)
168
+ expect(names).toContain('alignComponents')
169
+ },
170
+ TIMEOUT_MS
171
+ )
172
+
173
+ test(
174
+ '"보드 배경 어둡게" → modifyBoard',
175
+ async () => {
176
+ const a = new DefaultBoardAIAssistant(client)
177
+ const r = await a.chat(
178
+ [{ role: 'user', content: '보드 배경을 어둡게 해줘' }],
179
+ { width: 1000, height: 600, fillStyle: '#ffffff', components: [] } as any,
180
+ COMMON_OPTIONS
181
+ )
182
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
183
+ dumpUsages(r.toolUsages)
184
+ // modifyBoard 또는 atmosphere primer + modifyBoard 둘 다 OK
185
+ expect(names).toContain('modifyBoard')
186
+ },
187
+ TIMEOUT_MS
188
+ )
189
+
190
+ test(
191
+ '"심해 분위기로" → getAtmosphereGuide + modifyBoard 패턴',
192
+ async () => {
193
+ const a = new DefaultBoardAIAssistant(client)
194
+ const r = await a.chat(
195
+ [{ role: 'user', content: '이 보드를 심해 분위기로 만들어줘' }],
196
+ { width: 1000, height: 600, components: [] } as any,
197
+ COMMON_OPTIONS
198
+ )
199
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
200
+ dumpUsages(r.toolUsages)
201
+ // 핵심: AI 가 atmosphere primer 를 활용 (LLM 이 atmosphere 모르더라도 우리
202
+ // primer 가이드를 호출해야 일관된 결과 — 시스템 프롬프트의 의도)
203
+ expect(names).toContain('modifyBoard')
204
+ },
205
+ TIMEOUT_MS
206
+ )
207
+
208
+ test(
209
+ '"view 모드로" → setSceneMode action (ephemeral)',
210
+ async () => {
211
+ const a = new DefaultBoardAIAssistant(client)
212
+ const r = await a.chat(
213
+ [{ role: 'user', content: 'view 모드로 바꿔줘' }],
214
+ { width: 1000, height: 600, components: [] } as any,
215
+ COMMON_OPTIONS
216
+ )
217
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
218
+ dumpUsages(r.toolUsages)
219
+ expect(names).toContain('setSceneMode')
220
+ // ephemeral — patch 없음, actions 에 들어감
221
+ expect(r.actions?.some(a => a.action === 'setSceneMode')).toBe(true)
222
+ },
223
+ TIMEOUT_MS
224
+ )
225
+
226
+ test(
227
+ '"보드 전체 보이게" → fitToView action',
228
+ async () => {
229
+ const a = new DefaultBoardAIAssistant(client)
230
+ const r = await a.chat(
231
+ [{ role: 'user', content: '보드 전체가 보이게 화면 맞춰줘' }],
232
+ { width: 2000, height: 1200, components: [{ refid: 1, type: 'rect' }] } as any,
233
+ COMMON_OPTIONS
234
+ )
235
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
236
+ dumpUsages(r.toolUsages)
237
+ expect(names).toContain('fitToView')
238
+ },
239
+ TIMEOUT_MS
240
+ )
241
+
242
+ test(
243
+ '"선택한 거 화면 가운데" + selection → centerToComponent',
244
+ async () => {
245
+ const a = new DefaultBoardAIAssistant(client)
246
+ const r = await a.chat(
247
+ [{ role: 'user', content: '선택한 컴포넌트를 화면 가운데로 보여줘' }],
248
+ { width: 1000, height: 600, components: [{ refid: 7, type: 'rect' }] } as any,
249
+ { ...COMMON_OPTIONS, selectedRefids: [7] }
250
+ )
251
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
252
+ dumpUsages(r.toolUsages)
253
+ expect(names).toContain('centerToComponent')
254
+ },
255
+ TIMEOUT_MS
256
+ )
257
+
258
+ test(
259
+ '"refid 1 삭제" → removeComponentByRefid',
260
+ async () => {
261
+ const a = new DefaultBoardAIAssistant(client)
262
+ const r = await a.chat(
263
+ [{ role: 'user', content: 'refid 1번 컴포넌트 삭제해줘' }],
264
+ {
265
+ width: 1000,
266
+ height: 600,
267
+ components: [
268
+ { refid: 1, type: 'rect' },
269
+ { refid: 2, type: 'circle' }
270
+ ]
271
+ } as any,
272
+ COMMON_OPTIONS
273
+ )
274
+ const names = (r.toolUsages ?? []).map((u: any) => u.name)
275
+ dumpUsages(r.toolUsages)
276
+ expect(names).toContain('removeComponentByRefid')
277
+ expect(r.patch?.ops.some(op => op.op === 'remove')).toBe(true)
278
+ },
279
+ TIMEOUT_MS
280
+ )
281
+ })
282
+
283
+ // ── Skip 안내 — 사용법 노출 ───────────────────────────────────────
284
+ if (!ENABLED) {
285
+ describe('LLM 스모크 — 비활성 안내', () => {
286
+ test('실행 방법', () => {
287
+ // 의도적 no-op — 사용법을 콘솔로만 노출. 비활성 상태에서도 jest 가 1 test 통과로 보고.
288
+ // eslint-disable-next-line no-console
289
+ console.log(
290
+ '[llm-smoke] 비활성. 실행: RUN_LLM_SMOKE=1 ANTHROPIC_API_KEY=sk-... yarn test:llm-smoke ' +
291
+ '(또는 GEMINI_API_KEY / OPENAI_API_KEY 중 하나).'
292
+ )
293
+ expect(true).toBe(true)
294
+ })
295
+ })
296
+ }