Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
CONFIG_AUTH_CLIENT,
CONFIG_BLOB_CONTAINER_CLIENT,
CONFIG_CHAT_APPROACH,
CONFIG_CHAT_HISTORY_ENABLED,
CONFIG_CHAT_VISION_APPROACH,
CONFIG_CREDENTIAL,
CONFIG_GPT4V_DEPLOYED,
Expand Down Expand Up @@ -276,6 +277,7 @@ def config():
"showSpeechInput": current_app.config[CONFIG_SPEECH_INPUT_ENABLED],
"showSpeechOutputBrowser": current_app.config[CONFIG_SPEECH_OUTPUT_BROWSER_ENABLED],
"showSpeechOutputAzure": current_app.config[CONFIG_SPEECH_OUTPUT_AZURE_ENABLED],
"showChatHistory": current_app.config[CONFIG_CHAT_HISTORY_ENABLED],
}
)

Expand Down Expand Up @@ -439,6 +441,7 @@ async def setup_clients():
USE_SPEECH_INPUT_BROWSER = os.getenv("USE_SPEECH_INPUT_BROWSER", "").lower() == "true"
USE_SPEECH_OUTPUT_BROWSER = os.getenv("USE_SPEECH_OUTPUT_BROWSER", "").lower() == "true"
USE_SPEECH_OUTPUT_AZURE = os.getenv("USE_SPEECH_OUTPUT_AZURE", "").lower() == "true"
USE_CHAT_HISTORY = os.getenv("USE_CHAT_HISTORY", "").lower() == "true"

# WEBSITE_HOSTNAME is always set by App Service, RUNNING_IN_PRODUCTION is set in main.bicep
RUNNING_ON_AZURE = os.getenv("WEBSITE_HOSTNAME") is not None or os.getenv("RUNNING_IN_PRODUCTION") is not None
Expand Down Expand Up @@ -609,6 +612,7 @@ async def setup_clients():
current_app.config[CONFIG_SPEECH_INPUT_ENABLED] = USE_SPEECH_INPUT_BROWSER
current_app.config[CONFIG_SPEECH_OUTPUT_BROWSER_ENABLED] = USE_SPEECH_OUTPUT_BROWSER
current_app.config[CONFIG_SPEECH_OUTPUT_AZURE_ENABLED] = USE_SPEECH_OUTPUT_AZURE
current_app.config[CONFIG_CHAT_HISTORY_ENABLED] = USE_CHAT_HISTORY

# Various approaches to integrate GPT and external knowledge, most applications will use a single one of these patterns
# or some derivative, here we include several for exploration purposes
Expand Down
1 change: 1 addition & 0 deletions app/backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
CONFIG_SPEECH_SERVICE_LOCATION = "speech_service_location"
CONFIG_SPEECH_SERVICE_TOKEN = "speech_service_token"
CONFIG_SPEECH_SERVICE_VOICE = "speech_service_voice"
CONFIG_CHAT_HISTORY_ENABLED = "chat_history_enabled"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that a "chat history" feature means different things to different people, I think it might help for the config and env variable to be more specific.

For example, for the env var:

USE_CHAT_HISTORY_BROWSER

In future, we might have:
USE_CHAT_HISTORY_COSMOS

That'd be similar to how we did speech output.

6 changes: 6 additions & 0 deletions app/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"i18next": "^23.12.2",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.5.2",
"idb": "^8.0.0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a reasonable addition according to packagephobia and snyk:

https://packagephobia.com/result?p=idb
https://snyk.io/advisor/npm-package/idb

"ndjson-readablestream": "^1.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
1 change: 1 addition & 0 deletions app/frontend/src/api/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export type Config = {
showSpeechInput: boolean;
showSpeechOutputBrowser: boolean;
showSpeechOutputAzure: boolean;
showChatHistory: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
showChatHistory: boolean;
showChatHistoryBrowser: boolean;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per my other comment, I think we should be clear about what's enabled.

};

export type SimpleAPIResponse = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.container {
display: flex;
align-items: center;
gap: 0.375em;
cursor: pointer;
padding: 0.5rem;
}
22 changes: 22 additions & 0 deletions app/frontend/src/components/HistoryButton/HistoryButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { History24Regular } from "@fluentui/react-icons";
import { Button } from "@fluentui/react-components";
import { useTranslation } from "react-i18next";

import styles from "./HistoryButton.module.css";

interface Props {
className?: string;
onClick: () => void;
disabled?: boolean;
}

