From a697ac91097564e194180bc077fc572214129462 Mon Sep 17 00:00:00 2001 From: Al Marks Date: Mon, 13 Jan 2025 15:02:41 -0800 Subject: [PATCH 1/4] Clean up tool call rendering --- packages/bbrt/src/components/chat-message.ts | 6 +- packages/bbrt/src/components/tool-call.ts | 123 ++++++++++++------- 2 files changed, 79 insertions(+), 50 deletions(-) diff --git a/packages/bbrt/src/components/chat-message.ts b/packages/bbrt/src/components/chat-message.ts index 60d72a3ac0..c236a6dcff 100644 --- a/packages/bbrt/src/components/chat-message.ts +++ b/packages/bbrt/src/components/chat-message.ts @@ -153,10 +153,8 @@ export class BBRTChatMessage extends SignalWatcher(LitElement) { return nothing; } return html`
- ${calls.map((call) => - call.render - ? call.render() - : html`` + ${calls.map( + (call) => html`` )}
`; } diff --git a/packages/bbrt/src/components/tool-call.ts b/packages/bbrt/src/components/tool-call.ts index 09f002398b..70a1de9f2a 100644 --- a/packages/bbrt/src/components/tool-call.ts +++ b/packages/bbrt/src/components/tool-call.ts @@ -12,13 +12,13 @@ import { artifactStoreContext, type ArtifactStore, } from "../artifacts/artifact-store.js"; -import type { FunctionCallState } from "../state/function-call.js"; +import type { ReactiveFunctionCallState } from "../state/function-call.js"; import "./error-message.js"; @customElement("bbrt-tool-call") export class BBRTToolCallEl extends SignalWatcher(LitElement) { @property({ attribute: false }) - accessor toolCall: FunctionCallState | undefined = undefined; + accessor toolCall: ReactiveFunctionCallState | undefined = undefined; @consume({ context: artifactStoreContext }) @property({ attribute: false }) @@ -54,14 +54,22 @@ export class BBRTToolCallEl extends SignalWatcher(LitElement) { margin-top: 0; } .json-args, - .json-result { + .json-result, + .custom-widget { overflow: auto; - max-height: 200px; - background: rgba(0, 0, 0, 2%); padding: 12px; border-radius: 8px; border: 1px solid var(--bb-neutral-300); } + .json-args, + .json-result { + background: rgba(0, 0, 0, 2%); + max-height: 200px; + } + .custom-widget { + margin: 0 20px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.1); + } bbrt-error-message { border-radius: 8px; border: 1px solid var(--bb-error-color); @@ -75,61 +83,73 @@ export class BBRTToolCallEl extends SignalWatcher(LitElement) { `; override render() { - if (this.toolCall === undefined) { + const toolCall = this.toolCall; + if (toolCall === undefined) { return nothing; } - const status = (() => { - const response = this.toolCall.response; - switch (response.status) { - case "unstarted": { - return html`Status: Unstarted`; - } - case "executing": { - return html`Status: Executing...`; - } - case "success": { - return [ - html`Status: Success`, - html`
-${JSON.stringify(response.result, null, 2)}
`, - ]; - } - case "error": { - return html`Status: Error
- `; - } - default: { - response satisfies never; - console.error("Unexpected state", response); - return "Internal error"; - } - } - })(); + // prettier-ignore return html`
- Calling ${this.toolCall.functionId} with - arguments: -
-${JSON.stringify(this.toolCall.args, null, 2)}
-
${status}
+ Calling ${toolCall.functionId} with arguments: +
${JSON.stringify(toolCall.args, null, 2)}
+
Status: ${this.#renderStatus()}
+ ${this.#renderCustomWidget()} + ${this.#renderJsonResult()} + ${this.#renderError()} ${this.#renderArtifacts()}
`; } - #renderArtifacts() { - if (this.toolCall === undefined || this.artifacts === undefined) { + #renderStatus() { + if (this.toolCall === undefined) { return nothing; } const response = this.toolCall.response; - if (response.status !== "success") { + switch (response.status) { + case "unstarted": { + return html`Unstarted`; + } + case "executing": { + return html`Executing...`; + } + case "success": { + return [html`Success`]; + } + case "error": { + return html`Error`; + } + default: { + response satisfies never; + console.error("Unexpected state", response); + return "Internal error"; + } + } + } + + #renderCustomWidget() { + if (this.toolCall?.render) { + return html`
${this.toolCall.render()}
`; + } + return nothing; + } + + #renderJsonResult() { + const response = this.toolCall?.response; + if (response?.status !== "success") { return nothing; } - if (!response.artifacts.length) { + const indented = JSON.stringify(response.result, null, 2); + return html`
${indented}
`; + } + + #renderArtifacts() { + const response = this.toolCall?.response; + if ( + response?.status !== "success" || + this.artifacts === undefined || + !response.artifacts.length + ) { return nothing; } const artifacts = []; @@ -150,6 +170,17 @@ ${JSON.stringify(this.toolCall.args, null, 2)} + `; + } } declare global { From a4e75467245801f6992c54a4b3a289f5b748cefd Mon Sep 17 00:00:00 2001 From: Al Marks Date: Mon, 13 Jan 2025 15:52:36 -0800 Subject: [PATCH 2/4] Add retry button --- packages/bbrt/src/components/chat-message.ts | 46 +++++++++++++++---- packages/bbrt/src/components/main.ts | 48 +++++++++++++------- packages/bbrt/src/components/tool-call.ts | 7 ++- packages/bbrt/src/llm/{fork.ts => events.ts} | 10 ++++ packages/bbrt/src/state/session.ts | 7 +++ 5 files changed, 91 insertions(+), 27 deletions(-) rename packages/bbrt/src/llm/{fork.ts => events.ts} (64%) diff --git a/packages/bbrt/src/components/chat-message.ts b/packages/bbrt/src/components/chat-message.ts index c236a6dcff..01c6525cf6 100644 --- a/packages/bbrt/src/components/chat-message.ts +++ b/packages/bbrt/src/components/chat-message.ts @@ -7,7 +7,7 @@ import { SignalWatcher } from "@lit-labs/signals"; import { LitElement, css, html, nothing, svg } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { ForkEvent } from "../llm/fork.js"; +import { ForkEvent, RetryEvent } from "../llm/events.js"; import type { ReactiveTurnState } from "../state/turn.js"; import { iconButtonStyle } from "../style/icon-button.js"; import { connectedEffect } from "../util/connected-effect.js"; @@ -99,6 +99,7 @@ export class BBRTChatMessage extends SignalWatcher(LitElement) { width: min-content; position: relative; margin: 4px auto 12px -10px; + display: flex; } :host(:hover) #actions, :host(:focus) #actions, @@ -111,9 +112,14 @@ export class BBRTChatMessage extends SignalWatcher(LitElement) { } #actions button { border: none; - --bb-icon: var(--bb-icon-fork-down-right); --bb-icon-size: 20px; } + #retryButton { + --bb-icon: var(--bb-icon-refresh); + } + #forkButton { + --bb-icon: var(--bb-icon-fork-down-right); + } #actions button:not(:hover) { --bb-button-background: transparent; } @@ -177,16 +183,33 @@ export class BBRTChatMessage extends SignalWatcher(LitElement) { if (!this.turn) { return nothing; } - return html` -
+ const buttons = []; + if (this.turn.role === "user") { + buttons.push(html` + `); + } + if ( + this.turn.role === "model" && + this.turn.status === "done" && + this.turn.partialFunctionCalls.length === 0 + ) { + buttons.push(html` +
- `; + `); + } + return html`
${buttons}
`; } #onClickForkButton() { @@ -195,6 +218,13 @@ export class BBRTChatMessage extends SignalWatcher(LitElement) { } this.dispatchEvent(new ForkEvent(this.turn)); } + + #onClickRetryButton() { + if (!this.turn) { + return; + } + this.dispatchEvent(new RetryEvent(this.turn)); + } } declare global { diff --git a/packages/bbrt/src/components/main.ts b/packages/bbrt/src/components/main.ts index 1940883dbd..b55a6a3f14 100644 --- a/packages/bbrt/src/components/main.ts +++ b/packages/bbrt/src/components/main.ts @@ -35,7 +35,7 @@ import type { BBRTDriver } from "../drivers/driver-interface.js"; import { GeminiDriver } from "../drivers/gemini.js"; import { OpenAiDriver } from "../drivers/openai.js"; import { Conversation } from "../llm/conversation.js"; -import type { ForkEvent } from "../llm/fork.js"; +import type { ForkEvent, RetryEvent } from "../llm/events.js"; import { BREADBOARD_ASSISTANT_SYSTEM_INSTRUCTION } from "../llm/system-instruction.js"; import { IndexedDBSettingsSecrets } from "../secrets/indexed-db-secrets.js"; import type { SecretsProvider } from "../secrets/secrets-provider.js"; @@ -44,6 +44,7 @@ import { LocalStoragePersistence } from "../state/local-storage-persistence.js"; import type { Persistence } from "../state/persistence.js"; import { SessionStore } from "../state/session-store.js"; import type { ReactiveSessionState } from "../state/session.js"; +import type { ReactiveTurnState } from "../state/turn.js"; import { ActivateTool } from "../tools/activate-tool.js"; import { AddNode } from "../tools/add-node.js"; import { CreateBoard } from "../tools/create-board.js"; @@ -303,6 +304,7 @@ export class BBRTMain extends SignalWatcher(LitElement) { .appState=${this.#appState} .sessionStore=${this.#sessions} @bbrt-fork=${this.#onFork} + @bbrt-retry=${this.#onRetry} >