@veltra/ai 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/chat/index.d.ts +5 -0
  2. package/dist/chat/transports/index.d.ts +2 -0
  3. package/dist/chat/transports/openai.d.ts +23 -0
  4. package/dist/chat/transports/openai.js +176 -0
  5. package/dist/chat/transports/openai.js.map +1 -0
  6. package/dist/chat/types.d.ts +125 -0
  7. package/dist/chat/use-chat.d.ts +25 -0
  8. package/dist/chat/use-chat.js +214 -0
  9. package/dist/chat/use-chat.js.map +1 -0
  10. package/dist/components/ai-chat/ai-chat.js +88 -0
  11. package/dist/components/ai-chat/ai-chat.js.map +1 -0
  12. package/dist/components/ai-chat/ai-chat.vue.d.ts +36 -0
  13. package/dist/components/ai-chat/ask-question.js +187 -0
  14. package/dist/components/ai-chat/ask-question.js.map +1 -0
  15. package/dist/components/ai-chat/chat-input.js +143 -0
  16. package/dist/components/ai-chat/chat-input.js.map +1 -0
  17. package/dist/components/ai-chat/di.js +6 -0
  18. package/dist/components/ai-chat/di.js.map +1 -0
  19. package/dist/components/ai-chat/index.d.ts +2 -0
  20. package/dist/components/ai-chat/message-item.js +120 -0
  21. package/dist/components/ai-chat/message-item.js.map +1 -0
  22. package/dist/components/ai-chat/message-list.js +75 -0
  23. package/dist/components/ai-chat/message-list.js.map +1 -0
  24. package/dist/components/ai-chat/style.d.ts +5 -0
  25. package/dist/components/ai-chat/style.js +6 -0
  26. package/dist/components/ai-chat/style2.css +416 -0
  27. package/dist/components/ai-chat/tool-call.js +151 -0
  28. package/dist/components/ai-chat/tool-call.js.map +1 -0
  29. package/dist/components/index.d.ts +3 -0
  30. package/dist/index.d.ts +11 -0
  31. package/dist/index.js +5 -0
  32. package/dist/style.d.ts +1 -0
  33. package/dist/style.js +1 -0
  34. package/dist/tools/ask-question/ask-question.d.ts +45 -0
  35. package/dist/tools/ask-question/ask-question.js +65 -0
  36. package/dist/tools/ask-question/ask-question.js.map +1 -0
  37. package/dist/tools/ask-question/deferred.js +22 -0
  38. package/dist/tools/ask-question/deferred.js.map +1 -0
  39. package/dist/tools/ask-question/index.d.ts +2 -0
  40. package/dist/tools/index.d.ts +3 -0
  41. package/dist/types/ai-chat.d.ts +49 -0
  42. package/dist/types/index.d.ts +2 -0
  43. package/package.json +40 -0
  44. package/src/chat/__test__/use-chat.test.ts +246 -0
  45. package/src/chat/index.ts +3 -0
  46. package/src/chat/transports/index.ts +1 -0
  47. package/src/chat/transports/openai.ts +237 -0
  48. package/src/chat/types.ts +136 -0
  49. package/src/chat/use-chat.ts +245 -0
  50. package/src/components/ai-chat/__test__/ai-chat.test.ts +316 -0
  51. package/src/components/ai-chat/ai-chat.vue +67 -0
  52. package/src/components/ai-chat/ask-question.vue +232 -0
  53. package/src/components/ai-chat/chat-input.vue +127 -0
  54. package/src/components/ai-chat/di.ts +15 -0
  55. package/src/components/ai-chat/index.ts +1 -0
  56. package/src/components/ai-chat/message-item.vue +120 -0
  57. package/src/components/ai-chat/message-list.vue +81 -0
  58. package/src/components/ai-chat/style.scss +439 -0
  59. package/src/components/ai-chat/style.ts +6 -0
  60. package/src/components/ai-chat/tool-call.vue +147 -0
  61. package/src/components/index.ts +1 -0
  62. package/src/env.d.ts +12 -0
  63. package/src/index.ts +4 -0
  64. package/src/style.ts +1 -0
  65. package/src/tools/ask-question/__test__/ask-question.test.ts +198 -0
  66. package/src/tools/ask-question/ask-question.ts +114 -0
  67. package/src/tools/ask-question/deferred.ts +27 -0
  68. package/src/tools/ask-question/index.ts +1 -0
  69. package/src/tools/index.ts +1 -0
  70. package/src/types/ai-chat.ts +56 -0
  71. package/src/types/index.ts +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-item.js","names":[],"sources":["../../../src/components/ai-chat/message-item.vue"],"sourcesContent":["<template>\n <div\n :class=\"[cls.e('message'), cls.em('message', message.role), bem.is('streaming', isStreaming)]\"\n >\n <!-- 用户消息:右对齐气泡 -->\n <template v-if=\"message.role === 'user'\">\n <div :class=\"cls.e('message-bubble')\">\n <div v-if=\"message.attachments?.length\" :class=\"cls.e('message-attachments')\">\n <img\n v-for=\"att in message.attachments\"\n :key=\"att.dataUrl\"\n :src=\"att.dataUrl\"\n :alt=\"att.name\"\n :class=\"cls.e('message-attachment')\"\n />\n </div>\n <div v-if=\"message.content\" :class=\"cls.e('message-text')\">{{ message.content }}</div>\n </div>\n </template>\n\n <!-- assistant 消息 -->\n <template v-else>\n <div\n v-if=\"message.reasoning\"\n :class=\"[cls.e('reasoning'), bem.is('expanded', reasoningExpanded)]\"\n >\n <div :class=\"cls.e('reasoning-header')\" @click=\"reasoningExpanded = !reasoningExpanded\">\n <UIcon :class=\"[cls.e('reasoning-icon'), isThinking && cls.is('loading')]\">\n <Loading v-if=\"isThinking\" />\n <InfoCircle v-else />\n </UIcon>\n <span>{{ isThinking ? '思考中…' : '思考过程' }}</span>\n <UIcon :class=\"cls.e('reasoning-chevron')\"><ArrowDown /></UIcon>\n </div>\n <div v-show=\"reasoningExpanded\" :class=\"cls.e('reasoning-content')\">\n {{ message.reasoning }}\n </div>\n </div>\n\n <MarkdownRender\n v-if=\"message.content || isStreaming\"\n mode=\"chat\"\n :class=\"cls.e('message-content')\"\n :content=\"message.content\"\n :final=\"!isStreaming\"\n :fade=\"false\"\n v-bind=\"rendererProps\"\n />\n\n <ToolCallCard\n v-for=\"call in message.toolCalls ?? []\"\n :key=\"call.id\"\n :tool-call=\"call\"\n @respond=\"(approved) => emit('respond', call.id, approved)\"\n />\n\n <div v-if=\"message.status === 'error'\" :class=\"cls.e('message-status')\">生成出错</div>\n <div v-else-if=\"message.status === 'aborted'\" :class=\"cls.e('message-status')\">\n 已停止生成\n </div>\n\n <div v-if=\"showActions\" :class=\"cls.e('message-actions')\" @click=\"emit('regenerate')\">\n <UIcon title=\"重新生成\"><Refresh /></UIcon>\n </div>\n </template>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UIcon } from '@veltra/desktop'\nimport { ArrowDown, InfoCircle, Loading, Refresh } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport MarkdownRender from 'markstream-vue'\nimport { computed, inject, ref, watch } from 'vue'\n\nimport type { ChatMessage } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport ToolCallCard from './tool-call.vue'\n\ndefineOptions({ name: 'UAiChatMessageItem' })\n\nconst props = defineProps<{\n message: ChatMessage\n /** 是否为最后一条可见消息(决定是否显示重新生成) */\n isLast: boolean\n /** 透传给 MarkdownRender 的属性 */\n rendererProps?: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{\n (e: 'respond', toolCallId: string, approved: boolean): void\n (e: 'regenerate'): void\n}>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst isStreaming = computed(() => props.message.status === 'streaming')\n/** 思考中:流式且正文还未开始输出 */\nconst isThinking = computed(() => isStreaming.value && !props.message.content)\n\nconst reasoningExpanded = ref(true)\n\n// 流式结束后自动折叠思考过程\nwatch(\n () => props.message.status,\n (status) => {\n if (status === 'streaming') {\n reasoningExpanded.value = true\n } else if (status) {\n reasoningExpanded.value = false\n }\n }\n)\n\nconst showActions = computed(() => {\n if (props.message.role !== 'assistant' || !props.isLast) return false\n return ['done', 'error', 'aborted'].includes(props.message.status ?? '')\n})\n</script>\n"],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { AiChatDIKey } from "./di.js";
