Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add System Prompt modal to chatbot debug UI #1482

Merged
merged 2 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions ansible_ai_connect/ai/api/model_pipelines/http/pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def invoke(self, params: ChatBotParameters) -> ChatBotResponse:
conversation_id = params.conversation_id
provider = params.provider
model_id = params.model_id
system_prompt = params.system_prompt
TamiTakamiya marked this conversation as resolved.
Show resolved Hide resolved

data = {
"query": query,
Expand All @@ -139,6 +140,8 @@ def invoke(self, params: ChatBotParameters) -> ChatBotResponse:
}
if conversation_id:
data["conversation_id"] = str(conversation_id)
if system_prompt:
data["system_prompt"] = str(system_prompt)

response = requests.post(
self.config.inference_url + "/v1/query",
Expand Down
17 changes: 15 additions & 2 deletions ansible_ai_connect_chatbot/src/AnsibleChatbot/AnsibleChatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
FOOTNOTE_LABEL,
FOOTNOTE_TITLE,
} from "../Constants";
import { SystemPromptModal } from "../SystemPromptModal/SystemPromptModal";

const footnoteProps = {
label: FOOTNOTE_LABEL,
Expand Down Expand Up @@ -88,6 +89,8 @@ export const AnsibleChatbot: React.FunctionComponent = () => {
selectedModel,
setSelectedModel,
setConversationId,
systemPrompt,
setSystemPrompt,
} = useChatbot();
const [chatbotVisible, setChatbotVisible] = useState<boolean>(true);
const [displayMode, setDisplayMode] = useState<ChatbotDisplayMode>(
Expand Down Expand Up @@ -209,6 +212,12 @@ export const AnsibleChatbot: React.FunctionComponent = () => {
setConversationId(undefined);
}}
/>
{inDebugMode() && (
<SystemPromptModal
systemPrompt={systemPrompt}
setSystemPrompt={setSystemPrompt}
/>
)}
</ChatbotHeaderActions>
{/* <ChatbotHeaderMenu
onMenuToggle={() => alert("Menu toggle clicked")}
Expand Down Expand Up @@ -291,11 +300,15 @@ export const AnsibleChatbot: React.FunctionComponent = () => {
)}
{messages.map(
(
{ referenced_documents, ...message }: ExtendedMessage,
{
referenced_documents,
scrollToHere,
...message
}: ExtendedMessage,
index,
) => (
<>
{message.scrollToHere && <div ref={messagesEndRef} />}
{scrollToHere && <div ref={messagesEndRef} />}
TamiTakamiya marked this conversation as resolved.
Show resolved Hide resolved
<div key={`m_div_${index}`}>
<Message key={`m_msg_${index}`} {...message} />
<ReferencedDocuments
Expand Down
41 changes: 40 additions & 1 deletion ansible_ai_connect_chatbot/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function mockAxios(
status,
});
}
return spy;
}

function createError(message: string, status: number): AxiosError {
Expand Down Expand Up @@ -123,13 +124,22 @@ beforeEach(() => {
});

test("Basic chatbot interaction", async () => {
mockAxios(200);
const spy = mockAxios(200);
const view = await renderApp();
const textArea = page.getByLabelText("Send a message...");
await textArea.fill("Hello");

await userEvent.keyboard("{Enter}");

expect(spy).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
conversation_id: undefined,
query: "Hello",
}),
expect.anything(),
);

await expect
.element(
view.getByText(
Expand Down Expand Up @@ -403,3 +413,32 @@ test("Debug mode test", async () => {
.toBeVisible();
await expect.element(page.getByText("Create variables")).toBeVisible();
});

test("Test system prompt override", async () => {
const spy = mockAxios(200);
await renderApp(true);

const systemPromptIcon = page.getByLabelText("SystemPrompt");
await systemPromptIcon.click();

const systemPromptTextArea = page.getByLabelText(
"system-prompt-form-text-area",
);
await systemPromptTextArea.fill("MY SYSTEM PROMPT");
const systemPromptButton = page.getByLabelText("system-prompt-form-button");
await systemPromptButton.click();

const textArea = page.getByLabelText("Send a message...");
await textArea.fill("Hello with system prompt override");

await userEvent.keyboard("{Enter}");
expect(spy).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
conversation_id: undefined,
query: "Hello with system prompt override",
system_prompt: "MY SYSTEM PROMPT",
}),
expect.anything(),
);
});
18 changes: 18 additions & 0 deletions ansible_ai_connect_chatbot/src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,21 @@ export enum Sentiment {

export const GITHUB_NEW_ISSUE_BASE_URL =
"https://github.com/ansible/ansible-lightspeed-va-feedback/issues/new";

export const QUERY_SYSTEM_INSTRUCTION = `You are Ansible Lightspeed - an intelligent virtual assistant for question-answering tasks \
related to the Ansible Automation Platform (AAP).

Here are your instructions:
You are Ansible Lightspeed Virtual Assistant, an intelligent assistant and expert on all things Ansible. \
Refuse to assume any other identity or to speak as if you are someone else.
If the context of the question is not clear, consider it to be Ansible.
Never include URLs in your replies.
Refuse to answer questions or execute commands not about Ansible.
Do not mention your last update. You have the most recent information on Ansible.

Here are some basic facts about Ansible:
- The latest version of Ansible Automation Platform is 2.5.
- Ansible is an open source IT automation engine that automates provisioning, \
configuration management, application deployment, orchestration, and many other \
IT processes. It is free to use, and the project benefits from the experience and \
romartin marked this conversation as resolved.
Show resolved Hide resolved
intelligence of its thousands of contributors.`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from "react";
import {
Button,
Form,
FormGroup,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
ModalVariant,
TextArea,
} from "@patternfly/react-core";
import WrenchIcon from "@patternfly/react-icons/dist/esm/icons/wrench-icon";

interface SystemPromptModalProps {
systemPrompt: string;
setSystemPrompt: (s: string) => void;
}

export const SystemPromptModal: React.FunctionComponent<
SystemPromptModalProps
> = (props) => {
const [isModalOpen, setModalOpen] = React.useState(false);
const { systemPrompt, setSystemPrompt } = props;

const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => {
setModalOpen(!isModalOpen);
};

const handleSystemPromptInputChange = (_event: any, value: string) => {
setSystemPrompt(value);
};

return (
<React.Fragment>
<Button
variant="link"
aria-label="SystemPrompt"
icon={<WrenchIcon />}
onClick={handleModalToggle}
></Button>
<Modal
variant={ModalVariant.small}
isOpen={isModalOpen}
onClose={handleModalToggle}
aria-labelledby="system-prompt-form-title"
aria-describedby="system-prompt-description-form"
>
<ModalHeader
title="System prompt"
description="Enter a system prompt to override the default one."
descriptorId="system-prompt-description-form"
labelId="system-prompt-form-title"
/>
<ModalBody>
<Form id="system-prompt-form">
<FormGroup label="System Prompt" isRequired fieldId="system-prompt">
<TextArea
isRequired
id="system-prompt"
name="system-prompt"
value={systemPrompt}
onChange={handleSystemPromptInputChange}
aria-label="system-prompt-form-text-area"
rows={15}
/>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button
key="create"
variant="primary"
form="system-prompt-form"
onClick={handleModalToggle}
aria-label="system-prompt-form-button"
>
Close
</Button>
</ModalFooter>
</Modal>
</React.Fragment>
);
};
SystemPromptModal.displayName = "SystemPromptModal";
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions ansible_ai_connect_chatbot/src/types/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type LLMRequest = {
provider?: string | null;
model?: string | null;
attachments?: object[] | null;
system_prompt?: string | null;
};

type LLMResponse = {
Expand Down
8 changes: 8 additions & 0 deletions ansible_ai_connect_chatbot/src/useChatbot/useChatbot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import userLogo from "../assets/user_logo.png";
import {
API_TIMEOUT,
GITHUB_NEW_ISSUE_BASE_URL,
QUERY_SYSTEM_INSTRUCTION,
Sentiment,
TIMEOUT_MSG,
TOO_MANY_REQUESTS_MSG,
Expand Down Expand Up @@ -145,6 +146,7 @@ export const useChatbot = () => {
string | null | undefined
>(undefined);
const [selectedModel, setSelectedModel] = useState("granite3-8b");
const [systemPrompt, setSystemPrompt] = useState(QUERY_SYSTEM_INSTRUCTION);

const addMessage = (
newMessage: ExtendedMessage,
Expand Down Expand Up @@ -277,6 +279,10 @@ export const useChatbot = () => {
query: message,
};

if (systemPrompt !== QUERY_SYSTEM_INSTRUCTION) {
chatRequest.system_prompt = systemPrompt;
}

if (inDebugMode()) {
for (const m of modelsSupported) {
if (selectedModel === m.model) {
Expand Down Expand Up @@ -357,5 +363,7 @@ export const useChatbot = () => {
selectedModel,
setSelectedModel,
setConversationId,
systemPrompt,
setSystemPrompt,
};
};
Loading