Skip to content

Commit

Permalink
feat: select agent to start session
Browse files Browse the repository at this point in the history
  • Loading branch information
CristiCanizales committed Mar 10, 2025
1 parent acc9a79 commit 121918c
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 35 deletions.
23 changes: 14 additions & 9 deletions agent-chat/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ const vscode = typeof acquireVsCodeApi !== 'undefined' ? acquireVsCodeApi() : nu
const App: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
const [query, setQuery] = useState('');
const [sendDisabled, setSendDisabled] = useState(false);
const [sendDisabled, setSendDisabled] = useState(true);
const [isThinking, setIsThinking] = useState(false);
const [currentAgent, setCurrentAgent] = useState('Select agent');
const [agents, setAgents] = useState([]);
const [currentAgent, setCurrentAgent] = useState('Select agent to start session');
const [messages, setMessages] = useState<Message[]>([]);

useEffect(() => {
if (vscode) {
vscode.postMessage({ command: 'startSession' });
vscode.postMessage({ command: 'queryAgents' });
} else {
console.error('vscode API not available.');
}
Expand All @@ -39,11 +40,14 @@ const App: React.FC = () => {
useEffect(() => {
window.addEventListener('message', event => {
const { command, data, error } = event.data;
if (command === 'sessionStarted') {
if (command === 'setAgents') {
setAgents(data);
} else if (command === 'sessionStarted') {
setMessages(prev => [
...prev,
{ id: prev.length + 1, role: 'system', content: data.message, timestamp: new Date() }
]);
setSendDisabled(false);
setTimeout(() => inputRef.current?.focus(), 0);
} else if (command === 'chatResponse') {
setIsThinking(false);
Expand Down Expand Up @@ -77,24 +81,25 @@ const App: React.FC = () => {
const handleEndSession = () => {
setQuery('');
setSendDisabled(true);
setCurrentAgent('Select agent to start session');
setIsThinking(false);
if (vscode) {
vscode.postMessage({ command: 'endSession' });
vscode.postMessage({ command: 'endSession', data: messages });
} else {
console.warn('vscode API not available');
}
};

const handleAgentSelect = (agent: string) => {
console.log('Selected Agent:', agent);
const handleAgentSelect = (agent: { Id: string; MasterLabel: string }) => {
if (vscode) {
vscode.postMessage({ command: 'selectAgent', agent });
vscode.postMessage({ command: 'startSession', data: agent.Id });
}
};

return (
<div className="app-container">
<Navbar
agents={agents}
currentAgent={currentAgent}
setCurrentAgent={setCurrentAgent}
onAgentSelect={handleAgentSelect}
Expand All @@ -106,7 +111,7 @@ const App: React.FC = () => {
className="message-list"
typingIndicator={isThinking ? <TypingIndicator content="Thinking..." /> : null}
>
{messages.map(msg => (
{messages.map((msg: Message) => (
<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
{msg.role === 'system' && <Avatar size="md" color="blue" src={salesforceLogo} />}
<Message
Expand Down
40 changes: 24 additions & 16 deletions agent-chat/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { AppBar, Toolbar, IconButton, Menu, MenuItem, Button, Typography } from '@mui/material';
import { AppBar, Toolbar, IconButton, Menu, MenuItem, Typography } from '@mui/material';
import { ArrowDropDown, CallEnd } from '@mui/icons-material';
import { useState } from 'react';

const Navbar = ({
agents,
currentAgent,
setCurrentAgent,
onAgentSelect,
onEndSession
}: {
agents: any[];
currentAgent: string;
setCurrentAgent: (agent: string) => void;
onAgentSelect: (agent: string) => void;
onAgentSelect: (agent: { Id: string; MasterLabel: string }) => void;
onEndSession: () => void;
}) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
Expand All @@ -24,35 +26,41 @@ const Navbar = ({
setAnchorEl(null);
};

const handleSelectAgent = (agent: string) => {
setCurrentAgent(agent);
const handleSelectAgent = (agent: { Id: string; MasterLabel: string }) => {
setCurrentAgent(agent.MasterLabel);
onAgentSelect(agent);
handleMenuClose();
};

return (
<AppBar position="static" color="default" elevation={1} className="navbar">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
<Typography
variant="h6"
sx={{
flexGrow: 1,
minWidth: 0,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}
>
{currentAgent}
</Typography>

<IconButton onClick={handleMenuOpen}>
<ArrowDropDown />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={handleMenuClose}>
<MenuItem onClick={() => handleSelectAgent('Local Info Agent')}>Local Info Agent</MenuItem>
<MenuItem onClick={() => handleSelectAgent('Guest Experience Agent')}>Guest Experience Agent</MenuItem>
{agents.map(agent => (
<MenuItem onClick={() => handleSelectAgent(agent)}>{agent.MasterLabel}</MenuItem>
))}
</Menu>

{/* End Session Button */}
<Button
variant="contained"
color="error"
startIcon={<CallEnd />}
onClick={onEndSession}
aria-label="End session"
/>
{currentAgent !== 'Select agent to start session' && (
<IconButton color="error" onClick={onEndSession} aria-label="End session">
<CallEnd />
</IconButton>
)}
</Toolbar>
</AppBar>
);
Expand Down
52 changes: 42 additions & 10 deletions src/views/agentChatViewProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
import { Org } from '@salesforce/core-bundle';
import { Connection, Org } from '@salesforce/core-bundle';
import { AgentPreview } from '@salesforce/agents';

export class AgentChatViewProvider implements vscode.WebviewViewProvider {
Expand All @@ -20,17 +20,17 @@ export class AgentChatViewProvider implements vscode.WebviewViewProvider {
};
webviewView.webview.onDidReceiveMessage(async message => {
try {
//TODO: Pass default org
const org = await Org.create({ aliasOrUsername: 'aliasAgente' });
const conn = org.getConnection() as Connection;
if (message.command === 'startSession') {
if (!this.agentPreview) {
const org = await Org.create({ aliasOrUsername: 'aliasAgente' });
const conn = org.getConnection() as any;
const agentId = 'idAgente';
this.agentPreview = new AgentPreview(conn);
this.agentPreview = new AgentPreview(conn as any);

const session = await this.agentPreview.start(agentId);
const session = await this.agentPreview.start(message.data);
this.sessionId = session.sessionId;

console.log('Session started:', session);

webviewView.webview.postMessage({
command: 'sessionStarted',
data: session.messages.find(msg => msg.type === 'Inform')
Expand All @@ -55,13 +55,18 @@ export class AgentChatViewProvider implements vscode.WebviewViewProvider {

await this.agentPreview.end(this.sessionId, 'UserRequest');
console.log('Session ended:', this.sessionId);
this.saveChatToFile(message.data);
this.sessionId = undefined;
this.agentPreview = undefined;

webviewView.webview.postMessage({ command: 'sessionEnded' });
} else if (message.command === 'selectAgent') {
console.log('Agent selected:', message.agent);
// TODO: logic to get bot id from agent name
} else if (message.command === 'queryAgents') {
const query = await conn.query('SELECT Id, MasterLabel FROM BotDefinition');
if (query.records.length > 0) {
webviewView.webview.postMessage({ command: 'setAgents', data: query.records });
} else {
vscode.window.showErrorMessage('There are no agents in the default org');
}
}
} catch (error) {
console.error('Error:', error);
Expand Down Expand Up @@ -100,4 +105,31 @@ export class AgentChatViewProvider implements vscode.WebviewViewProvider {
</html>
`;
}

private async saveChatToFile(messages: any[]) {
try {
// Get the current workspace folder
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
if (!workspaceFolder) {
vscode.window.showErrorMessage('No workspace folder found.');
return;
}

// Define file path inside 'Chats with agents' folder
const folderPath = vscode.Uri.joinPath(workspaceFolder.uri, 'Chats with agents');
await vscode.workspace.fs.createDirectory(folderPath);
const filePath = vscode.Uri.joinPath(folderPath, `${this.sessionId}.json`);

// Convert messages to JSON
const fileContent = JSON.stringify({ sessionId: this.sessionId, messages }, null, 2);

// Write to file
await vscode.workspace.fs.writeFile(filePath, Buffer.from(fileContent, 'utf8'));

vscode.window.showInformationMessage(`Chat history successfully saved to ${filePath.path}`);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
vscode.window.showErrorMessage(`Error saving chat: ${errorMessage}`);
}
}
}

0 comments on commit 121918c

Please sign in to comment.