diff --git a/packages/sdk/index.html b/packages/sdk/index.html
index dcc9b62..c17d138 100644
--- a/packages/sdk/index.html
+++ b/packages/sdk/index.html
@@ -273,6 +273,7 @@
+
@@ -386,6 +387,7 @@
Console Logs
remoteVideo: document.getElementById('remote-video'),
promptInput: document.getElementById('prompt-input'),
sendPrompt: document.getElementById('send-prompt'),
+ resetInput: document.getElementById('reset-input'),
enrichToggle: document.getElementById('enrich-toggle'),
// Promise-based prompt elements
promisePromptInput: document.getElementById('promise-prompt-input'),
@@ -546,6 +548,7 @@ Console Logs
elements.disconnectBtn.disabled = false;
elements.promptInput.disabled = false;
elements.sendPrompt.disabled = false;
+ elements.resetInput.disabled = false;
elements.promisePromptInput.disabled = false;
elements.sendPromisePrompt.disabled = false;
elements.referenceImage.disabled = false;
@@ -599,6 +602,7 @@ Console Logs
elements.disconnectBtn.disabled = true;
elements.promptInput.disabled = true;
elements.sendPrompt.disabled = true;
+ elements.resetInput.disabled = true;
elements.promptInput.value = '';
elements.promisePromptInput.disabled = true;
elements.sendPromisePrompt.disabled = true;
@@ -641,6 +645,25 @@ Console Logs
// Send prompt on button click
elements.sendPrompt.addEventListener('click', sendPrompt);
+ // Reset input (back to passthrough)
+ elements.resetInput.addEventListener('click', async () => {
+ if (!decartRealtime || !isConnected) {
+ addLog('Not connected to Decart', 'error');
+ return;
+ }
+ try {
+ elements.resetInput.disabled = true;
+ addLog('Resetting input to passthrough...', 'info');
+ await decartRealtime.resetInput();
+ addLog('Reset to passthrough mode', 'success');
+ elements.promptInput.value = '';
+ } catch (error) {
+ addLog(`Failed to reset input: ${error.message}`, 'error');
+ } finally {
+ elements.resetInput.disabled = false;
+ }
+ });
+
// Send prompt on Enter key
elements.promptInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !elements.sendPrompt.disabled) {
diff --git a/packages/sdk/src/realtime/client.ts b/packages/sdk/src/realtime/client.ts
index f1869b0..f31f6e8 100644
--- a/packages/sdk/src/realtime/client.ts
+++ b/packages/sdk/src/realtime/client.ts
@@ -109,6 +109,7 @@ export type Events = {
export type RealTimeClient = {
set: (input: SetInput) => Promise;
setPrompt: (prompt: string, { enhance }?: { enhance?: boolean }) => Promise;
+ resetInput: () => Promise;
isConnected: () => boolean;
getConnectionState: () => ConnectionState;
disconnect: () => void;
@@ -118,7 +119,7 @@ export type RealTimeClient = {
subscribeToken: string | null;
setImage: (
image: Blob | File | string | null,
- options?: { prompt?: string; enhance?: boolean; timeout?: number },
+ options?: { prompt?: string | null; enhance?: boolean; timeout?: number },
) => Promise;
playAudio?: (audio: Blob | File | ArrayBuffer) => Promise;
};
@@ -329,6 +330,7 @@ export const createRealTimeClient = (opts: RealTimeClientOptions) => {
const client: RealTimeClient = {
set: methods.set,
setPrompt: methods.setPrompt,
+ resetInput: methods.resetInput,
isConnected: () => manager.isConnected(),
getConnectionState: () => manager.getConnectionState(),
disconnect: () => {
@@ -348,7 +350,7 @@ export const createRealTimeClient = (opts: RealTimeClientOptions) => {
},
setImage: async (
image: Blob | File | string | null,
- options?: { prompt?: string; enhance?: boolean; timeout?: number },
+ options?: { prompt?: string | null; enhance?: boolean; timeout?: number },
) => {
if (image === null) {
return manager.setImage(null, options);
diff --git a/packages/sdk/src/realtime/methods.ts b/packages/sdk/src/realtime/methods.ts
index 6755d41..39b6cde 100644
--- a/packages/sdk/src/realtime/methods.ts
+++ b/packages/sdk/src/realtime/methods.ts
@@ -7,7 +7,7 @@ const UPDATE_TIMEOUT_MS = 30 * 1000;
const setInputSchema = z
.object({
- prompt: z.string().min(1).optional(),
+ prompt: z.union([z.string().min(1), z.null()]).optional(),
enhance: z.boolean().optional().default(true),
image: z.union([z.instanceof(Blob), z.instanceof(File), z.string(), z.null()]).optional(),
})
@@ -109,8 +109,14 @@ export const realtimeMethods = (
}
};
+ const resetInput = async (): Promise => {
+ assertConnected();
+ await webrtcManager.setImage(null, { prompt: null, timeout: UPDATE_TIMEOUT_MS });
+ };
+
return {
set,
setPrompt,
+ resetInput,
};
};
diff --git a/packages/sdk/src/realtime/webrtc-manager.ts b/packages/sdk/src/realtime/webrtc-manager.ts
index 71408fb..1efca07 100644
--- a/packages/sdk/src/realtime/webrtc-manager.ts
+++ b/packages/sdk/src/realtime/webrtc-manager.ts
@@ -246,7 +246,7 @@ export class WebRTCManager {
setImage(
imageBase64: string | null,
- options?: { prompt?: string; enhance?: boolean; timeout?: number },
+ options?: { prompt?: string | null; enhance?: boolean; timeout?: number },
): Promise {
return this.connection.setImageBase64(imageBase64, options);
}
diff --git a/packages/sdk/tests/unit.test.ts b/packages/sdk/tests/unit.test.ts
index 627303e..aca56aa 100644
--- a/packages/sdk/tests/unit.test.ts
+++ b/packages/sdk/tests/unit.test.ts
@@ -1422,6 +1422,28 @@ describe("set()", () => {
timeout: 30000,
});
});
+
+ it("set({ prompt: null }) resets to passthrough", async () => {
+ await methods.set({ prompt: null });
+ expect(mockManager.setImage).toHaveBeenCalledWith(null, {
+ prompt: null,
+ enhance: true,
+ timeout: 30000,
+ });
+ });
+
+ it("resetInput sends passthrough signal", async () => {
+ await methods.resetInput();
+ expect(mockManager.setImage).toHaveBeenCalledWith(null, {
+ prompt: null,
+ timeout: 30000,
+ });
+ });
+
+ it("resetInput rejects when not connected", async () => {
+ mockManager.getConnectionState.mockReturnValue("disconnected");
+ await expect(methods.resetInput()).rejects.toThrow("Cannot send message: connection is disconnected");
+ });
});
describe("Subscribe Token", () => {