@blanddk/ 客户端
布兰德SDK是一款JavaScript/TypeScript SDK,用于与布兰德平台相融合. 它既提供服务器端组件,也提供客户端端组件来方便与布兰德的服务通信.
开始
布兰德SDK有两个组成部分:
- Admin SDK:用于创建会话,处理认证,并使用您特权的API密钥与布兰德互动的服务器-Side SDK.
- 客户端 SDK : Webchat SDK 用于嵌入 Bland 在您的应用程序中 。 您的 API 密钥不应该在这里使用 .
安装
npm install @blandsdk/client
概览
SDK被设计用于服务器端和客户端端环境. 服务器侧组件(Admin SDK)负责创建会话,使用一个特权的API密钥来请求bland并管理认证,而客户端组件(Webchat SDK)则处理用户界面和与Bland的交互.
Browser Client -> Your Server (which uses the Admin SDK) -> Bland
然后,当客户端有会话符号时:
Browser Client -> Bland
用法:服务器系统(Admin SDK)
管理员SDK用于为代理创建会话. 以下是为给定创建会话的Express路由示例: agentId 并返回会话符号。 此指使随后被Webchat SDK在客户端使用.
import express from "express";
import bodyParser from "body-parser";
import Bland from "@blandsdk/client";
const app = express();
app.use(bodyParser.json());
const bland = new Bland({
admin: {
apiKey: process.env.BLAND_API_KEY,
endpoint: "https://api.bland.ai" // enterprise users: contact your account executive for the correct endpoint.
},
webchat: {}
});
app.post("/api/agent-authorize", async (req, res) => {
try {
const agentId = req.body?.agentId;
if (!agentId) {
return res.status(400).json({ error: "missing agentId" });
}
const admin = await bland.AdminClient();
const session = await admin.sessions.create({ agentId });
res.json({ token: session.token });
} catch (err: any) {
res.status(500).json({ error: err?.message || "internal error" });
}
});
const port = 5174;
app.listen(port, () => {
console.log(`Admin server running on http://localhost:${port}`);
});
用法:客户端-Side (Webchat SDK) - 反应
以下是一个最小的例子, 请求您的管理者 SDK 提供会话符号, 连接 Webchat, 聆听事件, 并显示笔录 。 与返回的管理员区的快车路线对等 { token } 给定的 agentId。 。 。 .
import { useEffect, useMemo, useRef, useState } from "react";
import { useWebchat } from "@blandsdk/client/react";
type Token = { token: string };
async function authorizeAgent(agentId: string): Promise<Token> {
const r = await fetch("<your server url>", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ agentId })
});
if (!r.ok) throw new Error(`authorize error ${r.status}`);
return r.json();
}
export default function App() {
const [agentId, setAgentId] = useState("abc123");
const [status, setStatus] = useState("idle");
const [streamSid, setStreamSid] = useState<string | null>(null);
const [transcript, setTranscript] = useState<string>("");
const logRef = useRef<HTMLPreElement | null>(null);
const getToken = useMemo(() => {
return async () => authorizeAgent(agentId);
}, [agentId]);
const { state, start, stop, webchat } = useWebchat({
agentId,
getToken
});
useEffect(() => {
const off = [
webchat.on("open", () => {
setStatus("open");
log("[Websocket][open]");
}),
webchat.on("text", (raw: string) => {
log(`[Websocket][text] ${raw}`);
}),
webchat.on("message", (m: any) => {
log(`[Websocket][message] ${JSON.stringify(m)}`);
if (!streamSid && m?.streamSid) setStreamSid(m.streamSid);
}),
webchat.on("update", (m: any) => {
log(`[Websocket][update] ${JSON.stringify(m)}`);
const who = m?.payload?.type === "assistant" ? "assistant" : "human";
const t = m?.payload?.text;
if (typeof t === "string" && t.length) {
setTranscript((cur) => (cur ? `${cur}\n${who}: ${t}` : `${who}: ${t}`));
}
}),
webchat.on("closed", () => {
setStatus("closed");
log("[Websocket][closed]");
}),
webchat.on("error", (e: unknown) => {
setStatus("closed");
log(`[Websocket][error] ${stringify(e)}`);
})
];
return () => off.forEach((u) => u());
}, [webchat, streamSid]);
function stringify(v: unknown) {
try { return typeof v === "string" ? v : JSON.stringify(v) } catch { return String(v) }
}
function log(line: string) {
const ts = new Date().toISOString();
if (logRef.current) {
logRef.current.textContent += `[${ts}] ${line}\n`;
logRef.current.scrollTop = logRef.current.scrollHeight;
}
}
const onStart = async () => {
try {
setStatus("authorizing");
await start();
} catch (e) {
setStatus("closed");
log(String(e));
}
};
const onStop = () => {
try { stop() } finally { setStatus("closed") }
};
return (
<div style={{ maxWidth: 760, margin: "0 auto", padding: 24, fontFamily: "system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif" }}>
<h1 style={{ fontSize: 22, margin: "0 0 16px" }}>Bland React SDK</h1>
<div style={{ display: "flex", gap: 8, alignItems: "center", margin: "12px 0" }}>
<label htmlFor="agent">Agent</label>
<input id="agent" value={agentId} onChange={(e) => setAgentId(e.target.value)} />
<button onClick={onStart} disabled={state !== "closed" && state !== "connecting"}>Start</button>
<button onClick={onStop} disabled={state === "closed"}>Stop</button>
</div>
<div style={{ padding: "8px 10px", background: "#f5f5f5", borderRadius: 6, marginTop: 12 }}>{status}</div>
<h2 style={{ fontSize: 16, marginTop: 16 }}>Transcript</h2>
<pre style={{ whiteSpace: "pre-wrap", background: "#fafafa", padding: 12, borderRadius: 6 }}>{transcript}</pre>
<h2 style={{ fontSize: 16, marginTop: 16 }}>Log</h2>
<pre ref={logRef} style={{ whiteSpace: "pre-wrap", background: "#0b1020", color: "#e6edf3", padding: 12, borderRadius: 6, maxHeight: 300, overflow: "auto" }} />
</div>
);
}
客户选项
export interface BlandClientOptions {
admin: {
apiKey?: string;
endpoint?: string;
}
webchat?: {
endpoint?: string;
}
}