@seer-ai/seer-vue 0.1.0-beta.1 → 0.1.0-beta.2

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 CHANGED
@@ -17,7 +17,8 @@ import App from './App.vue'
17
17
 
18
18
  createApp(App)
19
19
  .use(SeerChatPlugin, {
20
- endpoint: '/api/seer-chat',
20
+ sessionEndpoint: 'https://seer.example.com/v1/browser/session',
21
+ endpoint: 'https://seer.example.com/v1/browser/chat/completions',
21
22
  model: 'seer',
22
23
  stream: false,
23
24
  })
@@ -55,4 +56,4 @@ async function submit() {
55
56
 
56
57
  ## Security
57
58
 
58
- Do not put long-lived seer API keys in browser code or `VITE_*` variables. Browser code should call an application-owned proxy such as `/api/seer-chat`; that server-side proxy forwards to seer `/v1/chat/completions` and injects the Bearer token.
59
+ Do not put long-lived seer API keys in browser code or `VITE_*` variables. Browser code should call seer's browser session endpoint, receive a short-lived token, and use that token for `/v1/browser/chat/completions`.
package/dist/client.d.ts CHANGED
@@ -6,6 +6,7 @@ export interface SeerChatMessage {
6
6
  }
7
7
  export interface SeerClientOptions {
8
8
  endpoint: string;
9
+ sessionEndpoint?: string;
9
10
  model?: string;
10
11
  fetcher?: typeof fetch;
11
12
  }
package/dist/index.js CHANGED
@@ -1,71 +1,88 @@
1
- import { hasInjectionContext as q, inject as A, ref as g, defineComponent as N, computed as T, openBlock as f, createElementBlock as h, createElementVNode as u, toDisplayString as S, unref as d, createCommentVNode as b, Fragment as R, renderList as j, normalizeClass as M, withModifiers as B, withDirectives as F, vModelText as P } from "vue";
1
+ import { hasInjectionContext as A, inject as q, ref as y, defineComponent as M, computed as N, openBlock as m, createElementBlock as v, createElementVNode as u, toDisplayString as S, unref as h, createCommentVNode as b, Fragment as j, renderList as O, normalizeClass as B, withModifiers as D, withDirectives as F, vModelText as P } from "vue";
2
2
  function V(n) {
3
3
  const t = n.fetcher ?? fetch, e = n.model ?? "seer";
4
+ let s, c = 0;
4
5
  return {
5
- async chat(s, i = {}) {
6
- const r = s.stream ?? !1, c = await t(n.endpoint, {
6
+ async chat(r, a = {}) {
7
+ const o = r.stream ?? !1, f = await l(), p = {
8
+ "Content-Type": "application/json"
9
+ };
10
+ f && (p.Authorization = `Bearer ${f}`);
11
+ const d = await t(n.endpoint, {
7
12
  method: "POST",
8
- headers: {
9
- "Content-Type": "application/json"
10
- },
11
- signal: i.signal,
13
+ headers: p,
14
+ signal: a.signal,
12
15
  body: JSON.stringify({
13
- model: s.model ?? e,
14
- messages: s.messages,
15
- stream: r,
16
- conversation_id: s.conversationId,
17
- metadata: s.metadata
16
+ model: r.model ?? e,
17
+ messages: r.messages,
18
+ stream: o,
19
+ conversation_id: r.conversationId,
20
+ metadata: r.metadata
18
21
  })
19
22
  });
20
- if (!c.ok)
21
- throw new Error(await L(c));
22
- if (r)
23
- return D(c, i.onDelta);
24
- const a = await c.json();
25
- return E(a);
23
+ if (!d.ok)
24
+ throw new Error(await R(d));
25
+ if (o)
26
+ return z(d, a.onDelta);
27
+ const w = await d.json();
28
+ return T(w);
26
29
  }
27
30
  };
31
+ async function l() {
32
+ if (!n.sessionEndpoint)
33
+ return;
34
+ const r = Math.floor(Date.now() / 1e3);
35
+ if (s && c - 30 > r)
36
+ return s;
37
+ const a = await t(n.sessionEndpoint, {
38
+ method: "GET"
39
+ });
40
+ if (!a.ok)
41
+ throw new Error(await R(a));
42
+ const o = await a.json();
43
+ return s = o.token, c = o.expires_at, s;
44
+ }
28
45
  }
29
- function E(n) {
30
- var e, s, i;
46
+ function T(n) {
47
+ var e, s, c;
31
48
  const t = (e = n.choices) == null ? void 0 : e[0];
32
49
  return {
33
- content: ((s = t == null ? void 0 : t.message) == null ? void 0 : s.content) ?? ((i = t == null ? void 0 : t.delta) == null ? void 0 : i.content) ?? "",
50
+ content: ((s = t == null ? void 0 : t.message) == null ? void 0 : s.content) ?? ((c = t == null ? void 0 : t.delta) == null ? void 0 : c.content) ?? "",
34
51
  conversationId: n.conversation_id,
35
52
  citations: n.citations ?? [],
36
53
  handoffRequested: n.handoff_requested ?? !1,
37
54
  finishReason: t == null ? void 0 : t.finish_reason
38
55
  };
39
56
  }
40
- async function D(n, t) {
41
- var y, v, C;
57
+ async function z(n, t) {
58
+ var p, d, w;
42
59
  if (!n.body)
43
- return E({});
60
+ return T({});
44
61
  const e = n.body.getReader(), s = new TextDecoder();
45
- let i = "", r = "", c, a = [], o = !1, m;
62
+ let c = "", l = "", r, a = [], o = !1, f;
46
63
  for (; ; ) {
47
- const { done: x, value: O } = await e.read();
64
+ const { done: x, value: I } = await e.read();
48
65
  if (x)
49
66
  break;
50
- i += s.decode(O, { stream: !0 });
51
- const k = i.split(`
67
+ c += s.decode(I, { stream: !0 });
68
+ const C = c.split(`
52
69
 
53
70
  `);
54
- i = k.pop() ?? "";
55
- for (const I of k) {
56
- const w = J(I);
57
- if (!w || w === "[DONE]")
71
+ c = C.pop() ?? "";
72
+ for (const E of C) {
73
+ const k = J(E);
74
+ if (!k || k === "[DONE]")
58
75
  continue;
59
- const p = JSON.parse(w), l = (y = p.choices) == null ? void 0 : y[0], _ = ((v = l == null ? void 0 : l.delta) == null ? void 0 : v.content) ?? ((C = l == null ? void 0 : l.message) == null ? void 0 : C.content) ?? "";
60
- _ && (r += _, t == null || t(_)), c = p.conversation_id ?? c, a = p.citations ?? a, o = p.handoff_requested ?? o, m = (l == null ? void 0 : l.finish_reason) ?? m;
76
+ const _ = JSON.parse(k), i = (p = _.choices) == null ? void 0 : p[0], g = ((d = i == null ? void 0 : i.delta) == null ? void 0 : d.content) ?? ((w = i == null ? void 0 : i.message) == null ? void 0 : w.content) ?? "";
77
+ g && (l += g, t == null || t(g)), r = _.conversation_id ?? r, a = _.citations ?? a, o = _.handoff_requested ?? o, f = (i == null ? void 0 : i.finish_reason) ?? f;
61
78
  }
62
79
  }
63
80
  return {
64
- content: r,
65
- conversationId: c,
81
+ content: l,
82
+ conversationId: r,
66
83
  citations: a,
67
84
  handoffRequested: o,
68
- finishReason: m
85
+ finishReason: f
69
86
  };
70
87
  }
71
88
  function J(n) {
@@ -74,7 +91,7 @@ function J(n) {
74
91
  return t.length === 0 ? null : t.join(`
75
92
  `);
76
93
  }
77
- async function L(n) {
94
+ async function R(n) {
78
95
  let t = n.statusText || "unknown error";
79
96
  try {
80
97
  const e = await n.json();
@@ -90,70 +107,71 @@ const $ = Symbol("seer-chat-options"), re = {
90
107
  n.provide($, t);
91
108
  }
92
109
  };
93
- function z(n = {}) {
94
- const e = { ...q() ? A($, void 0) : void 0, ...n };
110
+ function L(n = {}) {
111
+ const e = { ...A() ? q($, void 0) : void 0, ...n };
95
112
  if (!e.endpoint)
96
113
  throw new Error("Seer chat endpoint is required. Pass it to app.use() or useSeerChat().");
97
114
  const s = {
98
115
  endpoint: e.endpoint,
116
+ sessionEndpoint: e.sessionEndpoint,
99
117
  model: e.model,
100
118
  fetcher: e.fetcher
101
- }, i = V(s), r = g([]), c = g(!1), a = g(null), o = g(), m = g([]), y = g(!1);
102
- let v = null;
103
- async function C(k) {
104
- const I = k.trim();
105
- if (!I || c.value)
119
+ }, c = V(s), l = y([]), r = y(!1), a = y(null), o = y(), f = y([]), p = y(!1);
120
+ let d = null;
121
+ async function w(C) {
122
+ const E = C.trim();
123
+ if (!E || r.value)
106
124
  return;
107
- const w = { role: "user", content: I };
108
- r.value = [...r.value, w], c.value = !0, a.value = null, v = new AbortController();
109
- const p = { role: "assistant", content: "" };
110
- e.stream && (r.value = [...r.value, p]);
125
+ const k = { role: "user", content: E };
126
+ l.value = [...l.value, k], r.value = !0, a.value = null, d = new AbortController();
127
+ const _ = { role: "assistant", content: "" };
128
+ e.stream && (l.value = [...l.value, _]);
111
129
  try {
112
- const l = await i.chat(
130
+ const i = await c.chat(
113
131
  {
114
- messages: r.value.filter((_) => _.content),
132
+ messages: l.value.filter((g) => g.content),
115
133
  stream: e.stream ?? !1,
116
134
  conversationId: o.value
117
135
  },
118
136
  {
119
- signal: v.signal,
120
- onDelta(_) {
121
- p.content += _, r.value = [...r.value];
137
+ signal: d.signal,
138
+ onDelta(g) {
139
+ _.content += g, l.value = [...l.value];
122
140
  }
123
141
  }
124
142
  );
125
- e.stream || (r.value = [...r.value, { role: "assistant", content: l.content }]), o.value = l.conversationId, m.value = l.citations, y.value = l.handoffRequested;
126
- } catch (l) {
127
- l.name !== "AbortError" && (a.value = l instanceof Error ? l.message : String(l));
143
+ e.stream || (l.value = [...l.value, { role: "assistant", content: i.content }]), o.value = i.conversationId, f.value = i.citations, p.value = i.handoffRequested;
144
+ } catch (i) {
145
+ i.name !== "AbortError" && (a.value = i instanceof Error ? i.message : String(i));
128
146
  } finally {
129
- c.value = !1, v = null;
147
+ r.value = !1, d = null;
130
148
  }
131
149
  }
132
150
  function x() {
133
- r.value = [], a.value = null, o.value = void 0, m.value = [], y.value = !1;
151
+ l.value = [], a.value = null, o.value = void 0, f.value = [], p.value = !1;
134
152
  }
135
- function O() {
136
- v == null || v.abort();
153
+ function I() {
154
+ d == null || d.abort();
137
155
  }
138
156
  return {
139
- messages: r,
140
- loading: c,
157
+ messages: l,
158
+ loading: r,
141
159
  error: a,
142
160
  conversationId: o,
143
- citations: m,
144
- handoffRequested: y,
145
- send: C,
161
+ citations: f,
162
+ handoffRequested: p,
163
+ send: w,
146
164
  reset: x,
147
- abort: O
165
+ abort: I
148
166
  };
149
167
  }
150
- const K = {
168
+ const G = {
151
169
  class: "seer-chat",
152
170
  "aria-label": "多发货 AI 客服聊天"
153
- }, U = { class: "seer-chat__header" }, W = {
171
+ }, K = { class: "seer-chat__header" }, U = {
154
172
  class: "seer-chat__messages",
155
173
  "aria-live": "polite"
156
- }, G = {
174
+ }, W = {
157
175
  key: 0,
158
176
  class: "seer-chat__empty"
159
177
  }, H = { class: "seer-chat__role" }, Q = {
@@ -169,7 +187,7 @@ const K = {
169
187
  }, Z = {
170
188
  key: 3,
171
189
  class: "seer-chat__citations"
172
- }, ee = ["placeholder", "disabled"], te = { class: "seer-chat__actions" }, ne = ["disabled"], se = /* @__PURE__ */ N({
190
+ }, ee = ["placeholder", "disabled"], te = { class: "seer-chat__actions" }, ne = ["disabled"], se = /* @__PURE__ */ M({
173
191
  __name: "SeerChat",
174
192
  props: {
175
193
  endpoint: {},
@@ -180,20 +198,20 @@ const K = {
180
198
  placeholder: { default: "输入你的问题,例如:美国海派怎么报价?" }
181
199
  },
182
200
  setup(n) {
183
- const t = n, e = g(""), s = z({
201
+ const t = n, e = y(""), s = L({
184
202
  endpoint: t.endpoint,
185
203
  model: t.model,
186
204
  stream: t.stream,
187
205
  fetcher: t.fetcher
188
- }), i = T(() => e.value.trim().length > 0 && !s.loading.value);
189
- async function r() {
190
- if (!i.value)
206
+ }), c = N(() => e.value.trim().length > 0 && !s.loading.value);
207
+ async function l() {
208
+ if (!c.value)
191
209
  return;
192
- const c = e.value;
193
- e.value = "", await s.send(c);
210
+ const r = e.value;
211
+ e.value = "", await s.send(r);
194
212
  }
195
- return (c, a) => (f(), h("section", K, [
196
- u("header", U, [
213
+ return (r, a) => (m(), v("section", G, [
214
+ u("header", K, [
197
215
  u("div", null, [
198
216
  u("h2", null, S(n.title), 1),
199
217
  a[3] || (a[3] = u("p", null, "可咨询渠道报价、时效、下单资料、入仓和售后问题。", -1))
@@ -201,31 +219,31 @@ const K = {
201
219
  u("button", {
202
220
  type: "button",
203
221
  class: "seer-chat__ghost-button",
204
- onClick: a[0] || (a[0] = (o) => d(s).reset())
222
+ onClick: a[0] || (a[0] = (o) => h(s).reset())
205
223
  }, "清空")
206
224
  ]),
207
- u("div", W, [
208
- d(s).messages.value.length === 0 ? (f(), h("p", G, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : b("", !0),
209
- (f(!0), h(R, null, j(d(s).messages.value, (o, m) => (f(), h("article", {
210
- key: `${o.role}-${m}`,
211
- class: M(["seer-chat__message", `seer-chat__message--${o.role}`])
225
+ u("div", U, [
226
+ h(s).messages.value.length === 0 ? (m(), v("p", W, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : b("", !0),
227
+ (m(!0), v(j, null, O(h(s).messages.value, (o, f) => (m(), v("article", {
228
+ key: `${o.role}-${f}`,
229
+ class: B(["seer-chat__message", `seer-chat__message--${o.role}`])
212
230
  }, [
213
231
  u("span", H, S(o.role === "user" ? "你" : "AI 客服"), 1),
214
232
  u("p", null, S(o.content), 1)
215
233
  ], 2))), 128))
216
234
  ]),
217
- d(s).loading.value ? (f(), h("p", Q, "正在回复...")) : b("", !0),
218
- d(s).error.value ? (f(), h("p", X, S(d(s).error.value), 1)) : b("", !0),
219
- d(s).handoffRequested.value ? (f(), h("p", Y, " 建议转人工:请留下联系方式或等待客服接入。 ")) : b("", !0),
220
- d(s).citations.value.length > 0 ? (f(), h("footer", Z, [
235
+ h(s).loading.value ? (m(), v("p", Q, "正在回复...")) : b("", !0),
236
+ h(s).error.value ? (m(), v("p", X, S(h(s).error.value), 1)) : b("", !0),
237
+ h(s).handoffRequested.value ? (m(), v("p", Y, " 建议转人工:请留下联系方式或等待客服接入。 ")) : b("", !0),
238
+ h(s).citations.value.length > 0 ? (m(), v("footer", Z, [
221
239
  a[4] || (a[4] = u("span", null, "参考资料", -1)),
222
240
  u("ul", null, [
223
- (f(!0), h(R, null, j(d(s).citations.value, (o) => (f(), h("li", { key: o }, S(o), 1))), 128))
241
+ (m(!0), v(j, null, O(h(s).citations.value, (o) => (m(), v("li", { key: o }, S(o), 1))), 128))
224
242
  ])
225
243
  ])) : b("", !0),
226
244
  u("form", {
227
245
  class: "seer-chat__form",
228
- onSubmit: B(r, ["prevent"])
246
+ onSubmit: D(l, ["prevent"])
229
247
  }, [
230
248
  a[5] || (a[5] = u("label", {
231
249
  class: "seer-chat__label",
@@ -235,21 +253,21 @@ const K = {
235
253
  id: "seer-chat-input",
236
254
  "onUpdate:modelValue": a[1] || (a[1] = (o) => e.value = o),
237
255
  placeholder: n.placeholder,
238
- disabled: d(s).loading.value,
256
+ disabled: h(s).loading.value,
239
257
  rows: "3"
240
258
  }, null, 8, ee), [
241
259
  [P, e.value]
242
260
  ]),
243
261
  u("div", te, [
244
- d(s).loading.value ? (f(), h("button", {
262
+ h(s).loading.value ? (m(), v("button", {
245
263
  key: 0,
246
264
  type: "button",
247
265
  class: "seer-chat__ghost-button",
248
- onClick: a[2] || (a[2] = (o) => d(s).abort())
266
+ onClick: a[2] || (a[2] = (o) => h(s).abort())
249
267
  }, " 停止 ")) : b("", !0),
250
268
  u("button", {
251
269
  type: "submit",
252
- disabled: !i.value
270
+ disabled: !c.value
253
271
  }, "发送", 8, ne)
254
272
  ])
255
273
  ], 32)
@@ -257,13 +275,13 @@ const K = {
257
275
  }
258
276
  }), ae = (n, t) => {
259
277
  const e = n.__vccOpts || n;
260
- for (const [s, i] of t)
261
- e[s] = i;
278
+ for (const [s, c] of t)
279
+ e[s] = c;
262
280
  return e;
263
- }, le = /* @__PURE__ */ ae(se, [["__scopeId", "data-v-51cf8455"]]);
281
+ }, ie = /* @__PURE__ */ ae(se, [["__scopeId", "data-v-51cf8455"]]);
264
282
  export {
265
- le as SeerChat,
283
+ ie as SeerChat,
266
284
  re as SeerChatPlugin,
267
285
  V as createSeerClient,
268
- z as useSeerChat
286
+ L as useSeerChat
269
287
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seer-ai/seer-vue",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Vue chat plugin for the seer OpenAI-compatible API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",