@seer-ai/seer-vue 0.1.0-beta.4 → 0.1.0-beta.6
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/README.md +20 -1
- package/dist/content.d.ts +21 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +273 -193
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ npm install @seer-ai/seer-vue@beta
|
|
|
13
13
|
```ts
|
|
14
14
|
import { createApp } from 'vue'
|
|
15
15
|
import { SeerChatPlugin } from '@seer-ai/seer-vue'
|
|
16
|
+
import '@seer-ai/seer-vue/style.css' // 必须:链接的蓝色/下划线样式来自这里
|
|
16
17
|
import App from './App.vue'
|
|
17
18
|
|
|
18
19
|
createApp(App)
|
|
@@ -37,10 +38,14 @@ import { SeerChat } from '@seer-ai/seer-vue'
|
|
|
37
38
|
|
|
38
39
|
## Use custom UI
|
|
39
40
|
|
|
41
|
+
自定义 UI 时**不要**直接把 `message.content` 当纯文本渲染——回复里的下载/截图链接是裸
|
|
42
|
+
URL 文本,会原样显示成一长串 url。用导出的 `toContentLines` 把它切成可点链接(这正是
|
|
43
|
+
`<SeerChat>` 内部用的逻辑):
|
|
44
|
+
|
|
40
45
|
```vue
|
|
41
46
|
<script setup lang="ts">
|
|
42
47
|
import { ref } from 'vue'
|
|
43
|
-
import { useSeerChat } from '@seer-ai/seer-vue'
|
|
48
|
+
import { toContentLines, useSeerChat } from '@seer-ai/seer-vue'
|
|
44
49
|
|
|
45
50
|
const input = ref('')
|
|
46
51
|
const chat = useSeerChat()
|
|
@@ -52,6 +57,20 @@ async function submit() {
|
|
|
52
57
|
await chat.send(content)
|
|
53
58
|
}
|
|
54
59
|
</script>
|
|
60
|
+
|
|
61
|
+
<template>
|
|
62
|
+
<div v-for="(message, i) in chat.messages.value" :key="i">
|
|
63
|
+
<p v-for="(line, li) in toContentLines(message.content)" :key="li">
|
|
64
|
+
<template v-for="(seg, si) in line" :key="si">
|
|
65
|
+
<a v-if="seg.kind === 'link'" :href="seg.href" target="_blank" rel="noopener noreferrer">{{
|
|
66
|
+
seg.label
|
|
67
|
+
}}</a>
|
|
68
|
+
<img v-else-if="seg.kind === 'image'" :src="seg.href" :alt="seg.alt" loading="lazy" />
|
|
69
|
+
<span v-else>{{ seg.value }}</span>
|
|
70
|
+
</template>
|
|
71
|
+
</p>
|
|
72
|
+
</div>
|
|
73
|
+
</template>
|
|
55
74
|
```
|
|
56
75
|
|
|
57
76
|
## Security
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type ContentSegment = {
|
|
2
|
+
kind: 'text';
|
|
3
|
+
value: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'link';
|
|
6
|
+
href: string;
|
|
7
|
+
label: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'image';
|
|
10
|
+
href: string;
|
|
11
|
+
alt: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'price';
|
|
14
|
+
value: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'strong';
|
|
17
|
+
value: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function linkAnchorLabel(prefix: string): string | null;
|
|
20
|
+
export declare function toContentSegments(content: string): ContentSegment[];
|
|
21
|
+
export declare function toContentLines(content: string): ContentSegment[][];
|
package/dist/index.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.seer-chat[data-v-
|
|
1
|
+
.seer-chat[data-v-dc651545]{display:grid;gap:18px;max-width:720px;min-height:640px;padding:0;overflow:hidden;color:#18212f;background:radial-gradient(circle at 18% 0%,rgba(71,106,255,.14),transparent 32%),linear-gradient(180deg,#fff,#f7f9fd);border:1px solid rgba(127,143,170,.28);border-radius:28px;box-shadow:0 24px 70px #151f3429}.seer-chat__header[data-v-dc651545]{display:flex;gap:16px;align-items:flex-start;justify-content:space-between;padding:22px 22px 18px;color:#fff;background:linear-gradient(135deg,#141f34f5,#243f76f0),#172033}.seer-chat__header h2[data-v-dc651545]{margin:0;font-size:22px;letter-spacing:-.03em}.seer-chat__header p[data-v-dc651545],.seer-chat__empty[data-v-dc651545],.seer-chat__status[data-v-dc651545]{margin:4px 0 0;color:#5f6f89;font-size:14px}.seer-chat__header p[data-v-dc651545]{max-width:34ch;color:#ffffffb8}.seer-chat__messages[data-v-dc651545]{display:grid;gap:12px;align-content:start;min-height:330px;max-height:470px;padding:18px 18px 4px;overflow-y:auto}.seer-chat__empty[data-v-dc651545]{display:grid;place-items:center;min-height:260px;padding:0 28px;color:#66748a;text-align:center;border:1px dashed rgba(127,143,170,.38);border-radius:22px;background:#ffffff9e}.seer-chat__message[data-v-dc651545]{display:grid;gap:6px;max-width:min(86%,520px);padding:13px 15px;border:1px solid rgba(127,143,170,.16);border-radius:18px 18px 18px 6px;background:#ffffffe0;box-shadow:0 12px 28px #151f3414}.seer-chat__message--user[data-v-dc651545]{justify-self:end;color:#fff;border-color:transparent;border-radius:18px 18px 6px;background:linear-gradient(135deg,#285cff,#1d45c9);box-shadow:0 14px 30px #1f5eff3d}.seer-chat__role[data-v-dc651545]{font-size:12px;font-weight:700;opacity:.75}.seer-chat__body[data-v-dc651545]{display:flex;flex-direction:column}.seer-chat__line[data-v-dc651545]{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.seer-chat__line[data-v-dc651545]:not(:last-child){margin-bottom:5px}.seer-chat__line[data-v-dc651545]:empty{height:7px}.seer-chat__strong[data-v-dc651545]{font-weight:700}.seer-chat__price[data-v-dc651545]{color:#e0331d;font-weight:600}.seer-chat__message--user .seer-chat__price[data-v-dc651545]{color:#ffe2dc}.seer-chat__link[data-v-dc651545]{color:#1f5eff;font-weight:600;text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}.seer-chat__link[data-v-dc651545]:hover{color:#173fb3}.seer-chat__message--user .seer-chat__link[data-v-dc651545]{color:#fff}.seer-chat__image-button[data-v-dc651545]{display:block;padding:0;background:transparent;border:0;cursor:zoom-in}.seer-chat__image-button[data-v-dc651545]:not(:disabled):hover{transform:none}.seer-chat__image[data-v-dc651545]{display:block;max-width:100%;margin:6px 0;border-radius:12px;border:1px solid rgba(127,143,170,.22);transition:filter .16s ease}.seer-chat__image-button:hover .seer-chat__image[data-v-dc651545]{filter:brightness(.96)}.seer-chat__lightbox[data-v-dc651545]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;display:grid;place-items:center;padding:32px;background:#0a0f1ad1;cursor:zoom-out;animation:seer-chat__fade-in-dc651545 .14s ease}.seer-chat__lightbox-image[data-v-dc651545]{max-width:min(92vw,1200px);max-height:90vh;object-fit:contain;border-radius:12px;box-shadow:0 30px 80px #00000080}.seer-chat__lightbox-close[data-v-dc651545]{position:fixed;top:18px;right:22px;display:grid;place-items:center;width:40px;height:40px;color:#fff;font-size:26px;line-height:1;background:#ffffff24;border-radius:50%}.seer-chat__lightbox-close[data-v-dc651545]:not(:disabled):hover{background:#ffffff3d}@keyframes seer-chat__fade-in-dc651545{0%{opacity:0}to{opacity:1}}.seer-chat__error[data-v-dc651545],.seer-chat__handoff[data-v-dc651545]{margin:0 18px;padding:10px 12px;border-radius:14px}.seer-chat__error[data-v-dc651545]{color:#8a1f11;background:#fff0ed}.seer-chat__handoff[data-v-dc651545]{color:#704600;background:#fff7db}.seer-chat__form[data-v-dc651545]{display:grid;gap:11px;margin:0 18px 18px;padding:14px;background:#fff;border:1px solid rgba(127,143,170,.26);border-radius:22px;box-shadow:0 18px 38px #151f3417}.seer-chat__label[data-v-dc651545]{font-size:13px;font-weight:700}.seer-chat textarea[data-v-dc651545]{width:100%;box-sizing:border-box;min-height:92px;padding:14px;color:inherit;line-height:1.55;background:#f8fafd;border:1px solid transparent;border-radius:16px;outline:none;resize:vertical;transition:background .16s ease,border-color .16s ease,box-shadow .16s ease}.seer-chat button[data-v-dc651545]{color:inherit;font-weight:700;border:0;cursor:pointer;transition:transform .16s ease,box-shadow .16s ease,opacity .16s ease}.seer-chat button[data-v-dc651545]:not(:disabled):hover{transform:translateY(-1px)}.seer-chat button[data-v-dc651545]:disabled{cursor:not-allowed;opacity:.5}.seer-chat button[data-v-dc651545]:focus-visible,.seer-chat textarea[data-v-dc651545]:focus-visible{border-color:#1f5eff6b;box-shadow:0 0 0 4px #1f5eff21}.seer-chat__clear-button[data-v-dc651545]{flex:0 0 auto;padding:8px 11px;color:#ffffffc7;font-size:12px;background:transparent;border:1px solid rgba(255,255,255,.18)!important;border-radius:12px}.seer-chat__clear-button[data-v-dc651545]:not(:disabled):hover{color:#fff;background:#ffffff1a;box-shadow:none}.seer-chat__stop-button[data-v-dc651545]{padding:8px 11px;color:#6c3b12;font-size:12px;background:#fff1df;border-radius:12px}.seer-chat__send-button[data-v-dc651545]{display:inline-flex;gap:8px;align-items:center;justify-content:center;min-width:86px;padding:10px 14px;color:#fff;background:linear-gradient(135deg,#1f5eff,#173fb3);border-radius:14px;box-shadow:0 12px 24px #1f5eff38}.seer-chat__send-button[data-v-dc651545]:not(:disabled):hover{box-shadow:0 14px 28px #1f5eff47}.seer-chat__hint[data-v-dc651545]{margin-right:auto;color:#7a879a;font-size:12px}.seer-chat__composer-footer[data-v-dc651545]{display:flex;gap:8px;align-items:center;justify-content:flex-end}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createSeerClient } from './client';
|
|
2
|
+
export { linkAnchorLabel, toContentLines, toContentSegments, type ContentSegment, } from './content';
|
|
2
3
|
export { SeerChatPlugin } from './plugin';
|
|
3
4
|
export { useSeerChat } from './useSeerChat';
|
|
4
5
|
export { default as SeerChat } from './components/SeerChat.vue';
|
package/dist/index.js
CHANGED
|
@@ -1,196 +1,248 @@
|
|
|
1
|
-
import { hasInjectionContext as
|
|
2
|
-
function
|
|
3
|
-
const n =
|
|
4
|
-
let
|
|
1
|
+
import { hasInjectionContext as K, inject as D, ref as y, defineComponent as U, computed as B, onBeforeUnmount as M, openBlock as h, createElementBlock as _, createElementVNode as f, toDisplayString as C, unref as g, createCommentVNode as T, Fragment as R, renderList as I, normalizeClass as q, withModifiers as A, withDirectives as z, withKeys as F, vModelText as V } from "vue";
|
|
2
|
+
function X(e) {
|
|
3
|
+
const n = e.fetcher ?? fetch, t = e.model ?? "seer", s = e.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
4
|
+
let r, a = 0;
|
|
5
5
|
return {
|
|
6
|
-
async chat(l,
|
|
7
|
-
const
|
|
6
|
+
async chat(l, i = {}) {
|
|
7
|
+
const c = l.stream ?? !1, o = await v(), m = {
|
|
8
8
|
"Content-Type": "application/json"
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
const
|
|
10
|
+
o && (m.Authorization = `Bearer ${o}`);
|
|
11
|
+
const k = await n(e.endpoint, {
|
|
12
12
|
method: "POST",
|
|
13
|
-
headers:
|
|
14
|
-
signal:
|
|
13
|
+
headers: m,
|
|
14
|
+
signal: i.signal,
|
|
15
15
|
body: JSON.stringify({
|
|
16
|
-
model: l.model ??
|
|
16
|
+
model: l.model ?? t,
|
|
17
17
|
messages: l.messages,
|
|
18
|
-
stream:
|
|
18
|
+
stream: c,
|
|
19
19
|
conversation_id: l.conversationId,
|
|
20
20
|
metadata: l.metadata
|
|
21
21
|
})
|
|
22
22
|
});
|
|
23
|
-
if (!
|
|
24
|
-
throw new Error(await
|
|
25
|
-
if (
|
|
26
|
-
return
|
|
27
|
-
const
|
|
28
|
-
return
|
|
23
|
+
if (!k.ok)
|
|
24
|
+
throw new Error(await P(k));
|
|
25
|
+
if (c)
|
|
26
|
+
return G(k, i.onDelta);
|
|
27
|
+
const w = await k.json();
|
|
28
|
+
return j(w);
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
async function
|
|
32
|
-
if (!
|
|
31
|
+
async function v() {
|
|
32
|
+
if (!e.sessionEndpoint)
|
|
33
33
|
return;
|
|
34
|
-
const l =
|
|
35
|
-
if (
|
|
36
|
-
return
|
|
37
|
-
const
|
|
34
|
+
const l = s();
|
|
35
|
+
if (r && a - 30 > l)
|
|
36
|
+
return r;
|
|
37
|
+
const i = await n(e.sessionEndpoint, {
|
|
38
38
|
method: "GET",
|
|
39
|
-
headers: await
|
|
39
|
+
headers: await p()
|
|
40
40
|
});
|
|
41
|
-
if (!
|
|
42
|
-
throw new Error(await
|
|
43
|
-
const
|
|
44
|
-
return
|
|
41
|
+
if (!i.ok)
|
|
42
|
+
throw new Error(await P(i));
|
|
43
|
+
const c = await i.json();
|
|
44
|
+
return r = c.token, a = c.expires_at, r;
|
|
45
45
|
}
|
|
46
|
-
async function
|
|
46
|
+
async function p() {
|
|
47
47
|
const l = {};
|
|
48
|
-
return
|
|
48
|
+
return e.challengeTokenProvider && (l["X-Seer-Challenge-Token"] = await e.challengeTokenProvider()), l;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
function
|
|
52
|
-
var
|
|
53
|
-
const n = (
|
|
51
|
+
function j(e) {
|
|
52
|
+
var t, s, r;
|
|
53
|
+
const n = (t = e.choices) == null ? void 0 : t[0];
|
|
54
54
|
return {
|
|
55
|
-
content: ((
|
|
56
|
-
conversationId:
|
|
57
|
-
citations:
|
|
58
|
-
handoffRequested:
|
|
55
|
+
content: ((s = n == null ? void 0 : n.message) == null ? void 0 : s.content) ?? ((r = n == null ? void 0 : n.delta) == null ? void 0 : r.content) ?? "",
|
|
56
|
+
conversationId: e.conversation_id,
|
|
57
|
+
citations: e.citations ?? [],
|
|
58
|
+
handoffRequested: e.handoff_requested ?? !1,
|
|
59
59
|
finishReason: n == null ? void 0 : n.finish_reason
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
async function
|
|
63
|
-
var
|
|
64
|
-
if (!
|
|
65
|
-
return
|
|
66
|
-
const
|
|
67
|
-
let
|
|
62
|
+
async function G(e, n) {
|
|
63
|
+
var c, o, m;
|
|
64
|
+
if (!e.body)
|
|
65
|
+
return j({});
|
|
66
|
+
const t = e.body.getReader(), s = new TextDecoder();
|
|
67
|
+
let r = "", a = "", v, p = [], l = !1, i;
|
|
68
68
|
for (; ; ) {
|
|
69
|
-
const { done:
|
|
70
|
-
if (
|
|
69
|
+
const { done: k, value: w } = await t.read();
|
|
70
|
+
if (k)
|
|
71
71
|
break;
|
|
72
|
-
|
|
73
|
-
const
|
|
72
|
+
r += s.decode(w, { stream: !0 });
|
|
73
|
+
const x = r.split(`
|
|
74
74
|
|
|
75
75
|
`);
|
|
76
|
-
|
|
77
|
-
for (const
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
76
|
+
r = x.pop() ?? "";
|
|
77
|
+
for (const u of x) {
|
|
78
|
+
const E = H(u);
|
|
79
|
+
if (!E || E === "[DONE]")
|
|
80
80
|
continue;
|
|
81
|
-
const
|
|
82
|
-
|
|
81
|
+
const b = JSON.parse(E), d = (c = b.choices) == null ? void 0 : c[0], S = ((o = d == null ? void 0 : d.delta) == null ? void 0 : o.content) ?? ((m = d == null ? void 0 : d.message) == null ? void 0 : m.content) ?? "";
|
|
82
|
+
S && (a += S, n == null || n(S)), v = b.conversation_id ?? v, p = b.citations ?? p, l = b.handoff_requested ?? l, i = (d == null ? void 0 : d.finish_reason) ?? i;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
return {
|
|
86
|
-
content:
|
|
87
|
-
conversationId:
|
|
88
|
-
citations:
|
|
86
|
+
content: a,
|
|
87
|
+
conversationId: v,
|
|
88
|
+
citations: p,
|
|
89
89
|
handoffRequested: l,
|
|
90
|
-
finishReason:
|
|
90
|
+
finishReason: i
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
-
function
|
|
94
|
-
const n =
|
|
95
|
-
`).map((
|
|
93
|
+
function H(e) {
|
|
94
|
+
const n = e.split(`
|
|
95
|
+
`).map((t) => t.trim()).filter((t) => t.startsWith("data:")).map((t) => t.slice(5).trim());
|
|
96
96
|
return n.length === 0 ? null : n.join(`
|
|
97
97
|
`);
|
|
98
98
|
}
|
|
99
|
-
async function
|
|
100
|
-
let n =
|
|
99
|
+
async function P(e) {
|
|
100
|
+
let n = e.statusText || "unknown error";
|
|
101
101
|
try {
|
|
102
|
-
const
|
|
103
|
-
typeof (
|
|
102
|
+
const t = await e.json();
|
|
103
|
+
typeof (t == null ? void 0 : t.detail) == "string" ? n = t.detail : typeof (t == null ? void 0 : t.error) == "string" && (n = t.error);
|
|
104
104
|
} catch {
|
|
105
|
-
const
|
|
106
|
-
|
|
105
|
+
const t = await e.text().catch(() => "");
|
|
106
|
+
t && (n = t);
|
|
107
|
+
}
|
|
108
|
+
return `seer chat request failed with status ${e.status}: ${n}`;
|
|
109
|
+
}
|
|
110
|
+
const O = /https?:\/\/[^\s)]+/g, J = /\.(png|jpe?g|gif|webp|bmp|svg)$/i, W = "单价\\s*\\d[\\d.]*\\s*[A-Za-z]+\\/kg", Y = "按当前信息为您筛选出[^\\n:]*推荐方案[^\\n:]*:", Z = "方案PK|时效最快|价格最低", $ = new RegExp(`(${W})|(${Y})|(${Z})`, "g");
|
|
111
|
+
function L(e) {
|
|
112
|
+
const n = [];
|
|
113
|
+
$.lastIndex = 0;
|
|
114
|
+
let t = 0, s;
|
|
115
|
+
for (; (s = $.exec(e)) !== null; ) {
|
|
116
|
+
s.index > t && n.push({ kind: "text", value: e.slice(t, s.index) });
|
|
117
|
+
const r = s[0];
|
|
118
|
+
n.push(s[1] ? { kind: "price", value: r } : { kind: "strong", value: r }), t = s.index + r.length;
|
|
119
|
+
}
|
|
120
|
+
return t < e.length && n.push({ kind: "text", value: e.slice(t) }), n;
|
|
121
|
+
}
|
|
122
|
+
const Q = /标签|箱唛|唛头|入库单|交接单|入仓单|面单|下载/;
|
|
123
|
+
function ee(e, n) {
|
|
124
|
+
if (/方案|截图|报价图|效果图|示意图|图片/.test(n))
|
|
125
|
+
return !0;
|
|
126
|
+
if (Q.test(n))
|
|
127
|
+
return !1;
|
|
128
|
+
const t = e.split(/[?#]/)[0];
|
|
129
|
+
return J.test(t);
|
|
130
|
+
}
|
|
131
|
+
function te(e) {
|
|
132
|
+
return /方案|报价|截图|PK/.test(e) ? "方案PK" : "图片";
|
|
133
|
+
}
|
|
134
|
+
function ne(e) {
|
|
135
|
+
return /标签|箱唛/.test(e) ? "点击下载箱唛" : /入库单|交接单/.test(e) ? "点击下载入库单" : /面单/.test(e) ? "点击下载面单" : /截图|方案/.test(e) ? "点击查看方案PK" : /下载链接|labelUrl|下载/.test(e) ? "点击下载" : /链接/.test(e) ? "点击查看" : null;
|
|
136
|
+
}
|
|
137
|
+
function se(e) {
|
|
138
|
+
const n = [];
|
|
139
|
+
O.lastIndex = 0;
|
|
140
|
+
let t = 0, s;
|
|
141
|
+
for (; (s = O.exec(e)) !== null; ) {
|
|
142
|
+
const r = s[0], a = s.index, v = e.lastIndexOf(`
|
|
143
|
+
`, a - 1) + 1, p = e.slice(v, a), l = ee(r, p);
|
|
144
|
+
if (a > t) {
|
|
145
|
+
let i = e.slice(t, a);
|
|
146
|
+
l && (i = i.replace(/链接([::])\s*$/, "$1")), n.push(...L(i));
|
|
147
|
+
}
|
|
148
|
+
l ? n.push({ kind: "image", href: r, alt: te(p) }) : n.push({ kind: "link", href: r, label: ne(p) ?? r }), t = a + r.length;
|
|
107
149
|
}
|
|
108
|
-
return
|
|
150
|
+
return t < e.length && n.push(...L(e.slice(t))), n;
|
|
109
151
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
152
|
+
function ae(e) {
|
|
153
|
+
return e.split(`
|
|
154
|
+
`).map((n) => se(n));
|
|
155
|
+
}
|
|
156
|
+
const N = Symbol("seer-chat-options"), Ie = {
|
|
157
|
+
install(e, n) {
|
|
158
|
+
e.provide(N, n);
|
|
113
159
|
}
|
|
114
160
|
};
|
|
115
|
-
function
|
|
116
|
-
const
|
|
117
|
-
if (!
|
|
161
|
+
function oe(e = {}) {
|
|
162
|
+
const t = { ...K() ? D(N, void 0) : void 0, ...e };
|
|
163
|
+
if (!t.endpoint)
|
|
118
164
|
throw new Error("Seer chat endpoint is required. Pass it to app.use() or useSeerChat().");
|
|
119
|
-
const
|
|
120
|
-
endpoint:
|
|
121
|
-
sessionEndpoint:
|
|
122
|
-
challengeTokenProvider:
|
|
123
|
-
model:
|
|
124
|
-
fetcher:
|
|
125
|
-
},
|
|
126
|
-
let
|
|
127
|
-
async function
|
|
128
|
-
const
|
|
129
|
-
if (!
|
|
165
|
+
const s = {
|
|
166
|
+
endpoint: t.endpoint,
|
|
167
|
+
sessionEndpoint: t.sessionEndpoint,
|
|
168
|
+
challengeTokenProvider: t.challengeTokenProvider,
|
|
169
|
+
model: t.model,
|
|
170
|
+
fetcher: t.fetcher
|
|
171
|
+
}, r = X(s), a = y([]), v = y(!1), p = y(null), l = y(), i = y([]), c = y(!1);
|
|
172
|
+
let o = null;
|
|
173
|
+
async function m(x) {
|
|
174
|
+
const u = x.trim();
|
|
175
|
+
if (!u || v.value)
|
|
130
176
|
return;
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
177
|
+
const E = { role: "user", content: u };
|
|
178
|
+
a.value = [...a.value, E], v.value = !0, p.value = null, o = new AbortController();
|
|
179
|
+
const b = { role: "assistant", content: "" };
|
|
180
|
+
t.stream && (a.value = [...a.value, b]);
|
|
135
181
|
try {
|
|
136
|
-
const
|
|
182
|
+
const d = await r.chat(
|
|
137
183
|
{
|
|
138
|
-
messages:
|
|
139
|
-
stream:
|
|
184
|
+
messages: a.value.filter((S) => S.content),
|
|
185
|
+
stream: t.stream ?? !1,
|
|
140
186
|
conversationId: l.value
|
|
141
187
|
},
|
|
142
188
|
{
|
|
143
|
-
signal:
|
|
144
|
-
onDelta(
|
|
145
|
-
|
|
189
|
+
signal: o.signal,
|
|
190
|
+
onDelta(S) {
|
|
191
|
+
b.content += S, a.value = [...a.value];
|
|
146
192
|
}
|
|
147
193
|
}
|
|
148
194
|
);
|
|
149
|
-
|
|
150
|
-
} catch (
|
|
151
|
-
|
|
195
|
+
t.stream || (a.value = [...a.value, { role: "assistant", content: d.content }]), l.value = d.conversationId, i.value = d.citations, c.value = d.handoffRequested;
|
|
196
|
+
} catch (d) {
|
|
197
|
+
d.name !== "AbortError" && (p.value = d instanceof Error ? d.message : String(d));
|
|
152
198
|
} finally {
|
|
153
|
-
|
|
199
|
+
v.value = !1, o = null;
|
|
154
200
|
}
|
|
155
201
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
202
|
+
function k() {
|
|
203
|
+
a.value = [], p.value = null, l.value = void 0, i.value = [], c.value = !1;
|
|
158
204
|
}
|
|
159
|
-
function
|
|
160
|
-
|
|
205
|
+
function w() {
|
|
206
|
+
o == null || o.abort();
|
|
161
207
|
}
|
|
162
208
|
return {
|
|
163
|
-
messages:
|
|
164
|
-
loading:
|
|
165
|
-
error:
|
|
209
|
+
messages: a,
|
|
210
|
+
loading: v,
|
|
211
|
+
error: p,
|
|
166
212
|
conversationId: l,
|
|
167
|
-
citations:
|
|
168
|
-
handoffRequested:
|
|
169
|
-
send:
|
|
170
|
-
reset:
|
|
171
|
-
abort:
|
|
213
|
+
citations: i,
|
|
214
|
+
handoffRequested: c,
|
|
215
|
+
send: m,
|
|
216
|
+
reset: k,
|
|
217
|
+
abort: w
|
|
172
218
|
};
|
|
173
219
|
}
|
|
174
|
-
const
|
|
220
|
+
const re = {
|
|
175
221
|
class: "seer-chat",
|
|
176
222
|
"aria-label": "多发货 AI 客服聊天"
|
|
177
|
-
},
|
|
223
|
+
}, le = { class: "seer-chat__header" }, ie = {
|
|
178
224
|
class: "seer-chat__messages",
|
|
179
225
|
"aria-live": "polite"
|
|
180
|
-
},
|
|
226
|
+
}, ce = {
|
|
181
227
|
key: 0,
|
|
182
228
|
class: "seer-chat__empty"
|
|
183
|
-
},
|
|
229
|
+
}, ue = { class: "seer-chat__role" }, de = { class: "seer-chat__body" }, fe = ["href"], he = ["aria-label", "onClick"], _e = ["src", "alt"], ve = {
|
|
230
|
+
key: 2,
|
|
231
|
+
class: "seer-chat__strong"
|
|
232
|
+
}, pe = {
|
|
233
|
+
key: 3,
|
|
234
|
+
class: "seer-chat__price"
|
|
235
|
+
}, me = { key: 4 }, ge = {
|
|
184
236
|
key: 0,
|
|
185
237
|
class: "seer-chat__status"
|
|
186
|
-
},
|
|
238
|
+
}, ke = {
|
|
187
239
|
key: 1,
|
|
188
240
|
class: "seer-chat__error",
|
|
189
241
|
role: "alert"
|
|
190
|
-
},
|
|
242
|
+
}, be = {
|
|
191
243
|
key: 2,
|
|
192
244
|
class: "seer-chat__handoff"
|
|
193
|
-
},
|
|
245
|
+
}, ye = ["placeholder", "disabled", "onKeydown"], we = { class: "seer-chat__composer-footer" }, Ee = ["disabled"], Se = ["aria-label"], Ce = ["src", "alt"], xe = /* @__PURE__ */ U({
|
|
194
246
|
__name: "SeerChat",
|
|
195
247
|
props: {
|
|
196
248
|
endpoint: {},
|
|
@@ -200,128 +252,156 @@ const J = {
|
|
|
200
252
|
title: { default: "多发货 AI 客服" },
|
|
201
253
|
placeholder: { default: "输入你的问题,例如:美国海派怎么报价?" }
|
|
202
254
|
},
|
|
203
|
-
setup(
|
|
204
|
-
const n =
|
|
255
|
+
setup(e) {
|
|
256
|
+
const n = e, t = y(""), s = oe({
|
|
205
257
|
endpoint: n.endpoint,
|
|
206
258
|
model: n.model,
|
|
207
259
|
stream: n.stream,
|
|
208
260
|
fetcher: n.fetcher
|
|
209
|
-
}),
|
|
210
|
-
() =>
|
|
211
|
-
),
|
|
212
|
-
function
|
|
213
|
-
|
|
261
|
+
}), r = B(
|
|
262
|
+
() => t.value.trim().length > 0 && !s.loading.value
|
|
263
|
+
), a = y(null);
|
|
264
|
+
function v(c) {
|
|
265
|
+
c.key === "Escape" && l();
|
|
214
266
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
i.lastIndex = 0;
|
|
218
|
-
let r = 0, h;
|
|
219
|
-
for (; (h = i.exec(a)) !== null; ) {
|
|
220
|
-
const y = h[0], g = h.index;
|
|
221
|
-
g > r && s.push({ kind: "text", value: a.slice(r, g) });
|
|
222
|
-
const k = a.lastIndexOf(`
|
|
223
|
-
`, g - 1) + 1, b = p(a.slice(k, g)) ?? y;
|
|
224
|
-
s.push({ kind: "link", href: y, label: b }), r = g + y.length;
|
|
225
|
-
}
|
|
226
|
-
return r < a.length && s.push({ kind: "text", value: a.slice(r) }), s;
|
|
267
|
+
function p(c, o) {
|
|
268
|
+
a.value = { src: c, alt: o }, window.addEventListener("keydown", v);
|
|
227
269
|
}
|
|
228
|
-
function l(
|
|
229
|
-
|
|
230
|
-
`).map((s) => _(s));
|
|
270
|
+
function l() {
|
|
271
|
+
a.value = null, window.removeEventListener("keydown", v);
|
|
231
272
|
}
|
|
232
|
-
|
|
233
|
-
|
|
273
|
+
M(() => {
|
|
274
|
+
window.removeEventListener("keydown", v);
|
|
275
|
+
});
|
|
276
|
+
async function i() {
|
|
277
|
+
if (!r.value)
|
|
234
278
|
return;
|
|
235
|
-
const
|
|
236
|
-
|
|
279
|
+
const c = t.value;
|
|
280
|
+
t.value = "", await s.send(c);
|
|
237
281
|
}
|
|
238
|
-
return (
|
|
239
|
-
f("header",
|
|
282
|
+
return (c, o) => (h(), _("section", re, [
|
|
283
|
+
f("header", le, [
|
|
240
284
|
f("div", null, [
|
|
241
|
-
f("h2", null,
|
|
242
|
-
|
|
285
|
+
f("h2", null, C(e.title), 1),
|
|
286
|
+
o[3] || (o[3] = f("p", null, "可咨询渠道报价、时效、下单资料、入仓和售后问题。", -1))
|
|
243
287
|
]),
|
|
244
288
|
f("button", {
|
|
245
289
|
type: "button",
|
|
246
290
|
class: "seer-chat__clear-button",
|
|
247
|
-
onClick:
|
|
291
|
+
onClick: o[0] || (o[0] = (m) => g(s).reset())
|
|
248
292
|
}, " 清空对话 ")
|
|
249
293
|
]),
|
|
250
|
-
f("div",
|
|
251
|
-
|
|
252
|
-
(
|
|
253
|
-
key: `${
|
|
254
|
-
class:
|
|
294
|
+
f("div", ie, [
|
|
295
|
+
g(s).messages.value.length === 0 ? (h(), _("p", ce, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : T("", !0),
|
|
296
|
+
(h(!0), _(R, null, I(g(s).messages.value, (m, k) => (h(), _("article", {
|
|
297
|
+
key: `${m.role}-${k}`,
|
|
298
|
+
class: q(["seer-chat__message", `seer-chat__message--${m.role}`])
|
|
255
299
|
}, [
|
|
256
|
-
f("span",
|
|
257
|
-
f("div",
|
|
258
|
-
(
|
|
259
|
-
key:
|
|
300
|
+
f("span", ue, C(m.role === "user" ? "你" : "AI 客服"), 1),
|
|
301
|
+
f("div", de, [
|
|
302
|
+
(h(!0), _(R, null, I(g(ae)(m.content), (w, x) => (h(), _("p", {
|
|
303
|
+
key: x,
|
|
260
304
|
class: "seer-chat__line"
|
|
261
305
|
}, [
|
|
262
|
-
(
|
|
263
|
-
|
|
306
|
+
(h(!0), _(R, null, I(w, (u, E) => (h(), _(R, { key: E }, [
|
|
307
|
+
u.kind === "link" ? (h(), _("a", {
|
|
264
308
|
key: 0,
|
|
265
309
|
class: "seer-chat__link",
|
|
266
|
-
href:
|
|
310
|
+
href: u.href,
|
|
267
311
|
target: "_blank",
|
|
268
312
|
rel: "noopener noreferrer"
|
|
269
|
-
},
|
|
313
|
+
}, C(u.label), 9, fe)) : u.kind === "image" ? (h(), _("button", {
|
|
314
|
+
key: 1,
|
|
315
|
+
type: "button",
|
|
316
|
+
class: "seer-chat__image-button",
|
|
317
|
+
"aria-label": `放大查看${u.alt}`,
|
|
318
|
+
onClick: (b) => p(u.href, u.alt)
|
|
319
|
+
}, [
|
|
320
|
+
f("img", {
|
|
321
|
+
class: "seer-chat__image",
|
|
322
|
+
src: u.href,
|
|
323
|
+
alt: u.alt,
|
|
324
|
+
loading: "lazy"
|
|
325
|
+
}, null, 8, _e)
|
|
326
|
+
], 8, he)) : u.kind === "strong" ? (h(), _("strong", ve, C(u.value), 1)) : u.kind === "price" ? (h(), _("span", pe, C(u.value), 1)) : (h(), _("span", me, C(u.value), 1))
|
|
270
327
|
], 64))), 128))
|
|
271
328
|
]))), 128))
|
|
272
329
|
])
|
|
273
330
|
], 2))), 128))
|
|
274
331
|
]),
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
332
|
+
g(s).loading.value ? (h(), _("p", ge, "正在回复...")) : T("", !0),
|
|
333
|
+
g(s).error.value ? (h(), _("p", ke, C(g(s).error.value), 1)) : T("", !0),
|
|
334
|
+
g(s).handoffRequested.value ? (h(), _("p", be, " 建议转人工:请留下联系方式或等待客服接入。 ")) : T("", !0),
|
|
278
335
|
f("form", {
|
|
279
336
|
class: "seer-chat__form",
|
|
280
|
-
onSubmit:
|
|
337
|
+
onSubmit: A(i, ["prevent"])
|
|
281
338
|
}, [
|
|
282
|
-
|
|
339
|
+
o[6] || (o[6] = f("label", {
|
|
283
340
|
class: "seer-chat__label",
|
|
284
341
|
for: "seer-chat-input"
|
|
285
342
|
}, "问题", -1)),
|
|
286
|
-
|
|
343
|
+
z(f("textarea", {
|
|
287
344
|
id: "seer-chat-input",
|
|
288
|
-
"onUpdate:modelValue":
|
|
289
|
-
placeholder:
|
|
290
|
-
disabled:
|
|
345
|
+
"onUpdate:modelValue": o[1] || (o[1] = (m) => t.value = m),
|
|
346
|
+
placeholder: e.placeholder,
|
|
347
|
+
disabled: g(s).loading.value,
|
|
291
348
|
rows: "3",
|
|
292
|
-
onKeydown:
|
|
293
|
-
}, null, 40,
|
|
294
|
-
[
|
|
349
|
+
onKeydown: F(A(i, ["exact", "prevent"]), ["enter"])
|
|
350
|
+
}, null, 40, ye), [
|
|
351
|
+
[V, t.value]
|
|
295
352
|
]),
|
|
296
|
-
f("div",
|
|
297
|
-
|
|
298
|
-
|
|
353
|
+
f("div", we, [
|
|
354
|
+
o[5] || (o[5] = f("span", { class: "seer-chat__hint" }, "Enter 发送 · Shift+Enter 换行", -1)),
|
|
355
|
+
g(s).loading.value ? (h(), _("button", {
|
|
299
356
|
key: 0,
|
|
300
357
|
type: "button",
|
|
301
358
|
class: "seer-chat__stop-button",
|
|
302
|
-
onClick:
|
|
303
|
-
}, " 停止 ")) :
|
|
359
|
+
onClick: o[2] || (o[2] = (m) => g(s).abort())
|
|
360
|
+
}, " 停止 ")) : T("", !0),
|
|
304
361
|
f("button", {
|
|
305
362
|
class: "seer-chat__send-button",
|
|
306
363
|
type: "submit",
|
|
307
|
-
disabled: !
|
|
308
|
-
}, [...
|
|
364
|
+
disabled: !r.value
|
|
365
|
+
}, [...o[4] || (o[4] = [
|
|
309
366
|
f("span", null, "发送", -1),
|
|
310
367
|
f("span", { "aria-hidden": "true" }, "↵", -1)
|
|
311
|
-
])], 8,
|
|
368
|
+
])], 8, Ee)
|
|
312
369
|
])
|
|
313
|
-
], 32)
|
|
370
|
+
], 32),
|
|
371
|
+
a.value ? (h(), _("div", {
|
|
372
|
+
key: 3,
|
|
373
|
+
class: "seer-chat__lightbox",
|
|
374
|
+
role: "dialog",
|
|
375
|
+
"aria-modal": "true",
|
|
376
|
+
"aria-label": a.value.alt,
|
|
377
|
+
onClick: l
|
|
378
|
+
}, [
|
|
379
|
+
f("button", {
|
|
380
|
+
type: "button",
|
|
381
|
+
class: "seer-chat__lightbox-close",
|
|
382
|
+
"aria-label": "关闭",
|
|
383
|
+
onClick: l
|
|
384
|
+
}, " × "),
|
|
385
|
+
f("img", {
|
|
386
|
+
class: "seer-chat__lightbox-image",
|
|
387
|
+
src: a.value.src,
|
|
388
|
+
alt: a.value.alt
|
|
389
|
+
}, null, 8, Ce)
|
|
390
|
+
], 8, Se)) : T("", !0)
|
|
314
391
|
]));
|
|
315
392
|
}
|
|
316
|
-
}),
|
|
317
|
-
const
|
|
318
|
-
for (const [
|
|
319
|
-
|
|
320
|
-
return
|
|
321
|
-
},
|
|
393
|
+
}), Te = (e, n) => {
|
|
394
|
+
const t = e.__vccOpts || e;
|
|
395
|
+
for (const [s, r] of n)
|
|
396
|
+
t[s] = r;
|
|
397
|
+
return t;
|
|
398
|
+
}, Ae = /* @__PURE__ */ Te(xe, [["__scopeId", "data-v-dc651545"]]);
|
|
322
399
|
export {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
400
|
+
Ae as SeerChat,
|
|
401
|
+
Ie as SeerChatPlugin,
|
|
402
|
+
X as createSeerClient,
|
|
403
|
+
ne as linkAnchorLabel,
|
|
404
|
+
ae as toContentLines,
|
|
405
|
+
se as toContentSegments,
|
|
406
|
+
oe as useSeerChat
|
|
327
407
|
};
|