@gendive/chatllm 0.17.36 → 0.17.38

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.
@@ -1614,7 +1614,9 @@ interface UseSkillsReturn {
1614
1614
  * @Todo vibecode - callbacks로 진행 상태/스트리밍/중단 시그널 전달
1615
1615
  * @returns skillCall이 있으면 실행하고 결과 반환, 없으면 null
1616
1616
  */
1617
- handleSkillCall: (content: string, callbacks?: SkillExecuteCallbacks) => Promise<{
1617
+ handleSkillCall: (content: string, callbacks?: SkillExecuteCallbacks,
1618
+ /** @Todo vibecode - 스킬 params에 병합할 추가 데이터 (예: 첨부 이미지) */
1619
+ extraParams?: Record<string, unknown>) => Promise<{
1618
1620
  skillCall: ParsedSkillCall | null;
1619
1621
  cleanContent: string;
1620
1622
  result: SkillExecutionResult | null;
@@ -1614,7 +1614,9 @@ interface UseSkillsReturn {
1614
1614
  * @Todo vibecode - callbacks로 진행 상태/스트리밍/중단 시그널 전달
1615
1615
  * @returns skillCall이 있으면 실행하고 결과 반환, 없으면 null
1616
1616
  */
1617
- handleSkillCall: (content: string, callbacks?: SkillExecuteCallbacks) => Promise<{
1617
+ handleSkillCall: (content: string, callbacks?: SkillExecuteCallbacks,
1618
+ /** @Todo vibecode - 스킬 params에 병합할 추가 데이터 (예: 첨부 이미지) */
1619
+ extraParams?: Record<string, unknown>) => Promise<{
1618
1620
  skillCall: ParsedSkillCall | null;
1619
1621
  cleanContent: string;
1620
1622
  result: SkillExecutionResult | null;
@@ -1052,7 +1052,7 @@ ${skillDescriptions}
1052
1052
  **\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.**`;
1053
1053
  }, [resolvedSkills]);
1054
1054
  const handleSkillCall = (0, import_react3.useCallback)(
1055
- async (content, callbacks) => {
1055
+ async (content, callbacks, extraParams) => {
1056
1056
  const { skillCall, cleanContent } = parseSkillCallFromContent(content);
1057
1057
  if (!skillCall) {
1058
1058
  return { skillCall: null, cleanContent: content, result: null };
@@ -1062,9 +1062,10 @@ ${skillDescriptions}
1062
1062
  console.warn(`[useSkills] \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC740 \uC2A4\uD0AC: ${skillCall.name}`);
1063
1063
  return { skillCall, cleanContent, result: null };
1064
1064
  }
1065
+ const mergedParams = extraParams ? { ...skillCall.params, ...extraParams } : skillCall.params;
1065
1066
  setActiveSkillExecution({
1066
1067
  skillName: skillCall.name,
1067
- params: skillCall.params,
1068
+ params: mergedParams,
1068
1069
  status: "executing"
1069
1070
  });
1070
1071
  const abortController = new AbortController();
@@ -1080,7 +1081,7 @@ ${skillDescriptions}
1080
1081
  onStream: callbacks?.onStream,
1081
1082
  signal: callbacks?.signal || abortController.signal
1082
1083
  };
1083
- const result = await skill.execute(skillCall.params, mergedCallbacks);
1084
+ const result = await skill.execute(mergedParams, mergedCallbacks);
1084
1085
  setActiveSkillExecution(
1085
1086
  (prev) => prev ? { ...prev, status: "done", result } : null
1086
1087
  );
@@ -1857,6 +1858,7 @@ var useChatUI = (options) => {
1857
1858
  const skipNextSkillParsingRef = (0, import_react5.useRef)(false);
1858
1859
  const skipNextChecklistParsingRef = (0, import_react5.useRef)(false);
1859
1860
  const activeChecklistRef = (0, import_react5.useRef)(null);
1861
+ const pendingAttachmentDataRef = (0, import_react5.useRef)(null);
1860
1862
  const lastExtractionMsgCountRef = (0, import_react5.useRef)(0);
1861
1863
  const memoryOptions = (0, import_react5.useMemo)(
1862
1864
  () => ({
@@ -2691,6 +2693,14 @@ ${finalContent}`;
2691
2693
  );
2692
2694
  }
2693
2695
  }
2696
+ const hasImageAttachments = currentAttachments.some((a) => a.type === "image");
2697
+ const hasUserText = finalContent.trim().length > 0;
2698
+ if (hasImageAttachments && hasUserText) {
2699
+ const imageAttachments = currentAttachments.filter((a) => a.type === "image");
2700
+ pendingAttachmentDataRef.current = await convertAttachmentsToBase64(imageAttachments);
2701
+ } else {
2702
+ pendingAttachmentDataRef.current = null;
2703
+ }
2694
2704
  }
2695
2705
  let shouldContinueAfterAttachment = continueAfterToolResult;
2696
2706
  if (attachmentResults.length > 0) {
@@ -2785,19 +2795,33 @@ ${currentContextSummary}` },
2785
2795
  chatMessages = messagesToSend.map((m) => ({ role: m.role, content: m.content }));
2786
2796
  }
2787
2797
  if (attachmentResults.length > 0 && shouldContinueAfterAttachment) {
2788
- skipNextSkillParsingRef.current = true;
2789
2798
  const attachmentContext = attachmentResults.filter((part) => part.type === "tool_result").map((part) => `[${part.label || part.toolName} \uACB0\uACFC]
2790
2799
  ${part.result.content}`).join("\n\n");
2791
2800
  if (attachmentContext) {
2792
- chatMessages.push({
2793
- role: "user",
2794
- /** @Todo vibecode - 원본 파일 재요청 방지 + skill_use 태그 사용 금지 명시 */
2795
- 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.
2801
+ const hasImageWithText = pendingAttachmentDataRef.current !== null;
2802
+ if (hasImageWithText) {
2803
+ chatMessages.push({
2804
+ role: "user",
2805
+ /** @Todo vibecode - 분석 결과 제공 + 도구 선택 허용 (편집/생성 등) */
2806
+ 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.
2807
+
2808
+ ${attachmentContext}
2809
+
2810
+ \uC0AC\uC6A9\uC790\uC758 \uC694\uCCAD: "${finalContent}"
2811
+
2812
+ \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.`
2813
+ });
2814
+ } else {
2815
+ skipNextSkillParsingRef.current = true;
2816
+ chatMessages.push({
2817
+ role: "user",
2818
+ 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.
2796
2819
 
2797
2820
  ${attachmentContext}
2798
2821
 
2799
2822
  \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.`
2800
- });
2823
+ });
2824
+ }
2801
2825
  }
2802
2826
  }
2803
2827
  console.log("[ChatUI] Messages to send:", chatMessages.length, chatMessages.map((m) => ({ role: m.role, content: m.content.slice(0, 50) })));
@@ -3018,9 +3042,11 @@ ${attachmentContext}
3018
3042
  })
3019
3043
  );
3020
3044
  try {
3045
+ const attachExtra = pendingAttachmentDataRef.current ? { __attachedImages__: pendingAttachmentDataRef.current } : void 0;
3021
3046
  const { result: skillResult } = await handleSkillCall(assistantContent, {
3022
3047
  signal: abortControllerRef.current?.signal
3023
- });
3048
+ }, attachExtra);
3049
+ pendingAttachmentDataRef.current = null;
3024
3050
  if (skillResult?.content) {
3025
3051
  accumulatedContent = skillResult.content;
3026
3052
  }
@@ -3049,6 +3075,8 @@ ${attachmentContext}
3049
3075
  })
3050
3076
  );
3051
3077
  let streamedReport = "";
3078
+ const attachExtraParams = pendingAttachmentDataRef.current ? { __attachedImages__: pendingAttachmentDataRef.current } : void 0;
3079
+ pendingAttachmentDataRef.current = null;
3052
3080
  const { result } = await handleSkillCall(assistantContent, {
3053
3081
  onProgress: (progress) => {
3054
3082
  setSessions(
@@ -3083,7 +3111,7 @@ ${attachmentContext}
3083
3111
  );
3084
3112
  },
3085
3113
  signal: abortControllerRef.current?.signal
3086
- });
3114
+ }, attachExtraParams);
3087
3115
  if (result) {
3088
3116
  if (result.metadata?.__toolResult__) {
3089
3117
  const resultType = result.metadata.resultType;