@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.
- package/PLAN-styling-tools.md +1253 -0
- package/client/components/board-ai-chat.test.ts +149 -0
- package/client/components/board-ai-chat.ts +2574 -0
- package/client/components/chat-echo-dedup.test.ts +116 -0
- package/client/components/chat-echo-dedup.ts +89 -0
- package/client/components/chat-input-builder.ts +47 -0
- package/client/components/default-catalog-entries.test.ts +59 -0
- package/client/components/default-catalog-entries.ts +100 -0
- package/client/components/default-slash-templates.test.ts +55 -0
- package/client/components/default-slash-templates.ts +129 -0
- package/client/components/markdown.test.ts +518 -0
- package/client/components/markdown.ts +508 -0
- package/client/components/mention-popup-helpers.ts +433 -0
- package/client/components/mention-popup.test.ts +588 -0
- package/client/components/mention-popup.ts +238 -0
- package/client/index.ts +11 -0
- package/client/tsconfig.json +13 -0
- package/client/utils/board-edit-patch.ts +374 -0
- package/config/config.development.js +43 -0
- package/config/config.production.js +15 -0
- package/dist-client/client/components/board-ai-chat.d.ts +277 -0
- package/dist-client/client/components/board-ai-chat.js +2444 -0
- package/dist-client/client/components/board-ai-chat.js.map +1 -0
- package/dist-client/client/components/board-ai-chat.test.d.ts +1 -0
- package/dist-client/client/components/board-ai-chat.test.js +138 -0
- package/dist-client/client/components/board-ai-chat.test.js.map +1 -0
- package/dist-client/client/components/chat-echo-dedup.d.ts +45 -0
- package/dist-client/client/components/chat-echo-dedup.js +58 -0
- package/dist-client/client/components/chat-echo-dedup.js.map +1 -0
- package/dist-client/client/components/chat-echo-dedup.test.d.ts +1 -0
- package/dist-client/client/components/chat-echo-dedup.test.js +101 -0
- package/dist-client/client/components/chat-echo-dedup.test.js.map +1 -0
- package/dist-client/client/components/chat-input-builder.d.ts +39 -0
- package/dist-client/client/components/chat-input-builder.js +26 -0
- package/dist-client/client/components/chat-input-builder.js.map +1 -0
- package/dist-client/client/components/default-catalog-entries.d.ts +15 -0
- package/dist-client/client/components/default-catalog-entries.js +86 -0
- package/dist-client/client/components/default-catalog-entries.js.map +1 -0
- package/dist-client/client/components/default-catalog-entries.test.d.ts +1 -0
- package/dist-client/client/components/default-catalog-entries.test.js +52 -0
- package/dist-client/client/components/default-catalog-entries.test.js.map +1 -0
- package/dist-client/client/components/default-slash-templates.d.ts +17 -0
- package/dist-client/client/components/default-slash-templates.js +113 -0
- package/dist-client/client/components/default-slash-templates.js.map +1 -0
- package/dist-client/client/components/default-slash-templates.test.d.ts +1 -0
- package/dist-client/client/components/default-slash-templates.test.js +51 -0
- package/dist-client/client/components/default-slash-templates.test.js.map +1 -0
- package/dist-client/client/components/markdown.d.ts +60 -0
- package/dist-client/client/components/markdown.js +422 -0
- package/dist-client/client/components/markdown.js.map +1 -0
- package/dist-client/client/components/markdown.test.d.ts +1 -0
- package/dist-client/client/components/markdown.test.js +418 -0
- package/dist-client/client/components/markdown.test.js.map +1 -0
- package/dist-client/client/components/mention-popup-helpers.d.ts +165 -0
- package/dist-client/client/components/mention-popup-helpers.js +330 -0
- package/dist-client/client/components/mention-popup-helpers.js.map +1 -0
- package/dist-client/client/components/mention-popup.d.ts +35 -0
- package/dist-client/client/components/mention-popup.js +223 -0
- package/dist-client/client/components/mention-popup.js.map +1 -0
- package/dist-client/client/components/mention-popup.test.d.ts +1 -0
- package/dist-client/client/components/mention-popup.test.js +474 -0
- package/dist-client/client/components/mention-popup.test.js.map +1 -0
- package/dist-client/client/index.d.ts +11 -0
- package/dist-client/client/index.js +12 -0
- package/dist-client/client/index.js.map +1 -0
- package/dist-client/client/utils/board-edit-patch.d.ts +157 -0
- package/dist-client/client/utils/board-edit-patch.js +284 -0
- package/dist-client/client/utils/board-edit-patch.js.map +1 -0
- package/dist-client/server/service/agentic-loop.d.ts +87 -0
- package/dist-client/server/service/agentic-loop.js +276 -0
- package/dist-client/server/service/agentic-loop.js.map +1 -0
- package/dist-client/server/service/assistant.d.ts +84 -0
- package/dist-client/server/service/assistant.js +2220 -0
- package/dist-client/server/service/assistant.js.map +1 -0
- package/dist-client/server/service/catalog-resolver.d.ts +18 -0
- package/dist-client/server/service/catalog-resolver.js +93 -0
- package/dist-client/server/service/catalog-resolver.js.map +1 -0
- package/dist-client/server/service/component-tree.d.ts +68 -0
- package/dist-client/server/service/component-tree.js +68 -0
- package/dist-client/server/service/component-tree.js.map +1 -0
- package/dist-client/server/service/mention-resolver.d.ts +96 -0
- package/dist-client/server/service/mention-resolver.js +172 -0
- package/dist-client/server/service/mention-resolver.js.map +1 -0
- package/dist-client/server/service/prefab-registry.d.ts +73 -0
- package/dist-client/server/service/prefab-registry.js +252 -0
- package/dist-client/server/service/prefab-registry.js.map +1 -0
- package/dist-client/server/service/styling/clear-tools.d.ts +15 -0
- package/dist-client/server/service/styling/clear-tools.js +50 -0
- package/dist-client/server/service/styling/clear-tools.js.map +1 -0
- package/dist-client/server/service/styling/color-utils.d.ts +43 -0
- package/dist-client/server/service/styling/color-utils.js +205 -0
- package/dist-client/server/service/styling/color-utils.js.map +1 -0
- package/dist-client/server/service/styling/copy-tools.d.ts +27 -0
- package/dist-client/server/service/styling/copy-tools.js +78 -0
- package/dist-client/server/service/styling/copy-tools.js.map +1 -0
- package/dist-client/server/service/styling/effect-tools.d.ts +45 -0
- package/dist-client/server/service/styling/effect-tools.js +85 -0
- package/dist-client/server/service/styling/effect-tools.js.map +1 -0
- package/dist-client/server/service/styling/fill-tools.d.ts +60 -0
- package/dist-client/server/service/styling/fill-tools.js +226 -0
- package/dist-client/server/service/styling/fill-tools.js.map +1 -0
- package/dist-client/server/service/styling/material-tools.d.ts +32 -0
- package/dist-client/server/service/styling/material-tools.js +88 -0
- package/dist-client/server/service/styling/material-tools.js.map +1 -0
- package/dist-client/server/service/styling/read-tools.d.ts +34 -0
- package/dist-client/server/service/styling/read-tools.js +42 -0
- package/dist-client/server/service/styling/read-tools.js.map +1 -0
- package/dist-client/server/service/styling/registry.d.ts +91 -0
- package/dist-client/server/service/styling/registry.js +345 -0
- package/dist-client/server/service/styling/registry.js.map +1 -0
- package/dist-client/server/service/styling/stroke-tools.d.ts +35 -0
- package/dist-client/server/service/styling/stroke-tools.js +94 -0
- package/dist-client/server/service/styling/stroke-tools.js.map +1 -0
- package/dist-client/server/service/styling/text-tools.d.ts +32 -0
- package/dist-client/server/service/styling/text-tools.js +101 -0
- package/dist-client/server/service/styling/text-tools.js.map +1 -0
- package/dist-client/server/service/types.d.ts +265 -0
- package/dist-client/server/service/types.js +2 -0
- package/dist-client/server/service/types.js.map +1 -0
- package/dist-client/server/service/validation/board-model-schema.d.ts +8311 -0
- package/dist-client/server/service/validation/board-model-schema.js +402 -0
- package/dist-client/server/service/validation/board-model-schema.js.map +1 -0
- package/dist-client/server/service/validation/tool-validation.d.ts +31 -0
- package/dist-client/server/service/validation/tool-validation.js +193 -0
- package/dist-client/server/service/validation/tool-validation.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/index.d.ts +23 -0
- package/dist-server/index.js +27 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/service/agentic-loop.d.ts +87 -0
- package/dist-server/service/agentic-loop.js +280 -0
- package/dist-server/service/agentic-loop.js.map +1 -0
- package/dist-server/service/apply-patch.d.ts +47 -0
- package/dist-server/service/apply-patch.js +369 -0
- package/dist-server/service/apply-patch.js.map +1 -0
- package/dist-server/service/assistant.d.ts +84 -0
- package/dist-server/service/assistant.js +2237 -0
- package/dist-server/service/assistant.js.map +1 -0
- package/dist-server/service/board-ai-resolver.d.ts +54 -0
- package/dist-server/service/board-ai-resolver.js +409 -0
- package/dist-server/service/board-ai-resolver.js.map +1 -0
- package/dist-server/service/catalog-resolver.d.ts +18 -0
- package/dist-server/service/catalog-resolver.js +98 -0
- package/dist-server/service/catalog-resolver.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-broadcast.d.ts +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-publish.d.ts +6 -0
- package/dist-server/service/chat-message/chat-message-publish.js +27 -0
- package/dist-server/service/chat-message/chat-message-publish.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-subscription.d.ts +8 -0
- package/dist-server/service/chat-message/chat-message-subscription.js +53 -0
- package/dist-server/service/chat-message/chat-message-subscription.js.map +1 -0
- package/dist-server/service/chat-message/chat-message.d.ts +56 -0
- package/dist-server/service/chat-message/chat-message.js +149 -0
- package/dist-server/service/chat-message/chat-message.js.map +1 -0
- package/dist-server/service/chat-message/index.d.ts +5 -0
- package/dist-server/service/chat-message/index.js +12 -0
- package/dist-server/service/chat-message/index.js.map +1 -0
- package/dist-server/service/chat-session/chat-session.d.ts +31 -0
- package/dist-server/service/chat-session/chat-session.js +131 -0
- package/dist-server/service/chat-session/chat-session.js.map +1 -0
- package/dist-server/service/chat-session/index.d.ts +3 -0
- package/dist-server/service/chat-session/index.js +7 -0
- package/dist-server/service/chat-session/index.js.map +1 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.d.ts +30 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js +112 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js.map +1 -0
- package/dist-server/service/chat-session-participant/index.d.ts +4 -0
- package/dist-server/service/chat-session-participant/index.js +10 -0
- package/dist-server/service/chat-session-participant/index.js.map +1 -0
- package/dist-server/service/chat-session-participant/invite-notification.d.ts +27 -0
- package/dist-server/service/chat-session-participant/invite-notification.js +30 -0
- package/dist-server/service/chat-session-participant/invite-notification.js.map +1 -0
- package/dist-server/service/chat-session-participant/presence.d.ts +4 -0
- package/dist-server/service/chat-session-participant/presence.js +22 -0
- package/dist-server/service/chat-session-participant/presence.js.map +1 -0
- package/dist-server/service/chat-session-resolver.d.ts +20 -0
- package/dist-server/service/chat-session-resolver.js +356 -0
- package/dist-server/service/chat-session-resolver.js.map +1 -0
- package/dist-server/service/component-tree.d.ts +68 -0
- package/dist-server/service/component-tree.js +73 -0
- package/dist-server/service/component-tree.js.map +1 -0
- package/dist-server/service/index.d.ts +20 -0
- package/dist-server/service/index.js +34 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/mention-marker.d.ts +78 -0
- package/dist-server/service/mention-marker.js +162 -0
- package/dist-server/service/mention-marker.js.map +1 -0
- package/dist-server/service/mention-resolver.d.ts +96 -0
- package/dist-server/service/mention-resolver.js +177 -0
- package/dist-server/service/mention-resolver.js.map +1 -0
- package/dist-server/service/patch-entry/board-patch-broadcast.d.ts +13 -0
- package/dist-server/service/patch-entry/board-patch-broadcast.js +13 -0
- package/dist-server/service/patch-entry/board-patch-broadcast.js.map +1 -0
- package/dist-server/service/patch-entry/board-patch-subscription.d.ts +12 -0
- package/dist-server/service/patch-entry/board-patch-subscription.js +63 -0
- package/dist-server/service/patch-entry/board-patch-subscription.js.map +1 -0
- package/dist-server/service/patch-entry/index.d.ts +4 -0
- package/dist-server/service/patch-entry/index.js +12 -0
- package/dist-server/service/patch-entry/index.js.map +1 -0
- package/dist-server/service/patch-entry/patch-entry.d.ts +16 -0
- package/dist-server/service/patch-entry/patch-entry.js +96 -0
- package/dist-server/service/patch-entry/patch-entry.js.map +1 -0
- package/dist-server/service/prefab-registry.d.ts +73 -0
- package/dist-server/service/prefab-registry.js +258 -0
- package/dist-server/service/prefab-registry.js.map +1 -0
- package/dist-server/service/styling/clear-tools.d.ts +15 -0
- package/dist-server/service/styling/clear-tools.js +53 -0
- package/dist-server/service/styling/clear-tools.js.map +1 -0
- package/dist-server/service/styling/color-utils.d.ts +43 -0
- package/dist-server/service/styling/color-utils.js +210 -0
- package/dist-server/service/styling/color-utils.js.map +1 -0
- package/dist-server/service/styling/copy-tools.d.ts +27 -0
- package/dist-server/service/styling/copy-tools.js +81 -0
- package/dist-server/service/styling/copy-tools.js.map +1 -0
- package/dist-server/service/styling/effect-tools.d.ts +45 -0
- package/dist-server/service/styling/effect-tools.js +90 -0
- package/dist-server/service/styling/effect-tools.js.map +1 -0
- package/dist-server/service/styling/fill-tools.d.ts +60 -0
- package/dist-server/service/styling/fill-tools.js +229 -0
- package/dist-server/service/styling/fill-tools.js.map +1 -0
- package/dist-server/service/styling/material-tools.d.ts +32 -0
- package/dist-server/service/styling/material-tools.js +92 -0
- package/dist-server/service/styling/material-tools.js.map +1 -0
- package/dist-server/service/styling/read-tools.d.ts +34 -0
- package/dist-server/service/styling/read-tools.js +45 -0
- package/dist-server/service/styling/read-tools.js.map +1 -0
- package/dist-server/service/styling/registry.d.ts +91 -0
- package/dist-server/service/styling/registry.js +352 -0
- package/dist-server/service/styling/registry.js.map +1 -0
- package/dist-server/service/styling/stroke-tools.d.ts +35 -0
- package/dist-server/service/styling/stroke-tools.js +97 -0
- package/dist-server/service/styling/stroke-tools.js.map +1 -0
- package/dist-server/service/styling/text-tools.d.ts +32 -0
- package/dist-server/service/styling/text-tools.js +105 -0
- package/dist-server/service/styling/text-tools.js.map +1 -0
- package/dist-server/service/subscribe-auth.d.ts +14 -0
- package/dist-server/service/subscribe-auth.js +33 -0
- package/dist-server/service/subscribe-auth.js.map +1 -0
- package/dist-server/service/types.d.ts +265 -0
- package/dist-server/service/types.js +3 -0
- package/dist-server/service/types.js.map +1 -0
- package/dist-server/service/validation/board-model-schema.d.ts +8311 -0
- package/dist-server/service/validation/board-model-schema.js +409 -0
- package/dist-server/service/validation/board-model-schema.js.map +1 -0
- package/dist-server/service/validation/tool-validation.d.ts +31 -0
- package/dist-server/service/validation/tool-validation.js +196 -0
- package/dist-server/service/validation/tool-validation.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +48 -0
- package/server/index.ts +23 -0
- package/server/service/agentic-loop.test.ts +705 -0
- package/server/service/agentic-loop.ts +431 -0
- package/server/service/apply-patch-drift.test.ts +829 -0
- package/server/service/apply-patch.test.ts +757 -0
- package/server/service/apply-patch.ts +411 -0
- package/server/service/assistant-integration.test.ts +922 -0
- package/server/service/assistant-llm-smoke.test.ts +296 -0
- package/server/service/assistant.test.ts +2034 -0
- package/server/service/assistant.ts +2378 -0
- package/server/service/board-ai-resolver.ts +407 -0
- package/server/service/catalog-resolver.test.ts +137 -0
- package/server/service/catalog-resolver.ts +102 -0
- package/server/service/chat-message/chat-message-broadcast.integration.test.ts +73 -0
- package/server/service/chat-message/chat-message-broadcast.test.ts +40 -0
- package/server/service/chat-message/chat-message-broadcast.ts +27 -0
- package/server/service/chat-message/chat-message-publish.ts +22 -0
- package/server/service/chat-message/chat-message-subscription.ts +41 -0
- package/server/service/chat-message/chat-message.ts +169 -0
- package/server/service/chat-message/index.ts +12 -0
- package/server/service/chat-session/chat-session.ts +130 -0
- package/server/service/chat-session/index.ts +5 -0
- package/server/service/chat-session-participant/chat-session-participant.ts +110 -0
- package/server/service/chat-session-participant/index.ts +6 -0
- package/server/service/chat-session-participant/invite-notification.test.ts +30 -0
- package/server/service/chat-session-participant/invite-notification.ts +48 -0
- package/server/service/chat-session-participant/presence.test.ts +39 -0
- package/server/service/chat-session-participant/presence.ts +21 -0
- package/server/service/chat-session-resolver.ts +312 -0
- package/server/service/component-tree.test.ts +248 -0
- package/server/service/component-tree.ts +135 -0
- package/server/service/index.ts +32 -0
- package/server/service/mention-marker.extract.test.ts +61 -0
- package/server/service/mention-marker.test.ts +228 -0
- package/server/service/mention-marker.ts +184 -0
- package/server/service/mention-resolver.test.ts +273 -0
- package/server/service/mention-resolver.ts +264 -0
- package/server/service/patch-entry/board-patch-broadcast.ts +24 -0
- package/server/service/patch-entry/board-patch-subscription.test.ts +40 -0
- package/server/service/patch-entry/board-patch-subscription.ts +50 -0
- package/server/service/patch-entry/index.ts +12 -0
- package/server/service/patch-entry/patch-entry.ts +89 -0
- package/server/service/prefab-registry.test.ts +172 -0
- package/server/service/prefab-registry.ts +332 -0
- package/server/service/styling/clear-tools.test.ts +164 -0
- package/server/service/styling/clear-tools.ts +69 -0
- package/server/service/styling/color-utils.test.ts +130 -0
- package/server/service/styling/color-utils.ts +226 -0
- package/server/service/styling/copy-tools.test.ts +152 -0
- package/server/service/styling/copy-tools.ts +115 -0
- package/server/service/styling/effect-tools.test.ts +277 -0
- package/server/service/styling/effect-tools.ts +124 -0
- package/server/service/styling/fill-tools.test.ts +331 -0
- package/server/service/styling/fill-tools.ts +293 -0
- package/server/service/styling/material-tools.test.ts +294 -0
- package/server/service/styling/material-tools.ts +114 -0
- package/server/service/styling/read-tools.test.ts +78 -0
- package/server/service/styling/read-tools.ts +67 -0
- package/server/service/styling/registry.test.ts +203 -0
- package/server/service/styling/registry.ts +423 -0
- package/server/service/styling/stroke-tools.test.ts +245 -0
- package/server/service/styling/stroke-tools.ts +145 -0
- package/server/service/styling/text-tools.test.ts +253 -0
- package/server/service/styling/text-tools.ts +131 -0
- package/server/service/styling/tier1-tools.test.ts +300 -0
- package/server/service/subscribe-auth.test.ts +57 -0
- package/server/service/subscribe-auth.ts +34 -0
- package/server/service/types.ts +235 -0
- package/server/service/validation/board-model-schema.test.ts +778 -0
- package/server/service/validation/board-model-schema.ts +446 -0
- package/server/service/validation/tool-validation.ts +228 -0
- package/things-factory.config.js +1 -0
- package/translations/en.json +49 -0
- package/translations/ja.json +48 -0
- package/translations/ko.json +49 -0
- package/translations/ms.json +48 -0
- package/translations/zh.json +48 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentic-loop — multi-turn LLM ↔ tool 루프.
|
|
3
|
+
*
|
|
4
|
+
* assistant.ts 에서 추출 — loop 자체를 *순수 함수* 로 만들어:
|
|
5
|
+
* 1. 단위 테스트 가능 (assistant.ts 의 board-import → @aws-sdk transitive 경로 회피)
|
|
6
|
+
* 2. 관측성 (error boundary / iter log / AbortSignal / repeated-failure 조기 종료)
|
|
7
|
+
* 을 한 곳에 응집
|
|
8
|
+
* 3. 향후 split (Phase 3) 의 자연 경계
|
|
9
|
+
*
|
|
10
|
+
* 입력 — `chat` 함수와 dispatch helper 들을 의존성 주입. board-ai 본체는 본 모듈에
|
|
11
|
+
* 의존하지만 본 모듈은 board-ai 의 어떤 무거운 import 도 모름.
|
|
12
|
+
*/
|
|
13
|
+
import debug from 'debug';
|
|
14
|
+
const log = debug('things-factory:board-ai:loop');
|
|
15
|
+
// ── Loop ─────────────────────────────────────────────────────────
|
|
16
|
+
const DEFAULT_MAX_ITERATIONS = 8;
|
|
17
|
+
/**
|
|
18
|
+
* multi-turn agentic loop.
|
|
19
|
+
*
|
|
20
|
+
* LLM 이 read tool 을 호출 → 서버에서 즉시 실행 → 결과 회신 → LLM 추론 반복.
|
|
21
|
+
* write tool 은 누적해 client 에 patch 로 전달. 무한 루프 방지를 위해 iteration cap.
|
|
22
|
+
*
|
|
23
|
+
* 본 함수는 *순수* — input 만 보고 결정. 외부 mutable state 없음.
|
|
24
|
+
*/
|
|
25
|
+
export async function runAgenticLoop(input) {
|
|
26
|
+
const { initialConversation, tools, options, chat, dispatch, currentBoard, selectedRefids } = input;
|
|
27
|
+
const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
28
|
+
const failureLimit = options.repeatedFailureLimit ?? 3;
|
|
29
|
+
const signal = options.signal;
|
|
30
|
+
let conversation = initialConversation;
|
|
31
|
+
const accumulatedWriteCalls = [];
|
|
32
|
+
const accumulatedActions = [];
|
|
33
|
+
const toolUsages = [];
|
|
34
|
+
let lastText;
|
|
35
|
+
let stopReason = 'end_turn';
|
|
36
|
+
let abortReason;
|
|
37
|
+
// 같은 tool 의 validation 실패 연속 카운터 — 다른 tool 호출 시 reset
|
|
38
|
+
let lastFailedToolName;
|
|
39
|
+
let consecutiveFailures = 0;
|
|
40
|
+
let iter = 0;
|
|
41
|
+
for (; iter < maxIterations; iter++) {
|
|
42
|
+
// (1) AbortSignal — iteration 시작 시 체크
|
|
43
|
+
if (signal?.aborted) {
|
|
44
|
+
log('iter %d: aborted by signal', iter);
|
|
45
|
+
abortReason = { type: 'aborted', iter };
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
log('iter %d: chat() 호출', iter);
|
|
49
|
+
let result;
|
|
50
|
+
try {
|
|
51
|
+
// (2) Error boundary — provider 예외 시 graceful 종료
|
|
52
|
+
result = await chat(conversation, tools, {
|
|
53
|
+
systemPrompt: options.systemPrompt,
|
|
54
|
+
model: options.model,
|
|
55
|
+
temperature: options.temperature,
|
|
56
|
+
maxTokens: options.maxTokens,
|
|
57
|
+
toolChoice: 'auto',
|
|
58
|
+
allowParallelToolCalls: true,
|
|
59
|
+
signal
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
// AbortError — 별도 분류
|
|
64
|
+
if (e?.name === 'AbortError' || signal?.aborted) {
|
|
65
|
+
log('iter %d: provider AbortError', iter);
|
|
66
|
+
abortReason = { type: 'aborted', iter };
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
log('iter %d: provider error: %s', iter, e?.message);
|
|
70
|
+
abortReason = {
|
|
71
|
+
type: 'provider_error',
|
|
72
|
+
message: e?.message ?? String(e),
|
|
73
|
+
iter
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
lastText = result.text || lastText;
|
|
79
|
+
stopReason = result.stopReason;
|
|
80
|
+
log('iter %d: text=%s, toolCalls=%d, stopReason=%s', iter, result.text ? `"${result.text.slice(0, 60)}..."` : '(none)', result.toolCalls.length, result.stopReason);
|
|
81
|
+
if (result.toolCalls.length === 0)
|
|
82
|
+
break;
|
|
83
|
+
// (1b) 삭제: chat() 직후 abort 는 다음 iter 시작 시 (1) 에서 감지.
|
|
84
|
+
// 현재 iter tool 실행을 중단하면 read tool 결과도 버려져 테스트/동작 불일치.
|
|
85
|
+
// tool 결과 빌드 — read 는 즉시 실행, write 는 누적, action 은 별도 채널
|
|
86
|
+
const beforeUsages = toolUsages.length;
|
|
87
|
+
const toolResultParts = await processToolCalls(result.toolCalls, currentBoard, selectedRefids, dispatch, accumulatedWriteCalls, accumulatedActions, toolUsages);
|
|
88
|
+
// (4) 같은 tool 의 validation 실패 연속 감지 — LLM 이 같은 잘못 반복 시 abort
|
|
89
|
+
const newUsages = toolUsages.slice(beforeUsages);
|
|
90
|
+
const repeatedFailureAbort = detectRepeatedValidationFailure(newUsages, lastFailedToolName, consecutiveFailures, failureLimit);
|
|
91
|
+
if (repeatedFailureAbort.shouldAbort) {
|
|
92
|
+
log('iter %d: repeated validation failure on %s (%d consecutive) — abort', iter, repeatedFailureAbort.toolName, repeatedFailureAbort.consecutive);
|
|
93
|
+
abortReason = {
|
|
94
|
+
type: 'repeated_validation_failure',
|
|
95
|
+
toolName: repeatedFailureAbort.toolName,
|
|
96
|
+
consecutive: repeatedFailureAbort.consecutive,
|
|
97
|
+
iter
|
|
98
|
+
};
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
lastFailedToolName = repeatedFailureAbort.lastFailedToolName;
|
|
102
|
+
consecutiveFailures = repeatedFailureAbort.consecutive;
|
|
103
|
+
// assistant 의 tool_use turn + user 의 tool_result turn 을 conversation 에 누적
|
|
104
|
+
const assistantContent = [];
|
|
105
|
+
if (result.text)
|
|
106
|
+
assistantContent.push({ type: 'text', text: result.text });
|
|
107
|
+
for (const tc of result.toolCalls) {
|
|
108
|
+
// providerMeta (Gemini thoughtSignature 등) 를 그대로 round-trip — 안 보내면 400.
|
|
109
|
+
assistantContent.push({
|
|
110
|
+
type: 'tool_use',
|
|
111
|
+
id: tc.id,
|
|
112
|
+
name: tc.name,
|
|
113
|
+
arguments: tc.arguments,
|
|
114
|
+
...(tc.providerMeta && { providerMeta: tc.providerMeta })
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
conversation = [
|
|
118
|
+
...conversation,
|
|
119
|
+
{ role: 'assistant', content: assistantContent },
|
|
120
|
+
{ role: 'user', content: toolResultParts }
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
// (3) max iterations 도달 시 사용자에게 표면화
|
|
124
|
+
if (iter >= maxIterations && !abortReason) {
|
|
125
|
+
log('iter %d: max iterations reached', iter);
|
|
126
|
+
abortReason = { type: 'max_iterations', iter };
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
lastText,
|
|
130
|
+
accumulatedWriteCalls,
|
|
131
|
+
accumulatedActions,
|
|
132
|
+
toolUsages,
|
|
133
|
+
stopReason,
|
|
134
|
+
abortReason
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 같은 tool 의 validation 실패 연속 감지 — pure helper.
|
|
139
|
+
*
|
|
140
|
+
* 같은 tool 이 반복 fail 하면 LLM 이 self-correct 못 하는 상태.
|
|
141
|
+
* 다른 tool 이 fail 하면 시퀀스 새로 시작 (전혀 다른 패턴이라).
|
|
142
|
+
* tool 이 succeed 하면 카운터 reset.
|
|
143
|
+
*/
|
|
144
|
+
function detectRepeatedValidationFailure(newUsages, lastFailedToolName, consecutiveFailures, limit) {
|
|
145
|
+
// 이번 iteration 에서 실패한 write tool 들 (validation 거절)
|
|
146
|
+
const failed = newUsages.filter(u => u.kind === 'write' && u.result?.error === 'Tool args failed validation');
|
|
147
|
+
if (failed.length === 0) {
|
|
148
|
+
// 실패 없음 — reset
|
|
149
|
+
return {
|
|
150
|
+
shouldAbort: false,
|
|
151
|
+
toolName: '',
|
|
152
|
+
consecutive: 0,
|
|
153
|
+
lastFailedToolName: undefined
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
// 모든 실패가 같은 tool 인지 확인
|
|
157
|
+
const firstName = failed[0].name;
|
|
158
|
+
const allSame = failed.every(u => u.name === firstName);
|
|
159
|
+
if (allSame && firstName === lastFailedToolName) {
|
|
160
|
+
const consecutive = consecutiveFailures + 1;
|
|
161
|
+
return {
|
|
162
|
+
shouldAbort: consecutive >= limit,
|
|
163
|
+
toolName: firstName,
|
|
164
|
+
consecutive,
|
|
165
|
+
lastFailedToolName: firstName
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// 다른 tool 또는 첫 실패
|
|
169
|
+
return {
|
|
170
|
+
shouldAbort: false,
|
|
171
|
+
toolName: firstName,
|
|
172
|
+
consecutive: 1,
|
|
173
|
+
lastFailedToolName: firstName
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 한 iteration 의 tool 결과를 분류·실행·누적. (loop 에서 분리해 읽기 쉽게.)
|
|
178
|
+
*/
|
|
179
|
+
async function processToolCalls(toolCalls, currentBoard, selectedRefids, dispatch, accumulatedWriteCalls, accumulatedActions, toolUsages) {
|
|
180
|
+
const toolResultParts = [];
|
|
181
|
+
for (const tc of toolCalls) {
|
|
182
|
+
if (dispatch.isReadTool(tc.name)) {
|
|
183
|
+
const value = dispatch.executeReadTool(tc, currentBoard, selectedRefids);
|
|
184
|
+
toolResultParts.push({
|
|
185
|
+
type: 'tool_result',
|
|
186
|
+
toolUseId: tc.id,
|
|
187
|
+
content: JSON.stringify(value)
|
|
188
|
+
});
|
|
189
|
+
toolUsages.push({
|
|
190
|
+
name: tc.name,
|
|
191
|
+
arguments: tc.arguments ?? {},
|
|
192
|
+
result: dispatch.summarizeToolResult(value),
|
|
193
|
+
kind: 'read'
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
else if (dispatch.isWriteTool(tc.name)) {
|
|
197
|
+
// 사전 검증 — args 가 schema / build 함수 검증 통과해야 누적.
|
|
198
|
+
// 실패 시 LLM 한테 detailed error 반환 → 다음 turn 에서 자기 회복 (retry).
|
|
199
|
+
const components = (currentBoard?.components ?? []);
|
|
200
|
+
const validation = dispatch.validateWriteToolCall(tc, components);
|
|
201
|
+
if (!validation.valid) {
|
|
202
|
+
const errorResult = {
|
|
203
|
+
error: 'Tool args failed validation',
|
|
204
|
+
issues: validation.errors,
|
|
205
|
+
suggestion: validation.suggestion,
|
|
206
|
+
note: 'Fix the args and call the tool again.'
|
|
207
|
+
};
|
|
208
|
+
toolResultParts.push({
|
|
209
|
+
type: 'tool_result',
|
|
210
|
+
toolUseId: tc.id,
|
|
211
|
+
content: JSON.stringify(errorResult)
|
|
212
|
+
});
|
|
213
|
+
toolUsages.push({
|
|
214
|
+
name: tc.name,
|
|
215
|
+
arguments: tc.arguments ?? {},
|
|
216
|
+
result: errorResult,
|
|
217
|
+
kind: 'write'
|
|
218
|
+
});
|
|
219
|
+
// continue — 누적 안 함. LLM 이 다음 turn 에서 정정 호출.
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
accumulatedWriteCalls.push(tc);
|
|
223
|
+
const queuedResult = { queued: true, note: 'Will be applied on the client.' };
|
|
224
|
+
toolResultParts.push({
|
|
225
|
+
type: 'tool_result',
|
|
226
|
+
toolUseId: tc.id,
|
|
227
|
+
content: JSON.stringify(queuedResult)
|
|
228
|
+
});
|
|
229
|
+
toolUsages.push({
|
|
230
|
+
name: tc.name,
|
|
231
|
+
arguments: tc.arguments ?? {},
|
|
232
|
+
result: queuedResult,
|
|
233
|
+
kind: 'write'
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else if (dispatch.isActionTool(tc.name)) {
|
|
238
|
+
// ephemeral scene 조작 — accumulatedActions 로 누적, ChatResponse.actions 로 client 전달
|
|
239
|
+
const action = dispatch.toolCallToBoardActionOp(tc);
|
|
240
|
+
if (action)
|
|
241
|
+
accumulatedActions.push(action);
|
|
242
|
+
const queuedResult = {
|
|
243
|
+
queued: true,
|
|
244
|
+
note: 'Will be executed on the modeller (ephemeral).'
|
|
245
|
+
};
|
|
246
|
+
toolResultParts.push({
|
|
247
|
+
type: 'tool_result',
|
|
248
|
+
toolUseId: tc.id,
|
|
249
|
+
content: JSON.stringify(queuedResult)
|
|
250
|
+
});
|
|
251
|
+
toolUsages.push({
|
|
252
|
+
name: tc.name,
|
|
253
|
+
arguments: tc.arguments ?? {},
|
|
254
|
+
result: queuedResult,
|
|
255
|
+
kind: 'write'
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const errResult = { error: `Unknown tool: ${tc.name}` };
|
|
260
|
+
toolResultParts.push({
|
|
261
|
+
type: 'tool_result',
|
|
262
|
+
toolUseId: tc.id,
|
|
263
|
+
content: JSON.stringify(errResult),
|
|
264
|
+
isError: true
|
|
265
|
+
});
|
|
266
|
+
toolUsages.push({
|
|
267
|
+
name: tc.name,
|
|
268
|
+
arguments: tc.arguments ?? {},
|
|
269
|
+
result: errResult,
|
|
270
|
+
kind: 'unknown'
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return toolResultParts;
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=agentic-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentic-loop.js","sourceRoot":"","sources":["../../../server/service/agentic-loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * agentic-loop — multi-turn LLM ↔ tool 루프.\n *\n * assistant.ts 에서 추출 — loop 자체를 *순수 함수* 로 만들어:\n * 1. 단위 테스트 가능 (assistant.ts 의 board-import → @aws-sdk transitive 경로 회피)\n * 2. 관측성 (error boundary / iter log / AbortSignal / repeated-failure 조기 종료)\n * 을 한 곳에 응집\n * 3. 향후 split (Phase 3) 의 자연 경계\n *\n * 입력 — `chat` 함수와 dispatch helper 들을 의존성 주입. board-ai 본체는 본 모듈에\n * 의존하지만 본 모듈은 board-ai 의 어떤 무거운 import 도 모름.\n */\nimport debug from 'debug'\nimport type {\n AIMessage,\n AIToolCall,\n AIToolChatResult\n} from '@things-factory/ai-client-base'\nimport type { BoardModel } from '@things-factory/board-import'\nimport type { BoardActionOp, ToolUsage } from './types'\n\nconst log = debug('things-factory:board-ai:loop')\n\n// ── Public types ─────────────────────────────────────────────────\n\n/**\n * Loop 가 호출하는 dispatch 함수들. 모두 의존성 주입.\n *\n * assistant.ts 가 자기 자신의 함수 (isReadTool, executeReadTool 등) 를 그대로\n * 넘겨주는 구조. 본 모듈은 이 함수들의 *시그니처* 만 알면 됨.\n */\nexport interface ToolDispatchHelpers {\n isReadTool: (name: string) => boolean\n isWriteTool: (name: string) => boolean\n isActionTool: (name: string) => boolean\n executeReadTool: (\n tc: AIToolCall,\n board: BoardModel | undefined,\n selectedRefids: number[]\n ) => any\n validateWriteToolCall: (\n tc: AIToolCall,\n components: any[]\n ) => { valid: boolean; errors?: any[]; suggestion?: string }\n toolCallToBoardActionOp: (tc: AIToolCall) => BoardActionOp | null\n summarizeToolResult: (value: any) => any\n}\n\nexport interface AgenticLoopOptions {\n systemPrompt: string\n model?: string\n temperature?: number\n maxTokens?: number\n /** 최대 iteration. 기본 8. */\n maxIterations?: number\n /**\n * 사용자 abort 신호. iteration 시작 직전 / chat() 호출 후 체크.\n * 이미 abort 된 신호로 들어오면 첫 iteration 도 안 돔.\n */\n signal?: AbortSignal\n /**\n * 같은 tool 의 validation 실패가 N회 연속이면 조기 종료. 기본 3.\n * LLM 이 같은 잘못을 무한 반복하는 경우 방어.\n */\n repeatedFailureLimit?: number\n}\n\nexport interface AgenticLoopInput {\n initialConversation: AIMessage[]\n tools: any[]\n options: AgenticLoopOptions\n /** LLM provider 의 chatWithTools 위임. provider-specific 구현. */\n chat: (\n messages: AIMessage[],\n tools: any[],\n options: any\n ) => Promise<AIToolChatResult>\n dispatch: ToolDispatchHelpers\n currentBoard: BoardModel | undefined\n selectedRefids: number[]\n}\n\nexport interface AgenticLoopResult {\n lastText: string | undefined\n accumulatedWriteCalls: AIToolCall[]\n accumulatedActions: BoardActionOp[]\n toolUsages: ToolUsage[]\n /** LLM 마지막 응답의 stopReason. provider-specific 문자열. */\n stopReason: string\n /**\n * 비정상 종료 사유 — 정상 종료 시 undefined.\n * 호출자 (assistant.ts) 가 사용자 메시지에 반영 (예: '시도 중 오류가 발생했어요').\n */\n abortReason?:\n | { type: 'provider_error'; message: string; iter: number }\n | { type: 'aborted'; iter: number }\n | { type: 'max_iterations'; iter: number }\n | { type: 'repeated_validation_failure'; toolName: string; consecutive: number; iter: number }\n}\n\n// ── Loop ─────────────────────────────────────────────────────────\n\nconst DEFAULT_MAX_ITERATIONS = 8\n\n/**\n * multi-turn agentic loop.\n *\n * LLM 이 read tool 을 호출 → 서버에서 즉시 실행 → 결과 회신 → LLM 추론 반복.\n * write tool 은 누적해 client 에 patch 로 전달. 무한 루프 방지를 위해 iteration cap.\n *\n * 본 함수는 *순수* — input 만 보고 결정. 외부 mutable state 없음.\n */\nexport async function runAgenticLoop(\n input: AgenticLoopInput\n): Promise<AgenticLoopResult> {\n const {\n initialConversation,\n tools,\n options,\n chat,\n dispatch,\n currentBoard,\n selectedRefids\n } = input\n\n const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS\n const failureLimit = options.repeatedFailureLimit ?? 3\n const signal = options.signal\n\n let conversation = initialConversation\n const accumulatedWriteCalls: AIToolCall[] = []\n const accumulatedActions: BoardActionOp[] = []\n const toolUsages: ToolUsage[] = []\n let lastText: string | undefined\n let stopReason: string = 'end_turn'\n let abortReason: AgenticLoopResult['abortReason']\n\n // 같은 tool 의 validation 실패 연속 카운터 — 다른 tool 호출 시 reset\n let lastFailedToolName: string | undefined\n let consecutiveFailures = 0\n\n let iter = 0\n for (; iter < maxIterations; iter++) {\n // (1) AbortSignal — iteration 시작 시 체크\n if (signal?.aborted) {\n log('iter %d: aborted by signal', iter)\n abortReason = { type: 'aborted', iter }\n break\n }\n\n log('iter %d: chat() 호출', iter)\n let result: AIToolChatResult\n try {\n // (2) Error boundary — provider 예외 시 graceful 종료\n result = await chat(conversation, tools, {\n systemPrompt: options.systemPrompt,\n model: options.model,\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n toolChoice: 'auto',\n allowParallelToolCalls: true,\n signal\n })\n } catch (e: any) {\n // AbortError — 별도 분류\n if (e?.name === 'AbortError' || signal?.aborted) {\n log('iter %d: provider AbortError', iter)\n abortReason = { type: 'aborted', iter }\n } else {\n log('iter %d: provider error: %s', iter, e?.message)\n abortReason = {\n type: 'provider_error',\n message: e?.message ?? String(e),\n iter\n }\n }\n break\n }\n\n lastText = result.text || lastText\n stopReason = result.stopReason\n\n log(\n 'iter %d: text=%s, toolCalls=%d, stopReason=%s',\n iter,\n result.text ? `\"${result.text.slice(0, 60)}...\"` : '(none)',\n result.toolCalls.length,\n result.stopReason\n )\n\n if (result.toolCalls.length === 0) break\n\n // (1b) 삭제: chat() 직후 abort 는 다음 iter 시작 시 (1) 에서 감지.\n // 현재 iter tool 실행을 중단하면 read tool 결과도 버려져 테스트/동작 불일치.\n\n // tool 결과 빌드 — read 는 즉시 실행, write 는 누적, action 은 별도 채널\n const beforeUsages = toolUsages.length\n const toolResultParts = await processToolCalls(\n result.toolCalls,\n currentBoard,\n selectedRefids,\n dispatch,\n accumulatedWriteCalls,\n accumulatedActions,\n toolUsages\n )\n\n // (4) 같은 tool 의 validation 실패 연속 감지 — LLM 이 같은 잘못 반복 시 abort\n const newUsages = toolUsages.slice(beforeUsages)\n const repeatedFailureAbort = detectRepeatedValidationFailure(\n newUsages,\n lastFailedToolName,\n consecutiveFailures,\n failureLimit\n )\n if (repeatedFailureAbort.shouldAbort) {\n log(\n 'iter %d: repeated validation failure on %s (%d consecutive) — abort',\n iter,\n repeatedFailureAbort.toolName,\n repeatedFailureAbort.consecutive\n )\n abortReason = {\n type: 'repeated_validation_failure',\n toolName: repeatedFailureAbort.toolName,\n consecutive: repeatedFailureAbort.consecutive,\n iter\n }\n break\n }\n lastFailedToolName = repeatedFailureAbort.lastFailedToolName\n consecutiveFailures = repeatedFailureAbort.consecutive\n\n // assistant 의 tool_use turn + user 의 tool_result turn 을 conversation 에 누적\n const assistantContent: any[] = []\n if (result.text) assistantContent.push({ type: 'text', text: result.text })\n for (const tc of result.toolCalls) {\n // providerMeta (Gemini thoughtSignature 등) 를 그대로 round-trip — 안 보내면 400.\n assistantContent.push({\n type: 'tool_use',\n id: tc.id,\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.providerMeta && { providerMeta: tc.providerMeta })\n })\n }\n\n conversation = [\n ...conversation,\n { role: 'assistant', content: assistantContent },\n { role: 'user', content: toolResultParts }\n ]\n }\n\n // (3) max iterations 도달 시 사용자에게 표면화\n if (iter >= maxIterations && !abortReason) {\n log('iter %d: max iterations reached', iter)\n abortReason = { type: 'max_iterations', iter }\n }\n\n return {\n lastText,\n accumulatedWriteCalls,\n accumulatedActions,\n toolUsages,\n stopReason,\n abortReason\n }\n}\n\n/**\n * 같은 tool 의 validation 실패 연속 감지 — pure helper.\n *\n * 같은 tool 이 반복 fail 하면 LLM 이 self-correct 못 하는 상태.\n * 다른 tool 이 fail 하면 시퀀스 새로 시작 (전혀 다른 패턴이라).\n * tool 이 succeed 하면 카운터 reset.\n */\nfunction detectRepeatedValidationFailure(\n newUsages: ToolUsage[],\n lastFailedToolName: string | undefined,\n consecutiveFailures: number,\n limit: number\n): {\n shouldAbort: boolean\n toolName: string\n consecutive: number\n lastFailedToolName: string | undefined\n} {\n // 이번 iteration 에서 실패한 write tool 들 (validation 거절)\n const failed = newUsages.filter(\n u => u.kind === 'write' && (u.result as any)?.error === 'Tool args failed validation'\n )\n\n if (failed.length === 0) {\n // 실패 없음 — reset\n return {\n shouldAbort: false,\n toolName: '',\n consecutive: 0,\n lastFailedToolName: undefined\n }\n }\n\n // 모든 실패가 같은 tool 인지 확인\n const firstName = failed[0].name\n const allSame = failed.every(u => u.name === firstName)\n\n if (allSame && firstName === lastFailedToolName) {\n const consecutive = consecutiveFailures + 1\n return {\n shouldAbort: consecutive >= limit,\n toolName: firstName,\n consecutive,\n lastFailedToolName: firstName\n }\n }\n\n // 다른 tool 또는 첫 실패\n return {\n shouldAbort: false,\n toolName: firstName,\n consecutive: 1,\n lastFailedToolName: firstName\n }\n}\n\n/**\n * 한 iteration 의 tool 결과를 분류·실행·누적. (loop 에서 분리해 읽기 쉽게.)\n */\nasync function processToolCalls(\n toolCalls: AIToolCall[],\n currentBoard: BoardModel | undefined,\n selectedRefids: number[],\n dispatch: ToolDispatchHelpers,\n accumulatedWriteCalls: AIToolCall[],\n accumulatedActions: BoardActionOp[],\n toolUsages: ToolUsage[]\n): Promise<any[]> {\n const toolResultParts: any[] = []\n\n for (const tc of toolCalls) {\n if (dispatch.isReadTool(tc.name)) {\n const value = dispatch.executeReadTool(tc, currentBoard, selectedRefids)\n toolResultParts.push({\n type: 'tool_result',\n toolUseId: tc.id,\n content: JSON.stringify(value)\n })\n toolUsages.push({\n name: tc.name,\n arguments: tc.arguments ?? {},\n result: dispatch.summarizeToolResult(value),\n kind: 'read'\n })\n } else if (dispatch.isWriteTool(tc.name)) {\n // 사전 검증 — args 가 schema / build 함수 검증 통과해야 누적.\n // 실패 시 LLM 한테 detailed error 반환 → 다음 turn 에서 자기 회복 (retry).\n const components = (currentBoard?.components ?? []) as any[]\n const validation = dispatch.validateWriteToolCall(tc, components)\n if (!validation.valid) {\n const errorResult = {\n error: 'Tool args failed validation',\n issues: validation.errors,\n suggestion: validation.suggestion,\n note: 'Fix the args and call the tool again.'\n }\n toolResultParts.push({\n type: 'tool_result',\n toolUseId: tc.id,\n content: JSON.stringify(errorResult)\n })\n toolUsages.push({\n name: tc.name,\n arguments: tc.arguments ?? {},\n result: errorResult,\n kind: 'write'\n })\n // continue — 누적 안 함. LLM 이 다음 turn 에서 정정 호출.\n } else {\n accumulatedWriteCalls.push(tc)\n const queuedResult = { queued: true, note: 'Will be applied on the client.' }\n toolResultParts.push({\n type: 'tool_result',\n toolUseId: tc.id,\n content: JSON.stringify(queuedResult)\n })\n toolUsages.push({\n name: tc.name,\n arguments: tc.arguments ?? {},\n result: queuedResult,\n kind: 'write'\n })\n }\n } else if (dispatch.isActionTool(tc.name)) {\n // ephemeral scene 조작 — accumulatedActions 로 누적, ChatResponse.actions 로 client 전달\n const action = dispatch.toolCallToBoardActionOp(tc)\n if (action) accumulatedActions.push(action)\n const queuedResult = {\n queued: true,\n note: 'Will be executed on the modeller (ephemeral).'\n }\n toolResultParts.push({\n type: 'tool_result',\n toolUseId: tc.id,\n content: JSON.stringify(queuedResult)\n })\n toolUsages.push({\n name: tc.name,\n arguments: tc.arguments ?? {},\n result: queuedResult,\n kind: 'write'\n })\n } else {\n const errResult = { error: `Unknown tool: ${tc.name}` }\n toolResultParts.push({\n type: 'tool_result',\n toolUseId: tc.id,\n content: JSON.stringify(errResult),\n isError: true\n })\n toolUsages.push({\n name: tc.name,\n arguments: tc.arguments ?? {},\n result: errResult,\n kind: 'unknown'\n })\n }\n }\n\n return toolResultParts\n}\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DefaultBoardAIAssistant — base AIClient 위에 LLM 프롬프트로 chat() 구현.
|
|
3
|
+
*/
|
|
4
|
+
import type { AIClient, AITool, AIToolCall, AIToolChatResult } from '@things-factory/ai-client-base';
|
|
5
|
+
import { type BoardModel, type ComponentCategory } from '@things-factory/board-import';
|
|
6
|
+
import type { BoardAIAssistant, BoardEditOp, ChatOptions, ChatResponse, LLMMessage } from './types.js';
|
|
7
|
+
import { type AgenticLoopResult } from './agentic-loop';
|
|
8
|
+
export interface BoardAIAssistantOptions {
|
|
9
|
+
/** 기본 scope (chat 호출 시 옵션이 없으면 사용) */
|
|
10
|
+
scopes?: string[];
|
|
11
|
+
knownTypes?: string[];
|
|
12
|
+
categories?: ComponentCategory[];
|
|
13
|
+
}
|
|
14
|
+
export declare class DefaultBoardAIAssistant implements BoardAIAssistant {
|
|
15
|
+
private base;
|
|
16
|
+
private options;
|
|
17
|
+
readonly id: string;
|
|
18
|
+
constructor(base: AIClient, options?: BoardAIAssistantOptions);
|
|
19
|
+
chat(messages: LLMMessage[], currentBoard: BoardModel | undefined, options?: ChatOptions): Promise<ChatResponse>;
|
|
20
|
+
private chatViaTools;
|
|
21
|
+
private resolveTypesAndCategories;
|
|
22
|
+
private buildSystemPromptForTools;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 4 op 직교 set 을 4 개의 도구로 정의.
|
|
26
|
+
*
|
|
27
|
+
* 핵심: addComponent 의 `type` 파라미터가 knownTypes 의 enum 으로 강제 →
|
|
28
|
+
* LLM 이 등록 안 된 type 을 만들 수 없음 (provider 단계에서 차단).
|
|
29
|
+
*
|
|
30
|
+
* 좌표/크기 키들은 모두 optional 로 허용 (type 별로 사용 키 다름).
|
|
31
|
+
* type-specific 추가 속성은 additionalProperties 로 허용.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildBoardEditTools(knownTypes: string[]): AITool[];
|
|
34
|
+
/**
|
|
35
|
+
* Read tool 의 서버측 실행기 — currentBoard / selectedRefids 에 대해 즉시 실행해 결과 반환.
|
|
36
|
+
* LLM 의 다음 turn 에 tool_result 로 회신된다.
|
|
37
|
+
*
|
|
38
|
+
* 식별자 정책:
|
|
39
|
+
* - 컴포넌트 targeting 은 항상 refid (universal numeric handle).
|
|
40
|
+
* - id 는 데이터 바인딩 이름이며 unique 가 아니다 — filter 용도로만 사용 (findComponents).
|
|
41
|
+
*/
|
|
42
|
+
export declare function executeReadTool(call: AIToolCall, currentBoard: BoardModel | null | undefined, selectedRefids: number[]): any;
|
|
43
|
+
export declare function isReadTool(name: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* 등록된 색 팔레트 이름 목록 — catalog-resolver / 클라이언트 popup 데이터 source.
|
|
46
|
+
* 호스트 패키지가 추가 팔레트 등록은 현재 미지원 (향후 별도 registry 패턴 필요).
|
|
47
|
+
*/
|
|
48
|
+
export declare function listColorPaletteNames(): string[];
|
|
49
|
+
/**
|
|
50
|
+
* 등록된 레이아웃 템플릿 이름 목록 — 위와 동일.
|
|
51
|
+
*/
|
|
52
|
+
export declare function listLayoutTemplateNames(): string[];
|
|
53
|
+
/**
|
|
54
|
+
* agentic loop 의 비정상 종료 사유 → 사용자 메시지에 부기할 한국어 한 줄.
|
|
55
|
+
*
|
|
56
|
+
* 정상 종료 (abortReason undefined) 면 빈 문자열 — concat 해도 무해.
|
|
57
|
+
* 사용자에게 "왜 중간에 멈췄는지" 단서 제공이 목적 — 침묵 종료 금지.
|
|
58
|
+
*/
|
|
59
|
+
export declare function formatAbortNotice(abortReason: AgenticLoopResult['abortReason']): string;
|
|
60
|
+
/**
|
|
61
|
+
* tool 결과를 UI fold-able 박스에 보여줄 만한 크기로 요약.
|
|
62
|
+
*
|
|
63
|
+
* 원본 그대로면 findComponents 50개 / getAtmosphereGuide 같은 큰 객체가 ChatMessage
|
|
64
|
+
* 영속까지 부담 — 핵심 모양만 유지하고 나머지는 marker.
|
|
65
|
+
*/
|
|
66
|
+
export declare function summarizeToolResult(value: any, depth?: number): any;
|
|
67
|
+
export declare function isWriteTool(name: string): boolean;
|
|
68
|
+
export declare function isActionTool(name: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Tool call → BoardActionOp 변환 (C-1 ephemeral scene 조작).
|
|
71
|
+
* 잘못된 인자는 null — agentic loop 가 Unknown tool 비슷하게 처리.
|
|
72
|
+
*/
|
|
73
|
+
export declare function toolCallToBoardActionOp(tc: AIToolCall): import('./types.js').BoardActionOp | null;
|
|
74
|
+
/**
|
|
75
|
+
* AIToolChatResult → ChatResponse.
|
|
76
|
+
*
|
|
77
|
+
* 정책 — followUp 항상 undefined: reply 와 동일 텍스트를 followUp 에 복제하면 클라이언트
|
|
78
|
+
* 에서 .md-body 와 .followup 두 박스에 같은 내용이 표시되어 사용자에게 "동일 응답이
|
|
79
|
+
* 두 번 반복" 으로 보인다. 명확화 질문이 필요한 경우 LLM 이 reply 자체에 포함시키도록
|
|
80
|
+
* 설계.
|
|
81
|
+
*/
|
|
82
|
+
export declare function toolResultToChatResponse(result: AIToolChatResult): ChatResponse;
|
|
83
|
+
export declare function toolCallToBoardEditOp(tc: AIToolCall, components?: ReadonlyArray<any>): BoardEditOp | BoardEditOp[] | null;
|
|
84
|
+
export declare function summarizeBoard(board: BoardModel | null | undefined): any;
|