2
+ import _sfc_main$1 from "./message-item.js";
3
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createTextVNode, defineComponent, inject, nextTick, normalizeClass, openBlock, ref, renderList, renderSlot, shallowRef, toDisplayString, unref, watch, withCtx } from "vue";
4
+ import { bem } from "@veltra/utils";
5
+ import { UScroll } from "@veltra/desktop";
6
+ //#region src/components/ai-chat/message-list.vue
7
+ const _sfc_main = /*@__PURE__*/ defineComponent({
8
+ name: "UAiChatMessageList",
9
+ __name: "message-list",
10
+ props: {
11
+ messages: {},
12
+ welcome: {},
13
+ rendererProps: {}
14
+ },
15
+ emits: ["respond", "regenerate"],
16
+ setup(__props, { emit: __emit }) {
17
+ const props = __props;
18
+ const emit = __emit;
19
+ const cls = inject(AiChatDIKey)?.cls ?? bem("ai-chat");
20
+ /** tool 消息不单独渲染,其内容体现在工具卡片的结果区 */
21
+ const visibleMessages = computed(() => props.messages.filter((msg) => msg.role !== "tool"));
22
+ const scrollRef = shallowRef();
23
+ /** 是否吸附底部(用户上翻浏览历史时取消吸附) */
24
+ const stickToBottom = ref(true);
25
+ const handleScroll = (position) => {
26
+ stickToBottom.value = position.sh - position.ch - position.y < 60;
27
+ };
28
+ watch(() => {
29
+ const list = visibleMessages.value;
30
+ const last = list[list.length - 1];
31
+ return [
32
+ list.length,
33
+ last?.content.length ?? 0,
34
+ last?.reasoning?.length ?? 0,
35
+ last?.toolCalls?.length ?? 0
36
+ ].join("|");
37
+ }, async () => {
38
+ if (!stickToBottom.value) return;
39
+ await nextTick();
40
+ scrollRef.value?.scrollTo({ y: Number.MAX_SAFE_INTEGER });
41
+ });
42
+ return (_ctx, _cache) => {
43
+ return openBlock(), createBlock(unref(UScroll), {
44
+ class: normalizeClass(unref(cls).e("list")),
45
+ ref_key: "scrollRef",
46
+ ref: scrollRef,
47
+ always: "",
48
+ onScroll: handleScroll
49
+ }, {
50
+ default: withCtx(() => [!visibleMessages.value.length ? (openBlock(), createElementBlock("div", {
51
+ key: 0,
52
+ class: normalizeClass(unref(cls).e("welcome"))
53
+ }, [renderSlot(_ctx.$slots, "welcome", {}, () => [createTextVNode(toDisplayString(__props.welcome), 1)])], 2)) : createCommentVNode("v-if", true), (openBlock(true), createElementBlock(Fragment, null, renderList(visibleMessages.value, (msg, index) => {
54
+ return openBlock(), createBlock(_sfc_main$1, {
55
+ key: msg.id,
56
+ message: msg,
57
+ "is-last": index === visibleMessages.value.length - 1,
58
+ "renderer-props": __props.rendererProps,
59
+ onRespond: _cache[0] || (_cache[0] = (id, approved) => emit("respond", id, approved)),
60
+ onRegenerate: _cache[1] || (_cache[1] = ($event) => emit("regenerate"))
61
+ }, null, 8, [
62
+ "message",
63
+ "is-last",
64
+ "renderer-props"
65
+ ]);
66
+ }), 128))]),
67
+ _: 3
68
+ }, 8, ["class"]);
69
+ };
70
+ }
71
+ });
72
+ //#endregion
73
+ export { _sfc_main as default };
74
+
75
+ //# sourceMappingURL=message-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-list.js","names":[],"sources":["../../../src/components/ai-chat/message-list.vue"],"sourcesContent":["<template>\n <UScroll :class=\"cls.e('list')\" ref=\"scrollRef\" always @scroll=\"handleScroll\">\n <div v-if=\"!visibleMessages.length\" :class=\"cls.e('welcome')\">\n <slot name=\"welcome\">{{ welcome }}</slot>\n </div>\n\n <MessageItem\n v-for=\"(msg, index) in visibleMessages\"\n :key=\"msg.id\"\n :message=\"msg\"\n :is-last=\"index === visibleMessages.length - 1\"\n :renderer-props=\"rendererProps\"\n @respond=\"(id, approved) => emit('respond', id, approved)\"\n @regenerate=\"emit('regenerate')\"\n />\n </UScroll>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UScroll } from '@veltra/desktop'\nimport type { ScrollPosition } from '@veltra/desktop'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, nextTick, ref, shallowRef, watch } from 'vue'\n\nimport type { ChatMessage } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport MessageItem from './message-item.vue'\n\ndefineOptions({ name: 'UAiChatMessageList' })\n\nconst props = defineProps<{\n messages: ChatMessage[]\n /** 空状态欢迎语 */\n welcome?: string\n /** 透传给 MarkdownRender 的属性 */\n rendererProps?: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{\n (e: 'respond', toolCallId: string, approved: boolean): void\n (e: 'regenerate'): void\n}>()\n\ndefineSlots<{\n /** 空状态欢迎区插槽 */\n welcome(): any\n}>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\n/** tool 消息不单独渲染,其内容体现在工具卡片的结果区 */\nconst visibleMessages = computed(() => props.messages.filter((msg) => msg.role !== 'tool'))\n\nconst scrollRef = shallowRef<{ scrollTo: (position: ScrollPosition) => void }>()\n/** 是否吸附底部(用户上翻浏览历史时取消吸附) */\nconst stickToBottom = ref(true)\n\nconst handleScroll = (position: Required<ScrollPosition>) => {\n stickToBottom.value = position.sh - position.ch - position.y < 60\n}\n\n// 消息数量或流式内容变化时,若处于吸附状态则滚动到底部\nwatch(\n () => {\n const list = visibleMessages.value\n const last = list[list.length - 1]\n return [\n list.length,\n last?.content.length ?? 0,\n last?.reasoning?.length ?? 0,\n last?.toolCalls?.length ?? 0\n ].join('|')\n },\n async () => {\n if (!stickToBottom.value) return\n await nextTick()\n scrollRef.value?.scrollTo({ y: Number.MAX_SAFE_INTEGER })\n }\n)\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;EA8BA,MAAM,QAAQ;EAQd,MAAM,OAAO;EAWb,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;;EAGpC,MAAM,kBAAkB,eAAe,MAAM,SAAS,QAAQ,QAAQ,IAAI,SAAS,MAAM,CAAC;EAE1F,MAAM,YAAY,WAA6D;;EAE/E,MAAM,gBAAgB,IAAI,IAAI;EAE9B,MAAM,gBAAgB,aAAuC;GAC3D,cAAc,QAAQ,SAAS,KAAK,SAAS,KAAK,SAAS,IAAI;EACjE;EAGA,YACQ;GACJ,MAAM,OAAO,gBAAgB;GAC7B,MAAM,OAAO,KAAK,KAAK,SAAS;GAChC,OAAO;IACL,KAAK;IACL,MAAM,QAAQ,UAAU;IACxB,MAAM,WAAW,UAAU;IAC3B,MAAM,WAAW,UAAU;GAC7B,CAAC,CAAC,KAAK,GAAG;EACZ,GACA,YAAY;GACV,IAAI,CAAC,cAAc,OAAO;GAC1B,MAAM,SAAS;GACf,UAAU,OAAO,SAAS,EAAE,GAAG,OAAO,iBAAiB,CAAC;EAC1D,CACF;;uBA9EE,YAcU,MAAA,OAAA,GAAA;IAdA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,MAAA,CAAA;aAAc;IAAJ,KAAI;IAAY,QAAA;IAAQ,UAAQ;;2BAGxD,CAAA,CAFM,gBAAA,MAAgB,UAAA,UAAA,GAA5B,mBAEM,OAAA;;KAF+B,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,SAAA,CAAA;QAC/C,WAAyC,KAAA,QAAA,WAAA,CAAA,SAAA,CAAA,gBAAA,gBAAjB,QAAA,OAAO,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,QAAA,IAAA,IAAA,UAAA,IAAA,GAGjC,mBAQE,UAAA,MAAA,WAPuB,gBAAA,QAAf,KAAK,UAAK;yBADpB,YAQE,aAAA;MANC,KAAK,IAAI;MACT,SAAS;MACT,WAAS,UAAU,gBAAA,MAAgB,SAAM;MACzC,kBAAgB,QAAA;MAChB,WAAO,OAAA,OAAA,OAAA,MAAG,IAAI,aAAa,KAAI,WAAY,IAAI,QAAQ;MACvD,cAAU,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,YAAA"}
@@ -0,0 +1,5 @@
1
+ import "@veltra/desktop/components/button/style";
2
+ import "@veltra/desktop/components/icon/style";
3
+ import "@veltra/desktop/components/input/style";
4
+ import "@veltra/desktop/components/scroll/style";
5
+ import "markstream-vue/index.css";
@@ -0,0 +1,6 @@
1
+ import "./style2.css";
2
+ import "@veltra/desktop/components/button/style";
3
+ import "@veltra/desktop/components/icon/style";
4
+ import "@veltra/desktop/components/input/style";
5
+ import "@veltra/desktop/components/scroll/style";
6
+ import "markstream-vue/index.css";
@@ -0,0 +1,416 @@
1
+ @charset "UTF-8";
2
+ /** flex布局 */
3
+ /**
4
+ * 设置css变量
5
+ * 参数1:变量前缀
6
+ * 参数2: 一个由值组成的列表或者由key, value组成的map
7
+ * css-var(height, (
8
+ * large: 40px
9
+ * default: 32px
10
+ * small: 24px
11
+ * ))
12
+ * 生成的css变量:
13
+ * --u-height-large: 40px;
14
+ * --u-height-default: 32px;
15
+ * --u-height-small: 24px;
16
+ */
17
+ /** 暗色:data-theme 与系统偏好,与 UITheme.injectBuiltInThemes 选择器策略一致 */
18
+ .u-ai-chat {
19
+ display: flex;
20
+ flex-direction: column;
21
+ height: 100%;
22
+ min-height: 0;
23
+ font-size: var(--u-font-size-main-default);
24
+ color: var(--u-text-color-main);
25
+ }
26
+ .u-ai-chat__list {
27
+ flex: 1;
28
+ min-height: 0;
29
+ }
30
+ .u-ai-chat__welcome {
31
+ height: 100%;
32
+ display: flex;
33
+ justify-content: center;
34
+ align-items: center;
35
+ flex-wrap: nowrap;
36
+ color: var(--u-text-color-assist);
37
+ padding: var(--u-gap-default);
38
+ text-align: center;
39
+ }
40
+ .u-ai-chat__message {
41
+ padding: var(--u-gap-small) var(--u-gap-default);
42
+ }
43
+ .u-ai-chat__message--user {
44
+ display: flex;
45
+ justify-content: flex-end;
46
+ }
47
+ .u-ai-chat__message-bubble {
48
+ max-width: 80%;
49
+ padding: var(--u-gap-small) var(--u-gap-default);
50
+ border-radius: var(--u-radius-large);
51
+ background-color: var(--u-color-primary-light-9);
52
+ color: var(--u-text-color-main);
53
+ overflow: hidden;
54
+ }
55
+ .u-ai-chat__message-text {
56
+ white-space: pre-wrap;
57
+ word-break: break-word;
58
+ }
59
+ .u-ai-chat__message-attachments {
60
+ display: flex;
61
+ flex-wrap: wrap;
62
+ gap: var(--u-gap-small);
63
+ margin-bottom: var(--u-gap-small);
64
+ }
65
+ .u-ai-chat__message-attachment {
66
+ max-width: 160px;
67
+ max-height: 160px;
68
+ border-radius: var(--u-radius-default);
69
+ display: block;
70
+ }
71
+ .u-ai-chat__message-content {
72
+ line-height: 1.7;
73
+ }
74
+ .u-ai-chat__message-status {
75
+ margin-top: var(--u-gap-small);
76
+ color: var(--u-text-color-assist);
77
+ font-size: var(--u-font-size-main-small);
78
+ }
79
+ .u-ai-chat__message-actions {
80
+ margin-top: var(--u-gap-small);
81
+ color: var(--u-text-color-assist);
82
+ cursor: pointer;
83
+ display: inline-flex;
84
+ }
85
+ .u-ai-chat__message-actions:hover {
86
+ color: var(--u-color-primary);
87
+ }
88
+ .u-ai-chat__reasoning {
89
+ margin-bottom: var(--u-gap-small);
90
+ color: var(--u-text-color-assist);
91
+ }
92
+ .u-ai-chat__reasoning.is-expanded .u-ai-chat__reasoning-chevron {
93
+ transform: rotate(180deg);
94
+ }
95
+ .u-ai-chat__reasoning-header {
96
+ display: flex;
97
+ justify-content: flex-start;
98
+ align-items: center;
99
+ flex-wrap: nowrap;
100
+ gap: var(--u-gap-small);
101
+ cursor: pointer;
102
+ user-select: none;
103
+ font-size: var(--u-font-size-main-small);
104
+ }
105
+ .u-ai-chat__reasoning-chevron {
106
+ transition: transform 0.2s;
107
+ }
108
+ .u-ai-chat__reasoning-content {
109
+ margin-top: var(--u-gap-small);
110
+ padding-left: var(--u-gap-default);
111
+ border-left: var(--u-border-muted);
112
+ white-space: pre-wrap;
113
+ word-break: break-word;
114
+ font-size: var(--u-font-size-main-small);
115
+ line-height: 1.6;
116
+ }
117
+ .u-ai-chat__tool-call {
118
+ margin: var(--u-gap-small) 0;
119
+ }
120
+ .u-ai-chat__tool-call-status {
121
+ flex-shrink: 0;
122
+ }
123
+ .u-ai-chat__tool-call-status.is-loading {
124
+ color: var(--u-color-primary);
125
+ animation: u-ai-chat-rotate 1s linear infinite;
126
+ }
127
+ .u-ai-chat__tool-call-status.is-warning {
128
+ color: var(--u-color-warning);
129
+ }
130
+ .u-ai-chat__tool-call-status.is-success {
131
+ color: var(--u-color-success);
132
+ }
133
+ .u-ai-chat__tool-call-status.is-danger {
134
+ color: var(--u-color-danger);
135
+ }
136
+ .u-ai-chat__tool-call-name {
137
+ font-family: var(--u-font-family-code);
138
+ font-size: var(--u-font-size-main-small);
139
+ color: var(--u-text-color-title);
140
+ flex-shrink: 0;
141
+ }
142
+ .u-ai-chat__tool-call-summary {
143
+ flex: 1;
144
+ min-width: 0;
145
+ overflow: hidden;
146
+ text-overflow: ellipsis;
147
+ white-space: nowrap;
148
+ color: var(--u-text-color-assist);
149
+ font-size: var(--u-font-size-main-small);
150
+ }
151
+ .u-ai-chat__tool-call-confirm {
152
+ flex-shrink: 0;
153
+ display: flex;
154
+ justify-content: flex-start;
155
+ align-items: center;
156
+ flex-wrap: nowrap;
157
+ gap: var(--u-gap-small);
158
+ }
159
+ .u-ai-chat__tool-call-section {
160
+ font-size: var(--u-font-size-main-small);
161
+ color: var(--u-text-color-assist);
162
+ margin: var(--u-gap-small) 0;
163
+ }
164
+ .u-ai-chat__tool-call-code {
165
+ margin: 0;
166
+ padding: var(--u-gap-small);
167
+ border-radius: var(--u-radius-default);
168
+ background-color: var(--u-bg-color-bottom);
169
+ font-family: var(--u-font-family-code);
170
+ font-size: var(--u-font-size-main-small);
171
+ white-space: pre-wrap;
172
+ word-break: break-all;
173
+ max-height: 240px;
174
+ overflow: auto;
175
+ }
176
+ .u-ai-chat__ask-question {
177
+ display: flex;
178
+ flex-direction: column;
179
+ gap: var(--u-gap-small);
180
+ font-size: var(--u-font-size-main-small);
181
+ }
182
+ .u-ai-chat__ask-question-progress {
183
+ display: flex;
184
+ justify-content: space-between;
185
+ align-items: center;
186
+ flex-wrap: nowrap;
187
+ color: var(--u-text-color-assist);
188
+ }
189
+ .u-ai-chat__ask-question-dots {
190
+ display: flex;
191
+ justify-content: flex-start;
192
+ align-items: center;
193
+ flex-wrap: nowrap;
194
+ gap: 6px;
195
+ }
196
+ .u-ai-chat__ask-question-dot {
197
+ width: 6px;
198
+ height: 6px;
199
+ border-radius: 50%;
200
+ background-color: var(--u-text-color-placeholder);
201
+ cursor: pointer;
202
+ transition: background-color 0.2s, box-shadow 0.2s;
203
+ }
204
+ .u-ai-chat__ask-question-dot.is-answered {
205
+ background-color: var(--u-color-primary);
206
+ }
207
+ .u-ai-chat__ask-question-dot.is-current {
208
+ box-shadow: 0 0 0 3px var(--u-color-primary-light-7);
209
+ }
210
+ .u-ai-chat__ask-question-q {
211
+ display: flex;
212
+ justify-content: flex-start;
213
+ align-items: center;
214
+ flex-wrap: nowrap;
215
+ gap: var(--u-gap-small);
216
+ color: var(--u-text-color-title);
217
+ font-weight: 500;
218
+ line-height: 1.5;
219
+ }
220
+ .u-ai-chat__ask-question-no {
221
+ flex-shrink: 0;
222
+ width: 18px;
223
+ height: 18px;
224
+ border-radius: 50%;
225
+ display: flex;
226
+ justify-content: center;
227
+ align-items: center;
228
+ flex-wrap: nowrap;
229
+ background-color: var(--u-color-primary-light-9);
230
+ color: var(--u-color-primary);
231
+ font-size: 11px;
232
+ }
233
+ .u-ai-chat__ask-question-options {
234
+ display: flex;
235
+ flex-direction: column;
236
+ gap: var(--u-gap-small);
237
+ }
238
+ .u-ai-chat__ask-question-opt {
239
+ display: flex;
240
+ justify-content: flex-start;
241
+ align-items: center;
242
+ flex-wrap: nowrap;
243
+ gap: var(--u-gap-small);
244
+ padding: var(--u-gap-small) var(--u-gap-default);
245
+ border: var(--u-border-muted);
246
+ border-radius: var(--u-radius-default);
247
+ background: none;
248
+ cursor: pointer;
249
+ text-align: left;
250
+ color: var(--u-text-color-main);
251
+ font-size: inherit;
252
+ font-family: inherit;
253
+ line-height: 1.5;
254
+ transition: border-color 0.2s, background-color 0.2s;
255
+ }
256
+ .u-ai-chat__ask-question-opt:hover {
257
+ border-color: var(--u-color-primary);
258
+ }
259
+ .u-ai-chat__ask-question-opt.is-selected {
260
+ border-color: var(--u-color-primary);
261
+ background-color: var(--u-color-primary-light-9);
262
+ }
263
+ .u-ai-chat__ask-question-glyph {
264
+ flex-shrink: 0;
265
+ position: relative;
266
+ width: 14px;
267
+ height: 14px;
268
+ border-radius: 50%;
269
+ border: var(--u-border-muted);
270
+ transition: border-color 0.2s;
271
+ }
272
+ .u-ai-chat__ask-question-glyph.is-selected {
273
+ border-color: var(--u-color-primary);
274
+ }
275
+ .u-ai-chat__ask-question-glyph.is-selected::after {
276
+ content: "";
277
+ position: absolute;
278
+ inset: 3px;
279
+ border-radius: 50%;
280
+ background-color: var(--u-color-primary);
281
+ }
282
+ .u-ai-chat__ask-question-actions {
283
+ display: flex;
284
+ justify-content: flex-end;
285
+ align-items: center;
286
+ flex-wrap: nowrap;
287
+ gap: var(--u-gap-small);
288
+ }
289
+ .u-ai-chat__ask-question-result {
290
+ display: flex;
291
+ flex-direction: column;
292
+ gap: 4px;
293
+ }
294
+ .u-ai-chat__ask-question-result-q {
295
+ display: flex;
296
+ justify-content: flex-start;
297
+ align-items: center;
298
+ flex-wrap: nowrap;
299
+ gap: 6px;
300
+ color: var(--u-text-color-assist);
301
+ }
302
+ .u-ai-chat__ask-question-result-no {
303
+ flex-shrink: 0;
304
+ width: 16px;
305
+ height: 16px;
306
+ border-radius: 50%;
307
+ display: flex;
308
+ justify-content: center;
309
+ align-items: center;
310
+ flex-wrap: nowrap;
311
+ background-color: var(--u-color-primary-light-9);
312
+ color: var(--u-color-primary);
313
+ font-size: 11px;
314
+ }
315
+ .u-ai-chat__ask-question-result-a {
316
+ color: var(--u-text-color-main);
317
+ line-height: 1.5;
318
+ }
319
+ .u-ai-chat__ask-question-chip {
320
+ display: inline-block;
321
+ padding: 2px var(--u-gap-small);
322
+ border-radius: var(--u-radius-default);
323
+ background-color: var(--u-color-primary-light-9);
324
+ color: var(--u-color-primary);
325
+ }
326
+ .u-ai-chat__ask-question-status {
327
+ color: var(--u-text-color-assist);
328
+ }
329
+ .u-ai-chat__input-area {
330
+ flex-shrink: 0;
331
+ margin: var(--u-gap-default);
332
+ margin-top: var(--u-gap-small);
333
+ border: var(--u-border-muted);
334
+ border-radius: var(--u-radius-large);
335
+ background-color: var(--u-bg-color-top);
336
+ padding: var(--u-gap-small);
337
+ }
338
+ .u-ai-chat__input-area:focus-within {
339
+ border-color: var(--u-color-primary);
340
+ }
341
+ .u-ai-chat__input {
342
+ width: 100%;
343
+ border: none;
344
+ outline: none;
345
+ resize: none;
346
+ background: transparent;
347
+ color: var(--u-text-color-main);
348
+ font-size: var(--u-font-size-main-default);
349
+ font-family: inherit;
350
+ line-height: 1.6;
351
+ padding: var(--u-gap-small);
352
+ box-sizing: border-box;
353
+ }
354
+ .u-ai-chat__input::placeholder {
355
+ color: var(--u-text-color-placeholder);
356
+ }
357
+ .u-ai-chat__input-toolbar {
358
+ display: flex;
359
+ justify-content: space-between;
360
+ align-items: center;
361
+ flex-wrap: nowrap;
362
+ padding: 0 var(--u-gap-small);
363
+ }
364
+ .u-ai-chat__input-attach {
365
+ color: var(--u-text-color-assist);
366
+ cursor: pointer;
367
+ font-size: 18px;
368
+ }
369
+ .u-ai-chat__input-attach:hover {
370
+ color: var(--u-color-primary);
371
+ }
372
+ .u-ai-chat__input-stop {
373
+ display: block;
374
+ width: 10px;
375
+ height: 10px;
376
+ border-radius: 2px;
377
+ background-color: currentColor;
378
+ }
379
+ .u-ai-chat__input-attachments {
380
+ display: flex;
381
+ flex-wrap: wrap;
382
+ gap: var(--u-gap-small);
383
+ padding: var(--u-gap-small);
384
+ }
385
+ .u-ai-chat__input-attachment {
386
+ position: relative;
387
+ width: 48px;
388
+ height: 48px;
389
+ border-radius: var(--u-radius-default);
390
+ overflow: hidden;
391
+ border: var(--u-border-muted);
392
+ }
393
+ .u-ai-chat__input-attachment img {
394
+ width: 100%;
395
+ height: 100%;
396
+ object-fit: cover;
397
+ display: block;
398
+ }
399
+ .u-ai-chat__input-attachment-remove {
400
+ position: absolute;
401
+ top: 0;
402
+ right: 0;
403
+ background-color: var(--u-shadow-color-a-52);
404
+ color: var(--u-text-color-white);
405
+ cursor: pointer;
406
+ font-size: 12px;
407
+ }
408
+
409
+ @keyframes u-ai-chat-rotate {
410
+ from {
411
+ transform: rotate(0deg);
412
+ }
413
+ to {
414
+ transform: rotate(360deg);
415
+ }
416
+ }
@@ -0,0 +1,151 @@
1
+ import { AiChatDIKey } from "./di.js";
2
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, normalizeClass, openBlock, ref, resolveDynamicComponent, toDisplayString, unref, watch, withCtx, withModifiers } from "vue";
3
+ import { bem } from "@veltra/utils";
4
+ import { UButton, UCollapseItem, UIcon } from "@veltra/desktop";
5
+ import { CircleCheckFilled, CircleClose, Close, Loading, WarningFilled } from "@veltra/icons/normal";
6
+ import "@veltra/desktop/components/collapse/style";
7
+ //#region src/components/ai-chat/tool-call.vue
8
+ const _sfc_main = /*@__PURE__*/ defineComponent({
9
+ name: "UAiChatToolCall",
10
+ __name: "tool-call",
11
+ props: { toolCall: {} },
12
+ emits: ["respond"],
13
+ setup(__props, { emit: __emit }) {
14
+ const props = __props;
15
+ const emit = __emit;
16
+ const di = inject(AiChatDIKey);
17
+ const cls = di?.cls ?? bem("ai-chat");
18
+ /** 当前调用对应的工具定义(解析 icon/label/render/autoCollapse) */
19
+ const tool = computed(() => di?.tools.value[props.toolCall.name]);
20
+ const isExpanded = ref(false);
21
+ /** 用户手动切换过后,不再随 status 自动展开/折叠 */
22
+ let userToggled = false;
23
+ function markUserToggled() {
24
+ userToggled = true;
25
+ }
26
+ /** 完成后是否自动折叠:缺省有 render 时不折叠,否则折叠 */
27
+ const autoCollapse = computed(() => tool.value?.autoCollapse ?? !tool.value?.render);
28
+ watch(() => props.toolCall.status, (status) => {
29
+ if (userToggled) return;
30
+ if (status === "running" || status === "awaiting-confirm") isExpanded.value = true;
31
+ else isExpanded.value = !autoCollapse.value;
32
+ }, { immediate: true });
33
+ const STATUS_META = {
34
+ pending: {
35
+ icon: Loading,
36
+ class: "is-loading"
37
+ },
38
+ running: {
39
+ icon: Loading,
40
+ class: "is-loading"
41
+ },
42
+ "awaiting-confirm": {
43
+ icon: WarningFilled,
44
+ class: "is-warning"
45
+ },
46
+ success: {
47
+ icon: CircleCheckFilled,
48
+ class: "is-success"
49
+ },
50
+ error: {
51
+ icon: CircleClose,
52
+ class: "is-danger"
53
+ },
54
+ rejected: {
55
+ icon: Close,
56
+ class: "is-danger"
57
+ }
58
+ };
59
+ const statusIcon = computed(() => STATUS_META[props.toolCall.status]?.icon ?? Loading);
60
+ const statusClass = computed(() => STATUS_META[props.toolCall.status]?.class ?? "");
61
+ /** 头部图标:工具自定义图标优先,缺省用状态图标 */
62
+ const headerIcon = computed(() => tool.value?.icon ?? statusIcon.value);
63
+ const parsedArguments = computed(() => {
64
+ if (!props.toolCall.arguments) return null;
65
+ try {
66
+ return JSON.parse(props.toolCall.arguments);
67
+ } catch {
68
+ return null;
69
+ }
70
+ });
71
+ /** 折叠态参数摘要:取第一个字符串参数值 */
72
+ const summary = computed(() => {
73
+ const args = parsedArguments.value;
74
+ if (!args) return "";
75
+ for (const value of Object.values(args)) if (typeof value === "string" && value) return value.length > 40 ? `${value.slice(0, 40)}…` : value;
76
+ return "";
77
+ });
78
+ const prettyArguments = computed(() => {
79
+ const args = parsedArguments.value;
80
+ if (args) return JSON.stringify(args, null, 2);
81
+ return props.toolCall.arguments || "";
82
+ });
83
+ const hasResult = computed(() => {
84
+ return props.toolCall.result != null || props.toolCall.error != null;
85
+ });
86
+ const displayResult = computed(() => {
87
+ const raw = props.toolCall.error ?? props.toolCall.result ?? "";
88
+ try {
89
+ return JSON.stringify(JSON.parse(raw), null, 2);
90
+ } catch {
91
+ return raw;
92
+ }
93
+ });
94
+ /** 使用方通过 tool-<name> 插槽自定义该工具的展示(有结果时替换整个 body) */
95
+ const slotBody = computed(() => {
96
+ const slot = di?.slots[`tool-${props.toolCall.name}`];
97
+ return slot ? () => slot({ toolCall: props.toolCall }) : void 0;
98
+ });
99
+ return (_ctx, _cache) => {
100
+ return openBlock(), createElementBlock("div", { class: normalizeClass([
101
+ unref(cls).e("tool-call"),
102
+ unref(bem).is(__props.toolCall.status),
103
+ unref(bem).is("expanded", isExpanded.value)
104
+ ]) }, [createVNode(unref(UCollapseItem), {
105
+ modelValue: isExpanded.value,
106
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => isExpanded.value = $event),
107
+ onChange: markUserToggled
108
+ }, {
109
+ title: withCtx(() => [
110
+ createVNode(unref(UIcon), { class: normalizeClass([unref(cls).e("tool-call-status"), statusClass.value]) }, {
111
+ default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(headerIcon.value)))]),
112
+ _: 1
113
+ }, 8, ["class"]),
114
+ createElementVNode("span", { class: normalizeClass(unref(cls).e("tool-call-name")) }, toDisplayString(tool.value?.label ?? __props.toolCall.name), 3),
115
+ summary.value ? (openBlock(), createElementBlock("span", {
116
+ key: 0,
117
+ class: normalizeClass(unref(cls).e("tool-call-summary"))
118
+ }, toDisplayString(summary.value), 3)) : createCommentVNode("v-if", true),
119
+ __props.toolCall.status === "awaiting-confirm" ? (openBlock(), createElementBlock("span", {
120
+ key: 1,
121
+ class: normalizeClass(unref(cls).e("tool-call-confirm")),
122
+ onClick: _cache[2] || (_cache[2] = withModifiers(() => {}, ["stop"]))
123
+ }, [createVNode(unref(UButton), {
124
+ size: "small",
125
+ type: "primary",
126
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("respond", true))
127
+ }, {
128
+ default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode("允许", -1)])]),
129
+ _: 1
130
+ }), createVNode(unref(UButton), {
131
+ size: "small",
132
+ text: "",
133
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("respond", false))
134
+ }, {
135
+ default: withCtx(() => [..._cache[5] || (_cache[5] = [createTextVNode("拒绝", -1)])]),
136
+ _: 1
137
+ })], 2)) : createCommentVNode("v-if", true)
138
+ ]),
139
+ default: withCtx(() => [tool.value?.render ? (openBlock(), createBlock(resolveDynamicComponent(tool.value?.render), {
140
+ key: 0,
141
+ "tool-call": __props.toolCall
142
+ }, null, 8, ["tool-call"])) : slotBody.value && hasResult.value ? (openBlock(), createBlock(resolveDynamicComponent(slotBody.value), { key: 1 })) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [prettyArguments.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("tool-call-section")) }, "参数", 2), createElementVNode("pre", { class: normalizeClass(unref(cls).e("tool-call-code")) }, toDisplayString(prettyArguments.value), 3)], 64)) : createCommentVNode("v-if", true), hasResult.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("tool-call-section")) }, toDisplayString(__props.toolCall.status === "error" ? "错误" : "结果"), 3), createElementVNode("pre", { class: normalizeClass(unref(cls).e("tool-call-code")) }, toDisplayString(displayResult.value), 3)], 64)) : createCommentVNode("v-if", true)], 64))]),
143
+ _: 1
144
+ }, 8, ["modelValue"])], 2);
145
+ };
146
+ }
147
+ });
148
+ //#endregion
149
+ export { _sfc_main as default };
150
+
151
+ //# sourceMappingURL=tool-call.js.map