export const HistoryButton = ({ className, disabled, onClick }: Props) => {
const { t } = useTranslation();
return (
<div className={`${styles.container} ${className ?? ""}`}>
<Button icon={<History24Regular />} disabled={disabled} onClick={onClick}>
{t("history.openChatHistory")}
</Button>
</div>
);
};
1 change: 1 addition & 0 deletions app/frontend/src/components/HistoryButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./HistoryButton";
120 changes: 120 additions & 0 deletions app/frontend/src/components/HistoryItem/HistoryItem.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
.historyItem {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 8px;
border-radius: 6px;
transition: background-color 0.2s;
}

.historyItem:hover {
background-color: #f3f4f6;
}

.historyItemButton {
flex-grow: 1;
text-align: left;
padding: 0;
margin-right: 4px;
background: none;
border: none;
cursor: pointer;
}

.historyItemTitle {
font-size: 1rem;
}

.deleteIcon {
width: 20px;
height: 20px;
}

.deleteButton {
opacity: 0;
transition: opacity 0.2s;
background: none;
border: none;
cursor: pointer;
padding: 4px;
border-radius: 9999px;
color: #6b7280;
}

.historyItem:hover .deleteButton,
.deleteButton:focus {
opacity: 1;
}

.deleteButton:hover {
color: #111827;
}

.modalOverlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 50;
}

.modalContent {
background-color: white;
padding: 24px;
border-radius: 8px;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
max-width: 400px;
width: 100%;
}

.modalTitle {
font-size: 20px;
font-weight: 600;
margin-top: 0px;
margin-bottom: 16px;
}

.modalDescription {
margin-top: 0px;
margin-bottom: 16px;
}

.modalActions {
display: flex;
justify-content: flex-end;
gap: 16px;
}

.modalCancelButton,
.modalConfirmButton {
padding: 8px 16px;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
}

.modalCancelButton {
background-color: #f3f4f6;
color: #374151;
}

.modalConfirmButton {
background-color: #ef4444;
color: white;
}

.modalCancelButton:hover {
background-color: #e5e7eb;
}

.modalConfirmButton:hover {
background-color: #dc2626;
}
59 changes: 59 additions & 0 deletions app/frontend/src/components/HistoryItem/HistoryItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useState, useCallback } from "react";
import { useTranslation } from "react-i18next";
import styles from "./HistoryItem.module.css";
import { DefaultButton } from "@fluentui/react";
import { Delete24Regular } from "@fluentui/react-icons";

export interface HistoryData {
id: string;
title: string;
timestamp: number;
}

interface HistoryItemProps {
item: HistoryData;
onSelect: (id: string) => void;
onDelete: (id: string) => void;
}

export function HistoryItem({ item, onSelect, onDelete }: HistoryItemProps) {
const [isModalOpen, setIsModalOpen] = useState(false);

const handleDelete = useCallback(() => {
setIsModalOpen(false);
onDelete(item.id);
}, [item.id, onDelete]);

return (
<div className={styles.historyItem}>
<button onClick={() => onSelect(item.id)} className={styles.historyItemButton}>
<div className={styles.historyItemTitle}>{item.title}</div>
</button>
<button onClick={() => setIsModalOpen(true)} className={styles.deleteButton} aria-label="delete this chat history">
<Delete24Regular className={styles.deleteIcon} />
</button>
<DeleteHistoryModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} onConfirm={handleDelete} />
</div>
);
}

function DeleteHistoryModal({ isOpen, onClose, onConfirm }: { isOpen: boolean; onClose: () => void; onConfirm: () => void }) {
if (!isOpen) return null;
const { t } = useTranslation();
return (
<div className={styles.modalOverlay}>
<div className={styles.modalContent}>
<h2 className={styles.modalTitle}>{t("history.deleteModalTitle")}</h2>
<p className={styles.modalDescription}>{t("history.deleteModalDescription")}</p>
<div className={styles.modalActions}>
<DefaultButton onClick={onClose} className={styles.modalCancelButton}>
{t("history.cancelLabel")}
</DefaultButton>
<DefaultButton onClick={onConfirm} className={styles.modalConfirmButton}>
{t("history.deleteLabel")}
</DefaultButton>
</div>
</div>
</div>
);
}
1 change: 1 addition & 0 deletions app/frontend/src/components/HistoryItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./HistoryItem";
14 changes: 14 additions & 0 deletions app/frontend/src/components/HistoryPanel/HistoryPanel.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.group {
margin-top: 1rem;
}
.groupLabel {
font-size: 0.9rem;
font-weight: bold;
margin-top: 0.5rem;
margin-bottom: 0.2rem;
}

.footer {
display: flex;
justify-content: space-between;
}
Loading
Loading