@seer-ai/seer-vue 0.1.0-beta.3 → 0.1.0-beta.5
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 +19 -1
- package/dist/client.d.ts +2 -0
- package/dist/components/SeerChat.vue.d.ts +2 -2
- package/dist/content.d.ts +11 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +229 -176
- package/package.json +3 -2
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,19 @@ 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
|
+
<span v-else>{{ seg.value }}</span>
|
|
69
|
+
</template>
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
</template>
|
|
55
73
|
```
|
|
56
74
|
|
|
57
75
|
## Security
|
package/dist/client.d.ts
CHANGED
|
@@ -7,8 +7,10 @@ export interface SeerChatMessage {
|
|
|
7
7
|
export interface SeerClientOptions {
|
|
8
8
|
endpoint: string;
|
|
9
9
|
sessionEndpoint?: string;
|
|
10
|
+
challengeTokenProvider?: () => Promise<string>;
|
|
10
11
|
model?: string;
|
|
11
12
|
fetcher?: typeof fetch;
|
|
13
|
+
now?: () => number;
|
|
12
14
|
}
|
|
13
15
|
export interface SeerChatRequest {
|
|
14
16
|
messages: SeerChatMessage[];
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { SeerClientOptions } from
|
|
1
|
+
import type { SeerClientOptions } from "../client";
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
endpoint?: string;
|
|
4
4
|
model?: string;
|
|
5
5
|
stream?: boolean;
|
|
6
|
-
fetcher?: SeerClientOptions[
|
|
6
|
+
fetcher?: SeerClientOptions["fetcher"];
|
|
7
7
|
title?: string;
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ContentSegment = {
|
|
2
|
+
kind: 'text';
|
|
3
|
+
value: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'link';
|
|
6
|
+
href: string;
|
|
7
|
+
label: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function linkAnchorLabel(prefix: string): string | null;
|
|
10
|
+
export declare function toContentSegments(content: string): ContentSegment[];
|
|
11
|
+
export declare function toContentLines(content: string): ContentSegment[][];
|
package/dist/index.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.seer-chat[data-v-
|
|
1
|
+
.seer-chat[data-v-f2b8b91f]{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-f2b8b91f]{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-f2b8b91f]{margin:0;font-size:22px;letter-spacing:-.03em}.seer-chat__header p[data-v-f2b8b91f],.seer-chat__empty[data-v-f2b8b91f],.seer-chat__status[data-v-f2b8b91f]{margin:4px 0 0;color:#5f6f89;font-size:14px}.seer-chat__header p[data-v-f2b8b91f]{max-width:34ch;color:#ffffffb8}.seer-chat__messages[data-v-f2b8b91f]{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-f2b8b91f]{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-f2b8b91f]{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-f2b8b91f]{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-f2b8b91f]{font-size:12px;font-weight:700;opacity:.75}.seer-chat__body[data-v-f2b8b91f]{display:flex;flex-direction:column}.seer-chat__line[data-v-f2b8b91f]{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}.seer-chat__line[data-v-f2b8b91f]:not(:last-child){margin-bottom:5px}.seer-chat__line[data-v-f2b8b91f]:empty{height:7px}.seer-chat__link[data-v-f2b8b91f]{color:#1f5eff;font-weight:600;text-decoration:underline;text-underline-offset:2px;overflow-wrap:anywhere}.seer-chat__link[data-v-f2b8b91f]:hover{color:#173fb3}.seer-chat__message--user .seer-chat__link[data-v-f2b8b91f]{color:#fff}.seer-chat__error[data-v-f2b8b91f],.seer-chat__handoff[data-v-f2b8b91f]{margin:0 18px;padding:10px 12px;border-radius:14px}.seer-chat__error[data-v-f2b8b91f]{color:#8a1f11;background:#fff0ed}.seer-chat__handoff[data-v-f2b8b91f]{color:#704600;background:#fff7db}.seer-chat__form[data-v-f2b8b91f]{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-f2b8b91f]{font-size:13px;font-weight:700}.seer-chat textarea[data-v-f2b8b91f]{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-f2b8b91f]{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-f2b8b91f]:not(:disabled):hover{transform:translateY(-1px)}.seer-chat button[data-v-f2b8b91f]:disabled{cursor:not-allowed;opacity:.5}.seer-chat button[data-v-f2b8b91f]:focus-visible,.seer-chat textarea[data-v-f2b8b91f]:focus-visible{border-color:#1f5eff6b;box-shadow:0 0 0 4px #1f5eff21}.seer-chat__clear-button[data-v-f2b8b91f]{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-f2b8b91f]:not(:disabled):hover{color:#fff;background:#ffffff1a;box-shadow:none}.seer-chat__stop-button[data-v-f2b8b91f]{padding:8px 11px;color:#6c3b12;font-size:12px;background:#fff1df;border-radius:12px}.seer-chat__send-button[data-v-f2b8b91f]{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-f2b8b91f]:not(:disabled):hover{box-shadow:0 14px 28px #1f5eff47}.seer-chat__hint[data-v-f2b8b91f]{margin-right:auto;color:#7a879a;font-size:12px}.seer-chat__composer-footer[data-v-f2b8b91f]{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,190 +1,217 @@
|
|
|
1
|
-
import { hasInjectionContext as
|
|
2
|
-
function
|
|
3
|
-
const
|
|
4
|
-
let
|
|
1
|
+
import { hasInjectionContext as N, inject as q, ref as w, defineComponent as L, computed as M, openBlock as v, createElementBlock as m, createElementVNode as u, toDisplayString as C, unref as _, createCommentVNode as E, Fragment as I, renderList as R, normalizeClass as B, withModifiers as O, withDirectives as K, withKeys as D, vModelText as F } from "vue";
|
|
2
|
+
function U(e) {
|
|
3
|
+
const n = e.fetcher ?? fetch, t = e.model ?? "seer", a = e.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
4
|
+
let l, r = 0;
|
|
5
5
|
return {
|
|
6
|
-
async chat(
|
|
7
|
-
const
|
|
6
|
+
async chat(s, c = {}) {
|
|
7
|
+
const f = s.stream ?? !1, h = await d(), p = {
|
|
8
8
|
"Content-Type": "application/json"
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
const
|
|
10
|
+
h && (p.Authorization = `Bearer ${h}`);
|
|
11
|
+
const g = await n(e.endpoint, {
|
|
12
12
|
method: "POST",
|
|
13
|
-
headers:
|
|
14
|
-
signal:
|
|
13
|
+
headers: p,
|
|
14
|
+
signal: c.signal,
|
|
15
15
|
body: JSON.stringify({
|
|
16
|
-
model:
|
|
17
|
-
messages:
|
|
18
|
-
stream:
|
|
19
|
-
conversation_id:
|
|
20
|
-
metadata:
|
|
16
|
+
model: s.model ?? t,
|
|
17
|
+
messages: s.messages,
|
|
18
|
+
stream: f,
|
|
19
|
+
conversation_id: s.conversationId,
|
|
20
|
+
metadata: s.metadata
|
|
21
21
|
})
|
|
22
22
|
});
|
|
23
|
-
if (!
|
|
24
|
-
throw new Error(await j(
|
|
25
|
-
if (
|
|
26
|
-
return
|
|
27
|
-
const
|
|
28
|
-
return
|
|
23
|
+
if (!g.ok)
|
|
24
|
+
throw new Error(await j(g));
|
|
25
|
+
if (f)
|
|
26
|
+
return V(g, c.onDelta);
|
|
27
|
+
const b = await g.json();
|
|
28
|
+
return P(b);
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
async function
|
|
32
|
-
if (!
|
|
31
|
+
async function d() {
|
|
32
|
+
if (!e.sessionEndpoint)
|
|
33
33
|
return;
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
return
|
|
37
|
-
const
|
|
38
|
-
method: "GET"
|
|
34
|
+
const s = a();
|
|
35
|
+
if (l && r - 30 > s)
|
|
36
|
+
return l;
|
|
37
|
+
const c = await n(e.sessionEndpoint, {
|
|
38
|
+
method: "GET",
|
|
39
|
+
headers: await o()
|
|
39
40
|
});
|
|
40
|
-
if (!
|
|
41
|
-
throw new Error(await j(
|
|
42
|
-
const
|
|
43
|
-
return
|
|
41
|
+
if (!c.ok)
|
|
42
|
+
throw new Error(await j(c));
|
|
43
|
+
const f = await c.json();
|
|
44
|
+
return l = f.token, r = f.expires_at, l;
|
|
45
|
+
}
|
|
46
|
+
async function o() {
|
|
47
|
+
const s = {};
|
|
48
|
+
return e.challengeTokenProvider && (s["X-Seer-Challenge-Token"] = await e.challengeTokenProvider()), s;
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
|
-
function
|
|
47
|
-
var
|
|
48
|
-
const
|
|
51
|
+
function P(e) {
|
|
52
|
+
var t, a, l;
|
|
53
|
+
const n = (t = e.choices) == null ? void 0 : t[0];
|
|
49
54
|
return {
|
|
50
|
-
content: ((
|
|
51
|
-
conversationId:
|
|
52
|
-
citations:
|
|
53
|
-
handoffRequested:
|
|
54
|
-
finishReason:
|
|
55
|
+
content: ((a = n == null ? void 0 : n.message) == null ? void 0 : a.content) ?? ((l = n == null ? void 0 : n.delta) == null ? void 0 : l.content) ?? "",
|
|
56
|
+
conversationId: e.conversation_id,
|
|
57
|
+
citations: e.citations ?? [],
|
|
58
|
+
handoffRequested: e.handoff_requested ?? !1,
|
|
59
|
+
finishReason: n == null ? void 0 : n.finish_reason
|
|
55
60
|
};
|
|
56
61
|
}
|
|
57
|
-
async function
|
|
58
|
-
var
|
|
59
|
-
if (!
|
|
60
|
-
return
|
|
61
|
-
const
|
|
62
|
-
let
|
|
62
|
+
async function V(e, n) {
|
|
63
|
+
var f, h, p;
|
|
64
|
+
if (!e.body)
|
|
65
|
+
return P({});
|
|
66
|
+
const t = e.body.getReader(), a = new TextDecoder();
|
|
67
|
+
let l = "", r = "", d, o = [], s = !1, c;
|
|
63
68
|
for (; ; ) {
|
|
64
|
-
const { done:
|
|
65
|
-
if (
|
|
69
|
+
const { done: g, value: b } = await t.read();
|
|
70
|
+
if (g)
|
|
66
71
|
break;
|
|
67
|
-
|
|
68
|
-
const
|
|
72
|
+
l += a.decode(b, { stream: !0 });
|
|
73
|
+
const T = l.split(`
|
|
69
74
|
|
|
70
75
|
`);
|
|
71
|
-
|
|
72
|
-
for (const
|
|
73
|
-
const
|
|
74
|
-
if (!
|
|
76
|
+
l = T.pop() ?? "";
|
|
77
|
+
for (const x of T) {
|
|
78
|
+
const S = z(x);
|
|
79
|
+
if (!S || S === "[DONE]")
|
|
75
80
|
continue;
|
|
76
|
-
const
|
|
77
|
-
|
|
81
|
+
const y = JSON.parse(S), i = (f = y.choices) == null ? void 0 : f[0], k = ((h = i == null ? void 0 : i.delta) == null ? void 0 : h.content) ?? ((p = i == null ? void 0 : i.message) == null ? void 0 : p.content) ?? "";
|
|
82
|
+
k && (r += k, n == null || n(k)), d = y.conversation_id ?? d, o = y.citations ?? o, s = y.handoff_requested ?? s, c = (i == null ? void 0 : i.finish_reason) ?? c;
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
return {
|
|
81
|
-
content:
|
|
82
|
-
conversationId:
|
|
83
|
-
citations:
|
|
84
|
-
handoffRequested:
|
|
85
|
-
finishReason:
|
|
86
|
+
content: r,
|
|
87
|
+
conversationId: d,
|
|
88
|
+
citations: o,
|
|
89
|
+
handoffRequested: s,
|
|
90
|
+
finishReason: c
|
|
86
91
|
};
|
|
87
92
|
}
|
|
88
|
-
function
|
|
89
|
-
const
|
|
90
|
-
`).map((
|
|
91
|
-
return
|
|
93
|
+
function z(e) {
|
|
94
|
+
const n = e.split(`
|
|
95
|
+
`).map((t) => t.trim()).filter((t) => t.startsWith("data:")).map((t) => t.slice(5).trim());
|
|
96
|
+
return n.length === 0 ? null : n.join(`
|
|
92
97
|
`);
|
|
93
98
|
}
|
|
94
|
-
async function j(
|
|
95
|
-
let
|
|
99
|
+
async function j(e) {
|
|
100
|
+
let n = e.statusText || "unknown error";
|
|
96
101
|
try {
|
|
97
|
-
const
|
|
98
|
-
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);
|
|
99
104
|
} catch {
|
|
100
|
-
const
|
|
101
|
-
|
|
105
|
+
const t = await e.text().catch(() => "");
|
|
106
|
+
t && (n = t);
|
|
102
107
|
}
|
|
103
|
-
return `seer chat request failed with status ${
|
|
108
|
+
return `seer chat request failed with status ${e.status}: ${n}`;
|
|
109
|
+
}
|
|
110
|
+
const A = /https?:\/\/[^\s)]+/g;
|
|
111
|
+
function J(e) {
|
|
112
|
+
return /标签|箱唛/.test(e) ? "点击下载箱唛" : /入库单|交接单/.test(e) ? "点击下载入库单" : /面单/.test(e) ? "点击下载面单" : /截图/.test(e) ? "点击查看报价截图" : /下载链接|labelUrl|下载/.test(e) ? "点击下载" : /链接/.test(e) ? "点击查看" : null;
|
|
113
|
+
}
|
|
114
|
+
function G(e) {
|
|
115
|
+
const n = [];
|
|
116
|
+
A.lastIndex = 0;
|
|
117
|
+
let t = 0, a;
|
|
118
|
+
for (; (a = A.exec(e)) !== null; ) {
|
|
119
|
+
const l = a[0], r = a.index;
|
|
120
|
+
r > t && n.push({ kind: "text", value: e.slice(t, r) });
|
|
121
|
+
const d = e.lastIndexOf(`
|
|
122
|
+
`, r - 1) + 1, o = J(e.slice(d, r)) ?? l;
|
|
123
|
+
n.push({ kind: "link", href: l, label: o }), t = r + l.length;
|
|
124
|
+
}
|
|
125
|
+
return t < e.length && n.push({ kind: "text", value: e.slice(t) }), n;
|
|
126
|
+
}
|
|
127
|
+
function H(e) {
|
|
128
|
+
return e.split(`
|
|
129
|
+
`).map((n) => G(n));
|
|
104
130
|
}
|
|
105
|
-
const
|
|
106
|
-
install(
|
|
107
|
-
|
|
131
|
+
const $ = Symbol("seer-chat-options"), he = {
|
|
132
|
+
install(e, n) {
|
|
133
|
+
e.provide($, n);
|
|
108
134
|
}
|
|
109
135
|
};
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
if (!
|
|
136
|
+
function W(e = {}) {
|
|
137
|
+
const t = { ...N() ? q($, void 0) : void 0, ...e };
|
|
138
|
+
if (!t.endpoint)
|
|
113
139
|
throw new Error("Seer chat endpoint is required. Pass it to app.use() or useSeerChat().");
|
|
114
|
-
const
|
|
115
|
-
endpoint:
|
|
116
|
-
sessionEndpoint:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
140
|
+
const a = {
|
|
141
|
+
endpoint: t.endpoint,
|
|
142
|
+
sessionEndpoint: t.sessionEndpoint,
|
|
143
|
+
challengeTokenProvider: t.challengeTokenProvider,
|
|
144
|
+
model: t.model,
|
|
145
|
+
fetcher: t.fetcher
|
|
146
|
+
}, l = U(a), r = w([]), d = w(!1), o = w(null), s = w(), c = w([]), f = w(!1);
|
|
147
|
+
let h = null;
|
|
148
|
+
async function p(T) {
|
|
149
|
+
const x = T.trim();
|
|
150
|
+
if (!x || d.value)
|
|
124
151
|
return;
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
152
|
+
const S = { role: "user", content: x };
|
|
153
|
+
r.value = [...r.value, S], d.value = !0, o.value = null, h = new AbortController();
|
|
154
|
+
const y = { role: "assistant", content: "" };
|
|
155
|
+
t.stream && (r.value = [...r.value, y]);
|
|
129
156
|
try {
|
|
130
|
-
const i = await
|
|
157
|
+
const i = await l.chat(
|
|
131
158
|
{
|
|
132
|
-
messages:
|
|
133
|
-
stream:
|
|
134
|
-
conversationId:
|
|
159
|
+
messages: r.value.filter((k) => k.content),
|
|
160
|
+
stream: t.stream ?? !1,
|
|
161
|
+
conversationId: s.value
|
|
135
162
|
},
|
|
136
163
|
{
|
|
137
|
-
signal:
|
|
138
|
-
onDelta(
|
|
139
|
-
|
|
164
|
+
signal: h.signal,
|
|
165
|
+
onDelta(k) {
|
|
166
|
+
y.content += k, r.value = [...r.value];
|
|
140
167
|
}
|
|
141
168
|
}
|
|
142
169
|
);
|
|
143
|
-
|
|
170
|
+
t.stream || (r.value = [...r.value, { role: "assistant", content: i.content }]), s.value = i.conversationId, c.value = i.citations, f.value = i.handoffRequested;
|
|
144
171
|
} catch (i) {
|
|
145
|
-
i.name !== "AbortError" && (
|
|
172
|
+
i.name !== "AbortError" && (o.value = i instanceof Error ? i.message : String(i));
|
|
146
173
|
} finally {
|
|
147
|
-
|
|
174
|
+
d.value = !1, h = null;
|
|
148
175
|
}
|
|
149
176
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
177
|
+
function g() {
|
|
178
|
+
r.value = [], o.value = null, s.value = void 0, c.value = [], f.value = !1;
|
|
152
179
|
}
|
|
153
|
-
function
|
|
154
|
-
|
|
180
|
+
function b() {
|
|
181
|
+
h == null || h.abort();
|
|
155
182
|
}
|
|
156
183
|
return {
|
|
157
|
-
messages:
|
|
158
|
-
loading:
|
|
159
|
-
error:
|
|
160
|
-
conversationId:
|
|
161
|
-
citations:
|
|
162
|
-
handoffRequested:
|
|
163
|
-
send:
|
|
164
|
-
reset:
|
|
165
|
-
abort:
|
|
184
|
+
messages: r,
|
|
185
|
+
loading: d,
|
|
186
|
+
error: o,
|
|
187
|
+
conversationId: s,
|
|
188
|
+
citations: c,
|
|
189
|
+
handoffRequested: f,
|
|
190
|
+
send: p,
|
|
191
|
+
reset: g,
|
|
192
|
+
abort: b
|
|
166
193
|
};
|
|
167
194
|
}
|
|
168
|
-
const
|
|
195
|
+
const X = {
|
|
169
196
|
class: "seer-chat",
|
|
170
197
|
"aria-label": "多发货 AI 客服聊天"
|
|
171
|
-
},
|
|
198
|
+
}, Q = { class: "seer-chat__header" }, Y = {
|
|
172
199
|
class: "seer-chat__messages",
|
|
173
200
|
"aria-live": "polite"
|
|
174
|
-
},
|
|
201
|
+
}, Z = {
|
|
175
202
|
key: 0,
|
|
176
203
|
class: "seer-chat__empty"
|
|
177
|
-
},
|
|
204
|
+
}, ee = { class: "seer-chat__role" }, te = { class: "seer-chat__body" }, ne = ["href"], se = { key: 1 }, ae = {
|
|
178
205
|
key: 0,
|
|
179
206
|
class: "seer-chat__status"
|
|
180
|
-
},
|
|
207
|
+
}, oe = {
|
|
181
208
|
key: 1,
|
|
182
209
|
class: "seer-chat__error",
|
|
183
210
|
role: "alert"
|
|
184
|
-
},
|
|
211
|
+
}, re = {
|
|
185
212
|
key: 2,
|
|
186
213
|
class: "seer-chat__handoff"
|
|
187
|
-
},
|
|
214
|
+
}, le = ["placeholder", "disabled", "onKeydown"], ie = { class: "seer-chat__composer-footer" }, ce = ["disabled"], ue = /* @__PURE__ */ L({
|
|
188
215
|
__name: "SeerChat",
|
|
189
216
|
props: {
|
|
190
217
|
endpoint: {},
|
|
@@ -194,85 +221,111 @@ const G = {
|
|
|
194
221
|
title: { default: "多发货 AI 客服" },
|
|
195
222
|
placeholder: { default: "输入你的问题,例如:美国海派怎么报价?" }
|
|
196
223
|
},
|
|
197
|
-
setup(
|
|
198
|
-
const
|
|
199
|
-
endpoint:
|
|
200
|
-
model:
|
|
201
|
-
stream:
|
|
202
|
-
fetcher:
|
|
203
|
-
}),
|
|
204
|
-
|
|
205
|
-
|
|
224
|
+
setup(e) {
|
|
225
|
+
const n = e, t = w(""), a = W({
|
|
226
|
+
endpoint: n.endpoint,
|
|
227
|
+
model: n.model,
|
|
228
|
+
stream: n.stream,
|
|
229
|
+
fetcher: n.fetcher
|
|
230
|
+
}), l = M(
|
|
231
|
+
() => t.value.trim().length > 0 && !a.loading.value
|
|
232
|
+
);
|
|
233
|
+
async function r() {
|
|
234
|
+
if (!l.value)
|
|
206
235
|
return;
|
|
207
|
-
const
|
|
208
|
-
|
|
236
|
+
const d = t.value;
|
|
237
|
+
t.value = "", await a.send(d);
|
|
209
238
|
}
|
|
210
|
-
return (
|
|
211
|
-
u("header",
|
|
239
|
+
return (d, o) => (v(), m("section", X, [
|
|
240
|
+
u("header", Q, [
|
|
212
241
|
u("div", null, [
|
|
213
|
-
u("h2", null,
|
|
214
|
-
|
|
242
|
+
u("h2", null, C(e.title), 1),
|
|
243
|
+
o[3] || (o[3] = u("p", null, "可咨询渠道报价、时效、下单资料、入仓和售后问题。", -1))
|
|
215
244
|
]),
|
|
216
245
|
u("button", {
|
|
217
246
|
type: "button",
|
|
218
|
-
class: "seer-
|
|
219
|
-
onClick:
|
|
220
|
-
}, "
|
|
247
|
+
class: "seer-chat__clear-button",
|
|
248
|
+
onClick: o[0] || (o[0] = (s) => _(a).reset())
|
|
249
|
+
}, " 清空对话 ")
|
|
221
250
|
]),
|
|
222
|
-
u("div",
|
|
223
|
-
|
|
224
|
-
(v(!0),
|
|
225
|
-
key: `${
|
|
226
|
-
class: B(["seer-chat__message", `seer-chat__message--${
|
|
251
|
+
u("div", Y, [
|
|
252
|
+
_(a).messages.value.length === 0 ? (v(), m("p", Z, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : E("", !0),
|
|
253
|
+
(v(!0), m(I, null, R(_(a).messages.value, (s, c) => (v(), m("article", {
|
|
254
|
+
key: `${s.role}-${c}`,
|
|
255
|
+
class: B(["seer-chat__message", `seer-chat__message--${s.role}`])
|
|
227
256
|
}, [
|
|
228
|
-
u("span",
|
|
229
|
-
u("
|
|
257
|
+
u("span", ee, C(s.role === "user" ? "你" : "AI 客服"), 1),
|
|
258
|
+
u("div", te, [
|
|
259
|
+
(v(!0), m(I, null, R(_(H)(s.content), (f, h) => (v(), m("p", {
|
|
260
|
+
key: h,
|
|
261
|
+
class: "seer-chat__line"
|
|
262
|
+
}, [
|
|
263
|
+
(v(!0), m(I, null, R(f, (p, g) => (v(), m(I, { key: g }, [
|
|
264
|
+
p.kind === "link" ? (v(), m("a", {
|
|
265
|
+
key: 0,
|
|
266
|
+
class: "seer-chat__link",
|
|
267
|
+
href: p.href,
|
|
268
|
+
target: "_blank",
|
|
269
|
+
rel: "noopener noreferrer"
|
|
270
|
+
}, C(p.label), 9, ne)) : (v(), m("span", se, C(p.value), 1))
|
|
271
|
+
], 64))), 128))
|
|
272
|
+
]))), 128))
|
|
273
|
+
])
|
|
230
274
|
], 2))), 128))
|
|
231
275
|
]),
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
276
|
+
_(a).loading.value ? (v(), m("p", ae, "正在回复...")) : E("", !0),
|
|
277
|
+
_(a).error.value ? (v(), m("p", oe, C(_(a).error.value), 1)) : E("", !0),
|
|
278
|
+
_(a).handoffRequested.value ? (v(), m("p", re, " 建议转人工:请留下联系方式或等待客服接入。 ")) : E("", !0),
|
|
235
279
|
u("form", {
|
|
236
280
|
class: "seer-chat__form",
|
|
237
|
-
onSubmit:
|
|
281
|
+
onSubmit: O(r, ["prevent"])
|
|
238
282
|
}, [
|
|
239
|
-
|
|
283
|
+
o[6] || (o[6] = u("label", {
|
|
240
284
|
class: "seer-chat__label",
|
|
241
285
|
for: "seer-chat-input"
|
|
242
286
|
}, "问题", -1)),
|
|
243
|
-
|
|
287
|
+
K(u("textarea", {
|
|
244
288
|
id: "seer-chat-input",
|
|
245
|
-
"onUpdate:modelValue":
|
|
246
|
-
placeholder:
|
|
247
|
-
disabled:
|
|
248
|
-
rows: "3"
|
|
249
|
-
|
|
250
|
-
|
|
289
|
+
"onUpdate:modelValue": o[1] || (o[1] = (s) => t.value = s),
|
|
290
|
+
placeholder: e.placeholder,
|
|
291
|
+
disabled: _(a).loading.value,
|
|
292
|
+
rows: "3",
|
|
293
|
+
onKeydown: D(O(r, ["exact", "prevent"]), ["enter"])
|
|
294
|
+
}, null, 40, le), [
|
|
295
|
+
[F, t.value]
|
|
251
296
|
]),
|
|
252
|
-
u("div",
|
|
253
|
-
|
|
297
|
+
u("div", ie, [
|
|
298
|
+
o[5] || (o[5] = u("span", { class: "seer-chat__hint" }, "Enter 发送 · Shift+Enter 换行", -1)),
|
|
299
|
+
_(a).loading.value ? (v(), m("button", {
|
|
254
300
|
key: 0,
|
|
255
301
|
type: "button",
|
|
256
|
-
class: "seer-
|
|
257
|
-
onClick:
|
|
258
|
-
}, " 停止 ")) :
|
|
302
|
+
class: "seer-chat__stop-button",
|
|
303
|
+
onClick: o[2] || (o[2] = (s) => _(a).abort())
|
|
304
|
+
}, " 停止 ")) : E("", !0),
|
|
259
305
|
u("button", {
|
|
306
|
+
class: "seer-chat__send-button",
|
|
260
307
|
type: "submit",
|
|
261
|
-
disabled: !
|
|
262
|
-
},
|
|
308
|
+
disabled: !l.value
|
|
309
|
+
}, [...o[4] || (o[4] = [
|
|
310
|
+
u("span", null, "发送", -1),
|
|
311
|
+
u("span", { "aria-hidden": "true" }, "↵", -1)
|
|
312
|
+
])], 8, ce)
|
|
263
313
|
])
|
|
264
314
|
], 32)
|
|
265
315
|
]));
|
|
266
316
|
}
|
|
267
|
-
}),
|
|
268
|
-
const
|
|
269
|
-
for (const [
|
|
270
|
-
|
|
271
|
-
return
|
|
272
|
-
},
|
|
317
|
+
}), de = (e, n) => {
|
|
318
|
+
const t = e.__vccOpts || e;
|
|
319
|
+
for (const [a, l] of n)
|
|
320
|
+
t[a] = l;
|
|
321
|
+
return t;
|
|
322
|
+
}, ve = /* @__PURE__ */ de(ue, [["__scopeId", "data-v-f2b8b91f"]]);
|
|
273
323
|
export {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
324
|
+
ve as SeerChat,
|
|
325
|
+
he as SeerChatPlugin,
|
|
326
|
+
U as createSeerClient,
|
|
327
|
+
J as linkAnchorLabel,
|
|
328
|
+
H as toContentLines,
|
|
329
|
+
G as toContentSegments,
|
|
330
|
+
W as useSeerChat
|
|
278
331
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seer-ai/seer-vue",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.5",
|
|
4
4
|
"description": "Vue chat plugin for the seer OpenAI-compatible API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"test": "vitest run",
|
|
24
24
|
"typecheck": "vue-tsc --noEmit",
|
|
25
|
-
"build": "vite build && vue-tsc -p tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist"
|
|
25
|
+
"build": "vite build && vue-tsc -p tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist",
|
|
26
|
+
"prepublishOnly": "npm run build"
|
|
26
27
|
},
|
|
27
28
|
"peerDependencies": {
|
|
28
29
|
"vue": "^3.5.0"
|