@gendive/chatllm 0.17.37 → 0.17.39

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.
@@ -983,7 +983,7 @@ ${skillDescriptions}
983
983
  **\uBC18\uBCF5: \uD574\uB2F9 \uB3C4\uAD6C\uAC00 \uC788\uB2E4\uBA74, <skill_use> \uD0DC\uADF8 \uC5C6\uC774 \uC808\uB300 \uC751\uB2F5\uD558\uC9C0 \uB9C8\uC138\uC694.**`;
984
984
  }, [resolvedSkills]);
985
985
  const handleSkillCall = useCallback3(
986
- async (content, callbacks) => {
986
+ async (content, callbacks, extraParams) => {
987
987
  const { skillCall, cleanContent } = parseSkillCallFromContent(content);
988
988
  if (!skillCall) {
989
989
  return { skillCall: null, cleanContent: content, result: null };
@@ -993,9 +993,10 @@ ${skillDescriptions}
993
993
  console.warn(`[useSkills] \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC740 \uC2A4\uD0AC: ${skillCall.name}`);
994
994
  return { skillCall, cleanContent, result: null };
995
995
  }
996
+ const mergedParams = extraParams ? { ...skillCall.params, ...extraParams } : skillCall.params;
996
997
  setActiveSkillExecution({
997
998
  skillName: skillCall.name,
998
- params: skillCall.params,
999
+ params: mergedParams,
999
1000
  status: "executing"
1000
1001
  });
1001
1002
  const abortController = new AbortController();
@@ -1011,7 +1012,7 @@ ${skillDescriptions}
1011
1012
  onStream: callbacks?.onStream,
1012
1013
  signal: callbacks?.signal || abortController.signal
1013
1014
  };
1014
- const result = await skill.execute(skillCall.params, mergedCallbacks);
1015
+ const result = await skill.execute(mergedParams, mergedCallbacks);
1015
1016
  setActiveSkillExecution(
1016
1017
  (prev) => prev ? { ...prev, status: "done", result } : null
1017
1018
  );
@@ -1712,8 +1713,6 @@ var useChatUI = (options) => {
1712
1713
  onDeleteProject,
1713
1714
  onAddProjectFile,
1714
1715
  onDeleteProjectFile,
1715
- // Image attachment mode
1716
- imageAttachmentMode = "auto-execute",
1717
1716
  // Stream control
1718
1717
  continueAfterToolResult = true,
1719
1718
  onSkillComplete,
@@ -1790,6 +1789,7 @@ var useChatUI = (options) => {
1790
1789
  const skipNextSkillParsingRef = useRef4(false);
1791
1790
  const skipNextChecklistParsingRef = useRef4(false);
1792
1791
  const activeChecklistRef = useRef4(null);
1792
+ const pendingAttachmentDataRef = useRef4(null);
1793
1793
  const lastExtractionMsgCountRef = useRef4(0);
1794
1794
  const memoryOptions = useMemo2(
1795
1795
  () => ({
@@ -2534,20 +2534,12 @@ ${finalContent}`;
2534
2534
  })
2535
2535
  );
2536
2536
  let attachmentResults = [];
