diff --git a/packages/dsh/src/client.ts b/packages/dsh/src/client.ts index 56b0d91..54158db 100644 --- a/packages/dsh/src/client.ts +++ b/packages/dsh/src/client.ts @@ -28,8 +28,8 @@ function insertStyles(css: string): () => void { }; } -/** Services required by the client plugin. */ -export const inject = ["slots"]; +/** Services required by the client plugin (conversation for the input hub to send queries). */ +export const inject = ["slots", "sessions", "conversation"]; /** Browser Cordis plugin: registers the "Bailian" settings.section page. */ export function apply(ctx: any): void { @@ -912,6 +912,26 @@ export function apply(ctx: any): void { .bw-card-desc{font-size:13px;color:var(--dsw-text-secondary,#71717a);line-height:1.6} `); + // Help the user send a natural-language query into the conversation (the + // agent then routes it to the right tool / may AskUserQuestion for params). + const sessionsSvc = ctx.get("sessions"); + const inputHub = ctx.conversation && ctx.conversation.input ? ctx.conversation.input : undefined; + function sendQuery(sessionId: string, text: string): boolean { + try { + if (!inputHub) return false; + let shell: any; + const actx = sessionsSvc && sessionsSvc.scope ? sessionsSvc.scope(sessionId) : undefined; + if (actx && inputHub.for) shell = inputHub.for(actx); + else if (inputHub.shell) shell = inputHub.shell(sessionId); + if (!shell) return false; + shell.setDraft(text); + if (shell.submit) shell.submit(); + return true; + } catch { + return false; + } + } + slots.inject("conversation.input.dock", () => slots.register( { name: "conversation.input.dock", id: "bailian-welcome", order: -100 }, @@ -919,24 +939,9 @@ export function apply(ctx: any): void { const session = props && props.session; if (!session || session.blank !== true) return null; const [tab, setTab] = React.useState("rec"); - const [feat, setFeat] = React.useState(null); - const [featLoading, setFeatLoading] = React.useState(""); let active = WELCOME_TABS.find((t) => t.id === tab); if (!active) active = WELCOME_TABS[0]; - - function runFeature(id: string) { - setFeatLoading(id); - setFeat(null); - fetch("/bailian/console", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ featureId: id }), - }) - .then((r) => r.json()) - .then((d) => setFeat(d)) - .catch((e) => setFeat({ error: e && e.message ? e.message : String(e) })) - .then(() => setFeatLoading("")); - } + const sessionId = session.sessionId; return React.createElement( "div", { className: "bw-wrap" }, @@ -980,7 +985,7 @@ export function apply(ctx: any): void { { key: c.title, className: "bw-card", - onClick: feature ? () => runFeature(feature.id) : undefined, + onClick: feature ? () => sendQuery(sessionId, feature.query) : undefined, style: feature ? undefined : { cursor: "default", opacity: 0.75 }, }, React.createElement("div", { className: "bw-card-title" }, c.title), @@ -989,36 +994,12 @@ export function apply(ctx: any): void { ? React.createElement( "div", { style: { marginTop: "8px", fontSize: "12px", color: "#6366f1" } }, - featLoading === feature.id ? "查询中..." : "点击查询 →", + "点击提问 →", ) : null, ); }), ), - feat - ? React.createElement( - "div", - { className: "bw-card", style: { marginTop: "14px" } }, - feat.error - ? React.createElement( - "div", - { className: "bw-card-desc", style: { color: "#e5484d" } }, - feat.error, - ) - : React.createElement( - "pre", - { - style: { - whiteSpace: "pre-wrap", - fontSize: "13px", - margin: 0, - color: "var(--dsw-text,#18181b)", - }, - }, - feat.summary || JSON.stringify(feat.data, null, 2), - ), - ) - : null, ); }, ), diff --git a/packages/dsh/src/features.ts b/packages/dsh/src/features.ts index 81544bb..2a96871 100644 --- a/packages/dsh/src/features.ts +++ b/packages/dsh/src/features.ts @@ -28,6 +28,8 @@ export interface BailianFeature { desc: string; /** Tool description: tells the LLM which user utterances should use it. */ intent: string; + /** Natural-language query sent into the conversation when the card is clicked. */ + query: string; /** `bl` command args (without `--output`); the Host appends `--output json`. */ argv: string[]; /** Args appended when the user supplies no params (e.g. ["--all"]). */ @@ -64,6 +66,7 @@ export const FEATURES: BailianFeature[] = [ desc: "查询免费额度用量,一键开启「用完即停」,额度耗尽自动停止调用,不再产生意外扣费", intent: "查询百炼免费额度用量与『用完即停』防护状态。当用户提到免费额度、额度耗尽、意外扣费、用完即停、额度防护时使用。", + query: "帮我查看百炼免费额度用量,并告诉我怎么开启「用完即停」防护", argv: ["usage", "freetier"], defaultArgs: ["--all"], paramFlags: [ @@ -99,6 +102,7 @@ export const FEATURES: BailianFeature[] = [ desc: "各模型/TokenPlan 的用量与百分比一次查清,自动生成用量分析", intent: "查询百炼 TokenPlan 个人版用量(5 小时/1 周窗口百分比、重置时间、套餐、用量包)。当用户问用量、用了多少、额度百分比、TokenPlan 使用情况时使用。", + query: "帮我查询百炼 TokenPlan 个人版用量(5 小时/1 周窗口、套餐与用量包)", argv: ["token-plan", "personal-usage"], summarize: (d) => { if (!d || typeof d !== "object") return "未获取到用量数据。"; @@ -118,21 +122,6 @@ export const FEATURES: BailianFeature[] = [ : "用量: " + JSON.stringify(d).slice(0, 300); }, }, - { - id: "apikey", - title: "API Key 管理", - desc: "查看 TokenPlan API Key(脱敏),创建/重置可在控制台或 bl 完成", - intent: - "查询当前百炼 TokenPlan API Key(脱敏)。当用户问 API Key、密钥、我的 key、查看 key 时使用。", - argv: ["token-plan", "personal-key"], - summarize: (d) => { - if (!d || typeof d !== "object") return "未获取到 API Key。"; - const mask = pick(d, "mask_apikey", "maskApikey", "apiKey"); - const id = pick(d, "key_id", "keyId"); - if (mask) return `API Key: ${mask}${id !== undefined ? `(id: ${id})` : ""}`; - return "API Key: " + JSON.stringify(d).slice(0, 300); - }, - }, ]; export function featureById(id: string): BailianFeature | undefined {