add instruction window
This commit is contained in:
@@ -20,9 +20,10 @@ export interface PhoneSimulatorProps {
|
|||||||
onDisconnect: () => void;
|
onDisconnect: () => void;
|
||||||
phoneMode?: "normal" | "capture";
|
phoneMode?: "normal" | "capture";
|
||||||
onCapture?: (image: File) => void;
|
onCapture?: (image: File) => void;
|
||||||
|
capturePrompt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PhoneSimulator({ onConnect, onDisconnect, phoneMode = "normal", onCapture }: PhoneSimulatorProps) {
|
export function PhoneSimulator({ onConnect, onDisconnect, phoneMode = "normal", onCapture, capturePrompt }: PhoneSimulatorProps) {
|
||||||
const { config, setUserSettings } = useConfig();
|
const { config, setUserSettings } = useConfig();
|
||||||
const { setToastMessage } = useToast();
|
const { setToastMessage } = useToast();
|
||||||
const room = useRoomContext();
|
const room = useRoomContext();
|
||||||
@@ -517,6 +518,15 @@ export function PhoneSimulator({ onConnect, onDisconnect, phoneMode = "normal",
|
|||||||
{/* Center Focus Indicator */}
|
{/* Center Focus Indicator */}
|
||||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-16 h-16 border border-white/50 rounded-sm"></div>
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-16 h-16 border border-white/50 rounded-sm"></div>
|
||||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-1 h-1 bg-white/50 rounded-full"></div>
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-1 h-1 bg-white/50 rounded-full"></div>
|
||||||
|
|
||||||
|
{/* Prompt Display */}
|
||||||
|
{capturePrompt && (
|
||||||
|
<div className="absolute top-32 left-0 w-full px-6 text-center z-20">
|
||||||
|
<div className="inline-block bg-black/60 backdrop-blur-md text-white px-4 py-3 rounded-2xl text-sm font-medium shadow-lg border border-white/10 max-w-full break-words">
|
||||||
|
{capturePrompt}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export default function Playground({
|
|||||||
const tracks = useTracks();
|
const tracks = useTracks();
|
||||||
const room = useRoomContext();
|
const room = useRoomContext();
|
||||||
const [phoneMode, setPhoneMode] = useState<"normal" | "capture">("normal");
|
const [phoneMode, setPhoneMode] = useState<"normal" | "capture">("normal");
|
||||||
|
const [capturePrompt, setCapturePrompt] = useState<string>("");
|
||||||
|
|
||||||
const [rpcMethod, setRpcMethod] = useState("");
|
const [rpcMethod, setRpcMethod] = useState("");
|
||||||
const [rpcPayload, setRpcPayload] = useState("");
|
const [rpcPayload, setRpcPayload] = useState("");
|
||||||
@@ -106,7 +107,17 @@ export default function Playground({
|
|||||||
|
|
||||||
localParticipant.registerRpcMethod(
|
localParticipant.registerRpcMethod(
|
||||||
'enterImageCaptureMode',
|
'enterImageCaptureMode',
|
||||||
async () => {
|
async (data: RpcInvocationData) => {
|
||||||
|
if (data.payload) {
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(data.payload);
|
||||||
|
if (payload.prompt) {
|
||||||
|
setCapturePrompt(payload.prompt);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to parse enterImageCaptureMode payload", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
setPhoneMode("capture");
|
setPhoneMode("capture");
|
||||||
return JSON.stringify({ success: true });
|
return JSON.stringify({ success: true });
|
||||||
}
|
}
|
||||||
@@ -116,6 +127,7 @@ export default function Playground({
|
|||||||
'exitImageCaptureMode',
|
'exitImageCaptureMode',
|
||||||
async () => {
|
async () => {
|
||||||
setPhoneMode("normal");
|
setPhoneMode("normal");
|
||||||
|
setCapturePrompt("");
|
||||||
return JSON.stringify({ success: true });
|
return JSON.stringify({ success: true });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -315,6 +327,24 @@ export default function Playground({
|
|||||||
voiceAssistant.agent,
|
voiceAssistant.agent,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const instructionsContent = (
|
||||||
|
<ConfigurationPanelItem title="Instructions">
|
||||||
|
<textarea
|
||||||
|
className="w-full bg-gray-950 text-white text-sm p-3 rounded-md border border-gray-800 focus:border-gray-600 focus:outline-none transition-colors resize-none disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
style={{ minHeight: "80px" }}
|
||||||
|
rows={3}
|
||||||
|
placeholder="Enter system instructions for the agent..."
|
||||||
|
value={config.settings.instructions}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newSettings = { ...config.settings };
|
||||||
|
newSettings.instructions = e.target.value;
|
||||||
|
setUserSettings(newSettings);
|
||||||
|
}}
|
||||||
|
disabled={roomState !== ConnectionState.Disconnected}
|
||||||
|
/>
|
||||||
|
</ConfigurationPanelItem>
|
||||||
|
);
|
||||||
|
|
||||||
const handleRpcCall = useCallback(async () => {
|
const handleRpcCall = useCallback(async () => {
|
||||||
if (!voiceAssistant.agent || !room) {
|
if (!voiceAssistant.agent || !room) {
|
||||||
throw new Error("No agent or room available");
|
throw new Error("No agent or room available");
|
||||||
@@ -599,6 +629,7 @@ export default function Playground({
|
|||||||
onConnect={() => onConnect(true)}
|
onConnect={() => onConnect(true)}
|
||||||
onDisconnect={() => onConnect(false)}
|
onDisconnect={() => onConnect(false)}
|
||||||
phoneMode={phoneMode}
|
phoneMode={phoneMode}
|
||||||
|
capturePrompt={capturePrompt}
|
||||||
onCapture={async (content: File) => {
|
onCapture={async (content: File) => {
|
||||||
if (localParticipant) {
|
if (localParticipant) {
|
||||||
await localParticipant.sendFile(content, { topic: "image" });
|
await localParticipant.sendFile(content, { topic: "image" });
|
||||||
@@ -616,6 +647,19 @@ export default function Playground({
|
|||||||
title: "Chat",
|
title: "Chat",
|
||||||
content: chatTileContent,
|
content: chatTileContent,
|
||||||
});
|
});
|
||||||
|
mobileTabs.push({
|
||||||
|
title: "Instructions",
|
||||||
|
content: (
|
||||||
|
<PlaygroundTile
|
||||||
|
padding={false}
|
||||||
|
backgroundColor="gray-950"
|
||||||
|
className="h-full w-full grow items-start overflow-y-auto"
|
||||||
|
childrenClassName="h-full grow items-start"
|
||||||
|
>
|
||||||
|
{instructionsContent}
|
||||||
|
</PlaygroundTile>
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mobileTabs.push({
|
mobileTabs.push({
|
||||||
@@ -672,6 +716,7 @@ export default function Playground({
|
|||||||
onConnect={() => onConnect(true)}
|
onConnect={() => onConnect(true)}
|
||||||
onDisconnect={() => onConnect(false)}
|
onDisconnect={() => onConnect(false)}
|
||||||
phoneMode={phoneMode}
|
phoneMode={phoneMode}
|
||||||
|
capturePrompt={capturePrompt}
|
||||||
onCapture={async (content: File) => {
|
onCapture={async (content: File) => {
|
||||||
if (localParticipant) {
|
if (localParticipant) {
|
||||||
await localParticipant.sendFile(content, { topic: "image" });
|
await localParticipant.sendFile(content, { topic: "image" });
|
||||||
@@ -683,12 +728,22 @@ export default function Playground({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{config.settings.chat && (
|
{config.settings.chat && (
|
||||||
<PlaygroundTile
|
<div className="flex flex-col h-full grow basis-1/4 hidden lg:flex gap-4">
|
||||||
title="Chat"
|
<PlaygroundTile
|
||||||
className="h-full grow basis-1/4 hidden lg:flex"
|
padding={false}
|
||||||
>
|
backgroundColor="gray-950"
|
||||||
{chatTileContent}
|
className="h-auto w-full flex-none min-h-0"
|
||||||
</PlaygroundTile>
|
childrenClassName="items-start"
|
||||||
|
>
|
||||||
|
{instructionsContent}
|
||||||
|
</PlaygroundTile>
|
||||||
|
<PlaygroundTile
|
||||||
|
title="Chat"
|
||||||
|
className="w-full grow min-h-0"
|
||||||
|
>
|
||||||
|
{chatTileContent}
|
||||||
|
</PlaygroundTile>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<PlaygroundTile
|
<PlaygroundTile
|
||||||
padding={false}
|
padding={false}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export type UserSettings = {
|
|||||||
participant_id: string;
|
participant_id: string;
|
||||||
participant_name: string;
|
participant_name: string;
|
||||||
agent_name?: string;
|
agent_name?: string;
|
||||||
|
instructions?: string;
|
||||||
metadata?: string;
|
metadata?: string;
|
||||||
attributes?: AttributeItem[];
|
attributes?: AttributeItem[];
|
||||||
};
|
};
|
||||||
@@ -67,6 +68,7 @@ const defaultConfig: AppConfig = {
|
|||||||
room_name: "",
|
room_name: "",
|
||||||
participant_id: "",
|
participant_id: "",
|
||||||
participant_name: "",
|
participant_name: "",
|
||||||
|
instructions: "",
|
||||||
metadata: "",
|
metadata: "",
|
||||||
attributes: [],
|
attributes: [],
|
||||||
},
|
},
|
||||||
@@ -140,6 +142,7 @@ export const ConfigProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
room_name: "",
|
room_name: "",
|
||||||
participant_id: "",
|
participant_id: "",
|
||||||
participant_name: "",
|
participant_name: "",
|
||||||
|
instructions: "",
|
||||||
} as UserSettings;
|
} as UserSettings;
|
||||||
}, [appConfig]);
|
}, [appConfig]);
|
||||||
|
|
||||||
|
|||||||
@@ -72,19 +72,19 @@ export const ConnectionProvider = ({
|
|||||||
if (config.settings.metadata) {
|
if (config.settings.metadata) {
|
||||||
body.metadata = config.settings.metadata;
|
body.metadata = config.settings.metadata;
|
||||||
}
|
}
|
||||||
|
const attributes: Record<string, string> = {};
|
||||||
|
if (config.settings.instructions) {
|
||||||
|
attributes.instructions = config.settings.instructions;
|
||||||
|
}
|
||||||
const attributesArray = Array.isArray(config.settings.attributes)
|
const attributesArray = Array.isArray(config.settings.attributes)
|
||||||
? config.settings.attributes
|
? config.settings.attributes
|
||||||
: [];
|
: [];
|
||||||
if (attributesArray?.length) {
|
attributesArray.forEach((attr) => {
|
||||||
const attributes = attributesArray.reduce(
|
if (attr.key) {
|
||||||
(acc, attr) => {
|
attributes[attr.key] = attr.value;
|
||||||
if (attr.key) {
|
}
|
||||||
acc[attr.key] = attr.value;
|
});
|
||||||
}
|
if (Object.keys(attributes).length > 0) {
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, string>,
|
|
||||||
);
|
|
||||||
body.attributes = attributes;
|
body.attributes = attributes;
|
||||||
}
|
}
|
||||||
const { accessToken } = await fetch(`/api/token`, {
|
const { accessToken } = await fetch(`/api/token`, {
|
||||||
@@ -111,6 +111,7 @@ export const ConnectionProvider = ({
|
|||||||
config.settings.participant_id,
|
config.settings.participant_id,
|
||||||
config.settings.metadata,
|
config.settings.metadata,
|
||||||
config.settings.attributes,
|
config.settings.attributes,
|
||||||
|
config.settings.instructions,
|
||||||
generateToken,
|
generateToken,
|
||||||
setToastMessage,
|
setToastMessage,
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user