2537
- const aiDecideImageAttachments = [];
2538
2537
  if (currentAttachments.length > 0) {
2539
- const autoExecAttachments = imageAttachmentMode === "ai-decide" ? currentAttachments.filter((att) => {
2540
- if (att.type === "image") {
2541
- aiDecideImageAttachments.push(att);
2542
- return false;
2543
- }
2544
- return true;
2545
- }) : currentAttachments;
2546
2538
  const attachmentSkills = Object.entries(resolvedSkills).filter(
2547
2539
  ([, config]) => config.trigger === "attachment"
2548
2540
  );
2549
2541
  for (const [skillName, skillConfig] of attachmentSkills) {
2550
- const matchedFiles = autoExecAttachments.filter((att) => {
2542
+ const matchedFiles = currentAttachments.filter((att) => {
2551
2543
  if (!skillConfig.acceptedTypes || skillConfig.acceptedTypes.length === 0) return true;
2552
2544
  return skillConfig.acceptedTypes.some((type) => {
2553
2545
  if (type.startsWith(".")) return att.name.toLowerCase().endsWith(type.toLowerCase());
@@ -2632,6 +2624,14 @@ ${finalContent}`;
2632
2624
  );
2633
2625
  }
2634
2626
  }
2627
+ const hasImageAttachments = currentAttachments.some((a) => a.type === "image");
2628
+ const hasUserText = finalContent.trim().length > 0;
2629
+ if (hasImageAttachments && hasUserText) {
2630
+ const imageAttachments = currentAttachments.filter((a) => a.type === "image");
2631
+ pendingAttachmentDataRef.current = await convertAttachmentsToBase64(imageAttachments);
2632
+ } else {
2633
+ pendingAttachmentDataRef.current = null;
2634
+ }
2635
2635
  }
2636
2636
  let shouldContinueAfterAttachment = continueAfterToolResult;
2637
2637
  if (attachmentResults.length > 0) {
@@ -2725,48 +2725,37 @@ ${currentContextSummary}` },
2725
2725
  } else {
2726
2726
  chatMessages = messagesToSend.map((m) => ({ role: m.role, content: m.content }));
2727
2727
  }
2728
- if (aiDecideImageAttachments.length > 0) {
2729
- const imageBase64s = await Promise.all(
2730
- aiDecideImageAttachments.map(async (att) => ({
2731
- mimeType: att.mimeType,
2732
- base64: await fileToBase64(att.file)
2733
- }))
2734
- );
2735
- for (let i = chatMessages.length - 1; i >= 0; i--) {
2736
- if (chatMessages[i].role === "user") {
2737
- const textContent = typeof chatMessages[i].content === "string" ? chatMessages[i].content : "";
2738
- const multimodalParts = [];
2739
- if (textContent.trim()) {
2740
- multimodalParts.push({ type: "text", text: textContent });
2741
- }
2742
- for (const img of imageBase64s) {
2743
- multimodalParts.push({
2744
- type: "image_url",
2745
- image_url: { url: `data:${img.mimeType};base64,${img.base64}` }
2746
- });
2747
- }
2748
- chatMessages[i] = { ...chatMessages[i], content: multimodalParts };
2749
- break;
2750
- }
2751
- }
2752
- }
2753
2728
  if (attachmentResults.length > 0 && shouldContinueAfterAttachment) {
2754
- skipNextSkillParsingRef.current = true;
2755
2729
  const attachmentContext = attachmentResults.filter((part) => part.type === "tool_result").map((part) => `[${part.label || part.toolName} \uACB0\uACFC]
2756
2730
  ${part.result.content}`).join("\n\n");
2757
2731
  if (attachmentContext) {
2758
- chatMessages.push({
2759
- role: "user",
2760
- /** @Todo vibecode - 원본 파일 재요청 방지 + skill_use 태그 사용 금지 명시 */
2761
- content: `\uC0AC\uC6A9\uC790\uAC00 \uCCA8\uBD80\uD55C \uD30C\uC77C\uC758 \uBD84\uC11D \uACB0\uACFC\uC785\uB2C8\uB2E4. \uC6D0\uBCF8 \uD30C\uC77C \uB0B4\uC6A9\uC774 \uC774\uBBF8 \uC544\uB798\uC5D0 \uD3EC\uD568\uB418\uC5B4 \uC788\uC73C\uBBC0\uB85C \uC6D0\uBCF8 \uD30C\uC77C\uC744 \uB2E4\uC2DC \uC694\uCCAD\uD558\uC9C0 \uB9C8\uC138\uC694.
2732
+ const hasImageWithText = pendingAttachmentDataRef.current !== null;
2733
+ if (hasImageWithText) {
2734
+ chatMessages.push({
2735
+ role: "user",
2736
+ /** @Todo vibecode - 분석 결과 제공 + 도구 선택 허용 (편집/생성 등) */
2737
+ content: `\uC0AC\uC6A9\uC790\uAC00 \uC774\uBBF8\uC9C0\uB97C \uCCA8\uBD80\uD588\uC2B5\uB2C8\uB2E4. \uC544\uB798\uB294 \uC774\uBBF8\uC9C0 \uC790\uB3D9 \uBD84\uC11D \uACB0\uACFC\uC785\uB2C8\uB2E4.
2738
+
2739
+ ${attachmentContext}
2740
+
2741
+ \uC0AC\uC6A9\uC790\uC758 \uC694\uCCAD: "${finalContent}"
2742
+
2743
+ \uC704 \uBD84\uC11D \uACB0\uACFC\uB97C \uCC38\uACE0\uD558\uB418, \uC0AC\uC6A9\uC790\uC758 \uC694\uCCAD \uC758\uB3C4\uC5D0 \uB530\uB77C \uC801\uC808\uD55C \uB3C4\uAD6C\uB97C \uC120\uD0DD\uD558\uC138\uC694. \uC774\uBBF8\uC9C0 \uD3B8\uC9D1\uC774\uB098 \uC0DD\uC131\uC774 \uD544\uC694\uD558\uBA74 \uD574\uB2F9 \uB3C4\uAD6C\uB97C \uD638\uCD9C\uD558\uACE0, \uBD84\uC11D \uACB0\uACFC\uB9CC\uC73C\uB85C \uCDA9\uBD84\uD558\uBA74 \uBC14\uB85C \uB2F5\uBCC0\uD558\uC138\uC694.`
2744
+ });
2745
+ } else {
2746
+ skipNextSkillParsingRef.current = true;
2747
+ chatMessages.push({
2748
+ role: "user",
2749
+ content: `\uC0AC\uC6A9\uC790\uAC00 \uCCA8\uBD80\uD55C \uD30C\uC77C\uC758 \uBD84\uC11D \uACB0\uACFC\uC785\uB2C8\uB2E4. \uC6D0\uBCF8 \uD30C\uC77C \uB0B4\uC6A9\uC774 \uC774\uBBF8 \uC544\uB798\uC5D0 \uD3EC\uD568\uB418\uC5B4 \uC788\uC73C\uBBC0\uB85C \uC6D0\uBCF8 \uD30C\uC77C\uC744 \uB2E4\uC2DC \uC694\uCCAD\uD558\uC9C0 \uB9C8\uC138\uC694.
2762
2750
 
2763
2751
  ${attachmentContext}
2764
2752
 
2765
2753
  \uC704 \uBD84\uC11D \uACB0\uACFC\uB9CC\uC73C\uB85C \uC0AC\uC6A9\uC790\uC758 \uC9C8\uBB38\uC5D0 \uB2F5\uBCC0\uD574\uC8FC\uC138\uC694. skill_use \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`
2766
- });
2754
+ });
2755
+ }
2767
2756
  }
2768
2757
  }
2769
- console.log("[ChatUI] Messages to send:", chatMessages.length, chatMessages.map((m) => ({ role: m.role, content: typeof m.content === "string" ? m.content.slice(0, 50) : "[multimodal]" })));
2758
+ console.log("[ChatUI] Messages to send:", chatMessages.length, chatMessages.map((m) => ({ role: m.role, content: m.content.slice(0, 50) })));
2770
2759
  const baseSystemPrompt = buildSystemPrompt();
2771
2760
  const combinedSystemPrompt = [baseSystemPrompt, actionPrompt].filter(Boolean).join("\n\n");
2772
2761
  const messagesForApi = combinedSystemPrompt ? [{ role: "system", content: combinedSystemPrompt }, ...chatMessages] : chatMessages;
@@ -2984,9 +2973,11 @@ ${attachmentContext}
2984
2973
  })
2985
2974
  );
2986
2975
  try {
2976
+ const attachExtra = pendingAttachmentDataRef.current ? { __attachedImages__: pendingAttachmentDataRef.current } : void 0;
2987
2977
  const { result: skillResult } = await handleSkillCall(assistantContent, {
2988
2978
  signal: abortControllerRef.current?.signal
2989
- });
2979
+ }, attachExtra);
2980
+ pendingAttachmentDataRef.current = null;
2990
2981
  if (skillResult?.content) {
2991
2982
  accumulatedContent = skillResult.content;
2992
2983
  }
@@ -3015,6 +3006,8 @@ ${attachmentContext}
3015
3006
  })
3016
3007
  );
3017
3008
  let streamedReport = "";
3009
+ const attachExtraParams = pendingAttachmentDataRef.current ? { __attachedImages__: pendingAttachmentDataRef.current } : void 0;
3010
+ pendingAttachmentDataRef.current = null;
3018
3011
  const { result } = await handleSkillCall(assistantContent, {
3019
3012
  onProgress: (progress) => {
3020
3013
  setSessions(
@@ -3049,7 +3042,7 @@ ${attachmentContext}
3049
3042
  );
3050
3043
  },
3051
3044
  signal: abortControllerRef.current?.signal
3052
- });
3045
+ }, attachExtraParams);
3053
3046
  if (result) {
3054
3047
  if (result.metadata?.__toolResult__) {
3055
3048
  const resultType = result.metadata.resultType;
@@ -12340,7 +12333,6 @@ var ChatUIWithHook = ({
12340
12333
  skills,
12341
12334
  tools,
12342
12335
  onToolCall,
12343
- imageAttachmentMode,
12344
12336
  continueAfterToolResult,
12345
12337
  onSkillComplete,
12346
12338
  onLoadModels,
@@ -12383,7 +12375,6 @@ var ChatUIWithHook = ({
12383
12375
  skills,
12384
12376
  tools,
12385
12377
  onToolCall,
12386
- imageAttachmentMode,
12387
12378
  continueAfterToolResult,
12388
12379
  onSkillComplete,
12389
12380
  onLoadModels,