mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Add agent_class to SystemMessageAction and display in frontend (#7935)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
5b5adc5c7b
commit
0412949018
@ -45,7 +45,15 @@ describe("Empty state", () => {
|
||||
it("should render suggestions if empty", () => {
|
||||
const { store } = renderWithProviders(<ChatInterface />, {
|
||||
preloadedState: {
|
||||
chat: { messages: [] },
|
||||
chat: {
|
||||
messages: [],
|
||||
systemMessage: {
|
||||
content: "",
|
||||
tools: [],
|
||||
openhands_version: null,
|
||||
agent_class: null
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -68,7 +76,15 @@ describe("Empty state", () => {
|
||||
it("should render the default suggestions", () => {
|
||||
renderWithProviders(<ChatInterface />, {
|
||||
preloadedState: {
|
||||
chat: { messages: [] },
|
||||
chat: {
|
||||
messages: [],
|
||||
systemMessage: {
|
||||
content: "",
|
||||
tools: [],
|
||||
openhands_version: null,
|
||||
agent_class: null
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -98,7 +114,15 @@ describe("Empty state", () => {
|
||||
const user = userEvent.setup();
|
||||
const { store } = renderWithProviders(<ChatInterface />, {
|
||||
preloadedState: {
|
||||
chat: { messages: [] },
|
||||
chat: {
|
||||
messages: [],
|
||||
systemMessage: {
|
||||
content: "",
|
||||
tools: [],
|
||||
openhands_version: null,
|
||||
agent_class: null
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -127,7 +151,15 @@ describe("Empty state", () => {
|
||||
const user = userEvent.setup();
|
||||
const { rerender } = renderWithProviders(<ChatInterface />, {
|
||||
preloadedState: {
|
||||
chat: { messages: [] },
|
||||
chat: {
|
||||
messages: [],
|
||||
systemMessage: {
|
||||
content: "",
|
||||
tools: [],
|
||||
openhands_version: null,
|
||||
agent_class: null
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
123
frontend/package-lock.json
generated
123
frontend/package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "0.33.0",
|
||||
"dependencies": {
|
||||
"@heroui/react": "2.7.6",
|
||||
"@microlink/react-json-view": "^1.26.1",
|
||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||
"@react-router/node": "^7.5.1",
|
||||
"@react-router/serve": "^7.5.1",
|
||||
@ -29,6 +30,7 @@
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"isbot": "^5.1.25",
|
||||
"jose": "^6.0.10",
|
||||
"lucide-react": "^0.501.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"posthog-js": "^1.236.2",
|
||||
"react": "^19.1.0",
|
||||
@ -3588,6 +3590,24 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@microlink/react-json-view": {
|
||||
"version": "1.26.1",
|
||||
"resolved": "https://registry.npmjs.org/@microlink/react-json-view/-/react-json-view-1.26.1.tgz",
|
||||
"integrity": "sha512-2H5QCYdZlJi+oN4YBiUYPPFTNh/KLCN9i9yz8NwmSkRqXSRXYtEVIRffc9L34jdopKGK/tK21SeuzXVJHQLkfQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-base16-styling": "~0.9.0",
|
||||
"react-lifecycles-compat": "~3.0.4",
|
||||
"react-textarea-autosize": "~8.5.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 15",
|
||||
"react-dom": ">= 15"
|
||||
}
|
||||
},
|
||||
"node_modules/@mjackson/node-fetch-server": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz",
|
||||
@ -6736,6 +6756,12 @@
|
||||
"@babel/types": "^7.20.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/base16": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.5.tgz",
|
||||
"integrity": "sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
||||
@ -7248,9 +7274,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz",
|
||||
"integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz",
|
||||
"integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.26.10",
|
||||
@ -7935,6 +7961,12 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base16": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
|
||||
"integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
@ -9120,9 +9152,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.138",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.138.tgz",
|
||||
"integrity": "sha512-FWlQc52z1dXqm+9cCJ2uyFgJkESd+16j6dBEjsgDNuHjBpuIzL8/lRc0uvh1k8RNI6waGo6tcy2DvwkTBJOLDg==",
|
||||
"version": "1.5.139",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.139.tgz",
|
||||
"integrity": "sha512-GGnRYOTdN5LYpwbIr0rwP/ZHOQSvAF6TG0LSzp28uCBb9JiXHJGmaaKw29qjNJc5bGnnp6kXJqRnGMQoELwi5w==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
@ -12502,6 +12534,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.curry": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
|
||||
"integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
@ -12716,6 +12754,15 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.501.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.501.0.tgz",
|
||||
"integrity": "sha512-E2KoyhW59fCb/yUbR3rbDer83fqn7a8NG91ZhIot2yWaPHjPyGzzsNKh40N//GezYShAuycf3TcQksRQznIsRw==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lz-string": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||
@ -13885,9 +13932,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/msw": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/msw/-/msw-2.7.4.tgz",
|
||||
"integrity": "sha512-A2kuMopOjAjNEYkn0AnB1uj+x7oBjLIunFk7Ud4icEnVWFf6iBekn8oXW4zIwcpfEdWP9sLqyVaHVzneWoGEww==",
|
||||
"version": "2.7.5",
|
||||
"resolved": "https://registry.npmjs.org/msw/-/msw-2.7.5.tgz",
|
||||
"integrity": "sha512-00MyTlY3TJutBa5kiU+jWiz2z5pNJDYHn2TgPkGkh92kMmNH43RqvMXd8y/7HxNn8RjzUbvZWYZjcS36fdb6sw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@ -15149,6 +15196,46 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-base16-styling": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz",
|
||||
"integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.16.7",
|
||||
"@types/base16": "^1.0.2",
|
||||
"@types/lodash": "^4.14.178",
|
||||
"base16": "^1.0.0",
|
||||
"color": "^3.2.1",
|
||||
"csstype": "^3.0.10",
|
||||
"lodash.curry": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-base16-styling/node_modules/color": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.3",
|
||||
"color-string": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-base16-styling/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react-base16-styling/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
@ -15225,6 +15312,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-markdown": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
|
||||
@ -17154,9 +17247,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
@ -17981,9 +18074,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/fdir": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
|
||||
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroui/react": "2.7.6",
|
||||
"@microlink/react-json-view": "^1.26.1",
|
||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||
"@react-router/node": "^7.5.1",
|
||||
"@react-router/serve": "^7.5.1",
|
||||
@ -28,6 +29,7 @@
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"isbot": "^5.1.25",
|
||||
"jose": "^6.0.10",
|
||||
"lucide-react": "^0.501.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"posthog-js": "^1.236.2",
|
||||
"react": "^19.1.0",
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
* - Please do NOT serve this file on production.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.7.3'
|
||||
const PACKAGE_VERSION = '2.7.5'
|
||||
const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
@ -8,6 +8,7 @@ interface ConversationCardContextMenuProps {
|
||||
onDelete?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onEdit?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onDisplayCost?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onShowAgentTools?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onDownloadViaVSCode?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
position?: "top" | "bottom";
|
||||
}
|
||||
@ -17,6 +18,7 @@ export function ConversationCardContextMenu({
|
||||
onDelete,
|
||||
onEdit,
|
||||
onDisplayCost,
|
||||
onShowAgentTools,
|
||||
onDownloadViaVSCode,
|
||||
position = "bottom",
|
||||
}: ConversationCardContextMenuProps) {
|
||||
@ -58,6 +60,14 @@ export function ConversationCardContextMenu({
|
||||
Display Cost
|
||||
</ContextMenuListItem>
|
||||
)}
|
||||
{onShowAgentTools && (
|
||||
<ContextMenuListItem
|
||||
testId="show-agent-tools-button"
|
||||
onClick={onShowAgentTools}
|
||||
>
|
||||
Show Agent Tools & Metadata
|
||||
</ContextMenuListItem>
|
||||
)}
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,10 +10,12 @@ import {
|
||||
} from "./conversation-state-indicator";
|
||||
import { EllipsisButton } from "./ellipsis-button";
|
||||
import { ConversationCardContextMenu } from "./conversation-card-context-menu";
|
||||
import { SystemMessageModal } from "./system-message-modal";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { BaseModal } from "../../shared/modals/base-modal/base-modal";
|
||||
import { RootState } from "#/store";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { selectSystemMessage } from "#/state/chat-slice";
|
||||
|
||||
interface ConversationCardProps {
|
||||
onClick?: () => void;
|
||||
@ -52,10 +54,12 @@ export function ConversationCard({
|
||||
const [contextMenuVisible, setContextMenuVisible] = React.useState(false);
|
||||
const [titleMode, setTitleMode] = React.useState<"view" | "edit">("view");
|
||||
const [metricsModalVisible, setMetricsModalVisible] = React.useState(false);
|
||||
const [systemModalVisible, setSystemModalVisible] = React.useState(false);
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
// Subscribe to metrics data from Redux store
|
||||
const metrics = useSelector((state: RootState) => state.metrics);
|
||||
const systemMessage = useSelector(selectSystemMessage);
|
||||
|
||||
const handleBlur = () => {
|
||||
if (inputRef.current?.value) {
|
||||
@ -129,6 +133,11 @@ export function ConversationCard({
|
||||
setMetricsModalVisible(true);
|
||||
};
|
||||
|
||||
const handleShowAgentTools = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.stopPropagation();
|
||||
setSystemModalVisible(true);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (titleMode === "edit") {
|
||||
inputRef.current?.focus();
|
||||
@ -207,6 +216,11 @@ export function ConversationCard({
|
||||
: undefined
|
||||
}
|
||||
onDisplayCost={showOptions ? handleDisplayCost : undefined}
|
||||
onShowAgentTools={
|
||||
showOptions && systemMessage
|
||||
? handleShowAgentTools
|
||||
: undefined
|
||||
}
|
||||
position={variant === "compact" ? "top" : "bottom"}
|
||||
/>
|
||||
)}
|
||||
@ -315,6 +329,12 @@ export function ConversationCard({
|
||||
)}
|
||||
</div>
|
||||
</BaseModal>
|
||||
|
||||
<SystemMessageModal
|
||||
isOpen={systemModalVisible}
|
||||
onClose={() => setSystemModalVisible(false)}
|
||||
systemMessage={systemMessage}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,238 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||
import ReactJsonView from "@microlink/react-json-view";
|
||||
import { BaseModalTitle } from "#/components/shared/modals/confirmation-modals/base-modal";
|
||||
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
|
||||
import { ModalBody } from "#/components/shared/modals/modal-body";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
// Custom JSON viewer theme that matches our application theme
|
||||
const jsonViewTheme = {
|
||||
base00: "transparent", // background
|
||||
base01: "#2d2d2d", // lighter background
|
||||
base02: "#4e4e4e", // selection background
|
||||
base03: "#6c6c6c", // comments, invisibles
|
||||
base04: "#969896", // dark foreground
|
||||
base05: "#d9d9d9", // default foreground
|
||||
base06: "#e8e8e8", // light foreground
|
||||
base07: "#ffffff", // light background
|
||||
base08: "#ff5370", // variables, red
|
||||
base09: "#f78c6c", // integers, orange
|
||||
base0A: "#ffcb6b", // booleans, yellow
|
||||
base0B: "#c3e88d", // strings, green
|
||||
base0C: "#89ddff", // support, cyan
|
||||
base0D: "#82aaff", // functions, blue
|
||||
base0E: "#c792ea", // keywords, purple
|
||||
base0F: "#ff5370", // deprecated, red
|
||||
};
|
||||
|
||||
interface SystemMessageModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
systemMessage: {
|
||||
content: string;
|
||||
tools: Array<Record<string, unknown>> | null;
|
||||
openhands_version: string | null;
|
||||
agent_class: string | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
interface FunctionData {
|
||||
name?: string;
|
||||
description?: string;
|
||||
parameters?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ToolData {
|
||||
type?: string;
|
||||
function?: FunctionData;
|
||||
name?: string;
|
||||
description?: string;
|
||||
parameters?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export function SystemMessageModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
systemMessage,
|
||||
}: SystemMessageModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState<"system" | "tools">("system");
|
||||
const [expandedTools, setExpandedTools] = useState<Record<number, boolean>>(
|
||||
{},
|
||||
);
|
||||
|
||||
if (!systemMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const toggleTool = (index: number) => {
|
||||
setExpandedTools((prev) => ({
|
||||
...prev,
|
||||
[index]: !prev[index],
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
isOpen && (
|
||||
<ModalBackdrop onClose={onClose}>
|
||||
<ModalBody
|
||||
width="medium"
|
||||
className="max-h-[80vh] flex flex-col items-start"
|
||||
>
|
||||
<div className="flex flex-col gap-6 w-full">
|
||||
<BaseModalTitle title={t("SYSTEM_MESSAGE_MODAL$TITLE")} />
|
||||
<div className="flex flex-col gap-2">
|
||||
{systemMessage.agent_class && (
|
||||
<div className="text-sm">
|
||||
<span className="font-semibold text-gray-300">
|
||||
{t("SYSTEM_MESSAGE_MODAL$AGENT_CLASS")}
|
||||
</span>{" "}
|
||||
<span className="font-medium text-gray-100">
|
||||
{systemMessage.agent_class}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{systemMessage.openhands_version && (
|
||||
<div className="text-sm">
|
||||
<span className="font-semibold text-gray-300">
|
||||
{t("SYSTEM_MESSAGE_MODAL$OPENHANDS_VERSION")}
|
||||
</span>{" "}
|
||||
<span className="text-gray-100">
|
||||
{systemMessage.openhands_version}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex border-b mb-2">
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"px-4 py-2 font-medium border-b-2 transition-colors",
|
||||
activeTab === "system"
|
||||
? "border-primary text-gray-100"
|
||||
: "border-transparent hover:text-gray-700 dark:hover:text-gray-300",
|
||||
)}
|
||||
onClick={() => setActiveTab("system")}
|
||||
>
|
||||
{t("SYSTEM_MESSAGE_MODAL$SYSTEM_MESSAGE_TAB")}
|
||||
</button>
|
||||
{systemMessage.tools && systemMessage.tools.length > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"px-4 py-2 font-medium border-b-2 transition-colors",
|
||||
activeTab === "tools"
|
||||
? "border-primary text-gray-100"
|
||||
: "border-transparent hover:text-gray-700 dark:hover:text-gray-300",
|
||||
)}
|
||||
onClick={() => setActiveTab("tools")}
|
||||
>
|
||||
{t("SYSTEM_MESSAGE_MODAL$TOOLS_TAB")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="h-[60vh] overflow-auto rounded-md">
|
||||
{activeTab === "system" && (
|
||||
<div className="p-4 whitespace-pre-wrap font-mono text-sm leading-relaxed text-gray-300 shadow-inner">
|
||||
{systemMessage.content}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "tools" &&
|
||||
systemMessage.tools &&
|
||||
systemMessage.tools.length > 0 && (
|
||||
<div className="p-2 space-y-3">
|
||||
{systemMessage.tools.map((tool, index) => {
|
||||
// Extract function data from the nested structure
|
||||
const toolData = tool as ToolData;
|
||||
const functionData = toolData.function || toolData;
|
||||
const name =
|
||||
functionData.name ||
|
||||
(toolData.type === "function" &&
|
||||
toolData.function?.name) ||
|
||||
"";
|
||||
const description =
|
||||
functionData.description ||
|
||||
(toolData.type === "function" &&
|
||||
toolData.function?.description) ||
|
||||
"";
|
||||
const parameters =
|
||||
functionData.parameters ||
|
||||
(toolData.type === "function" &&
|
||||
toolData.function?.parameters) ||
|
||||
null;
|
||||
|
||||
const isExpanded = expandedTools[index] || false;
|
||||
|
||||
return (
|
||||
<div key={index} className="rounded-md overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleTool(index)}
|
||||
className="w-full py-3 px-2 text-left flex items-center justify-between hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<h3 className="font-bold text-gray-100">
|
||||
{String(name)}
|
||||
</h3>
|
||||
</div>
|
||||
<span className="text-gray-300">
|
||||
{isExpanded ? (
|
||||
<ChevronDown size={18} />
|
||||
) : (
|
||||
<ChevronRight size={18} />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="px-2 pb-3 pt-1">
|
||||
<div className="mt-2 mb-3">
|
||||
<p className="text-sm whitespace-pre-wrap text-gray-300 leading-relaxed">
|
||||
{String(description)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Parameters section */}
|
||||
{parameters && (
|
||||
<div className="mt-2">
|
||||
<h4 className="text-sm font-semibold text-gray-300">
|
||||
{t("SYSTEM_MESSAGE_MODAL$PARAMETERS")}
|
||||
</h4>
|
||||
<div className="text-sm mt-2 p-3 bg-gray-900 rounded-md overflow-auto text-gray-300 max-h-[400px] shadow-inner">
|
||||
<ReactJsonView
|
||||
src={parameters}
|
||||
theme={jsonViewTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "tools" &&
|
||||
(!systemMessage.tools || systemMessage.tools.length === 0) && (
|
||||
<div className="flex items-center justify-center h-full p-4">
|
||||
<p className="text-gray-400">
|
||||
{t("SYSTEM_MESSAGE_MODAL$NO_TOOLS")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</ModalBackdrop>
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -1,18 +1,28 @@
|
||||
import React from "react";
|
||||
import { cn } from "#/utils/utils";
|
||||
|
||||
type ModalWidth = "small" | "medium";
|
||||
|
||||
interface ModalBodyProps {
|
||||
testID?: string;
|
||||
children: React.ReactNode;
|
||||
className?: React.HTMLProps<HTMLDivElement>["className"];
|
||||
width?: ModalWidth;
|
||||
}
|
||||
|
||||
export function ModalBody({ testID, children, className }: ModalBodyProps) {
|
||||
export function ModalBody({
|
||||
testID,
|
||||
children,
|
||||
className,
|
||||
width = "small",
|
||||
}: ModalBodyProps) {
|
||||
return (
|
||||
<div
|
||||
data-testid={testID}
|
||||
className={cn(
|
||||
"bg-base-secondary flex flex-col gap-6 items-center w-[384px] p-6 rounded-xl",
|
||||
"bg-base-secondary flex flex-col gap-6 items-center p-6 rounded-xl",
|
||||
width === "small" && "w-[384px]",
|
||||
width === "medium" && "w-[700px]",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@ -326,6 +326,7 @@ export enum I18nKey {
|
||||
ACTION_MESSAGE$BROWSE = "ACTION_MESSAGE$BROWSE",
|
||||
ACTION_MESSAGE$BROWSE_INTERACTIVE = "ACTION_MESSAGE$BROWSE_INTERACTIVE",
|
||||
ACTION_MESSAGE$THINK = "ACTION_MESSAGE$THINK",
|
||||
ACTION_MESSAGE$SYSTEM = "ACTION_MESSAGE$SYSTEM",
|
||||
OBSERVATION_MESSAGE$RUN = "OBSERVATION_MESSAGE$RUN",
|
||||
OBSERVATION_MESSAGE$RUN_IPYTHON = "OBSERVATION_MESSAGE$RUN_IPYTHON",
|
||||
OBSERVATION_MESSAGE$READ = "OBSERVATION_MESSAGE$READ",
|
||||
@ -416,4 +417,11 @@ export enum I18nKey {
|
||||
DIFF_VIEWER$ASK_OH = "DIFF_VIEWER$ASK_OH",
|
||||
DIFF_VIEWER$NO_CHANGES = "DIFF_VIEWER$NO_CHANGES",
|
||||
DIFF_VIEWER$WAITING_FOR_RUNTIME = "DIFF_VIEWER$WAITING_FOR_RUNTIME",
|
||||
SYSTEM_MESSAGE_MODAL$TITLE = "SYSTEM_MESSAGE_MODAL$TITLE",
|
||||
SYSTEM_MESSAGE_MODAL$AGENT_CLASS = "SYSTEM_MESSAGE_MODAL$AGENT_CLASS",
|
||||
SYSTEM_MESSAGE_MODAL$OPENHANDS_VERSION = "SYSTEM_MESSAGE_MODAL$OPENHANDS_VERSION",
|
||||
SYSTEM_MESSAGE_MODAL$SYSTEM_MESSAGE_TAB = "SYSTEM_MESSAGE_MODAL$SYSTEM_MESSAGE_TAB",
|
||||
SYSTEM_MESSAGE_MODAL$TOOLS_TAB = "SYSTEM_MESSAGE_MODAL$TOOLS_TAB",
|
||||
SYSTEM_MESSAGE_MODAL$PARAMETERS = "SYSTEM_MESSAGE_MODAL$PARAMETERS",
|
||||
SYSTEM_MESSAGE_MODAL$NO_TOOLS = "SYSTEM_MESSAGE_MODAL$NO_TOOLS",
|
||||
}
|
||||
|
||||
@ -4869,6 +4869,21 @@
|
||||
"es": "Pensando",
|
||||
"tr": "Düşünüyor"
|
||||
},
|
||||
"ACTION_MESSAGE$SYSTEM": {
|
||||
"en": "System Message",
|
||||
"zh-CN": "系统消息",
|
||||
"zh-TW": "系統訊息",
|
||||
"ko-KR": "시스템 메시지",
|
||||
"ja": "システムメッセージ",
|
||||
"no": "Systemmelding",
|
||||
"ar": "رسالة النظام",
|
||||
"de": "Systemnachricht",
|
||||
"fr": "Message Système",
|
||||
"it": "Messaggio di Sistema",
|
||||
"pt": "Mensagem do Sistema",
|
||||
"es": "Mensaje del Sistema",
|
||||
"tr": "Sistem Mesajı"
|
||||
},
|
||||
"OBSERVATION_MESSAGE$RUN": {
|
||||
"en": "Ran <cmd>{{observation.payload.extras.command}}</cmd>",
|
||||
"zh-CN": "运行 <cmd>{{observation.payload.extras.command}}</cmd>",
|
||||
@ -6207,5 +6222,110 @@
|
||||
"fr": "En attente du démarrage de l'exécution...",
|
||||
"tr": "Çalışma zamanının başlamasını bekliyor...",
|
||||
"de": "Warten auf den Start der Laufzeit..."
|
||||
},
|
||||
"SYSTEM_MESSAGE_MODAL$TITLE": {
|
||||
"en": "Agent Tools & Metadata",
|
||||
"zh-CN": "代理工具和元数据",
|
||||
"zh-TW": "代理工具和元數據",
|
||||
"ko-KR": "에이전트 도구 및 메타데이터",
|
||||
"ja": "エージェントツールとメタデータ",
|
||||
"no": "Agent-verktøy og metadata",
|
||||
"ar": "أدوات الوكيل والبيانات الوصفية",
|
||||
"de": "Agent-Tools und Metadaten",
|
||||
"fr": "Outils d'agent et métadonnées",
|
||||
"it": "Strumenti e metadati dell'agente",
|
||||
"pt": "Ferramentas e metadados do agente",
|
||||
"es": "Herramientas y metadatos del agente",
|
||||
"tr": "Ajan Araçları ve Meta Verileri"
|
||||
},
|
||||
"SYSTEM_MESSAGE_MODAL$AGENT_CLASS": {
|
||||
"en": "Agent Class:",
|
||||
"zh-CN": "代理类别:",
|
||||
"zh-TW": "代理類別:",
|
||||
"ko-KR": "에이전트 클래스:",
|
||||
"ja": "エージェントクラス:",
|
||||
"no": "Agent-klasse:",
|
||||
"ar": "فئة الوكيل:",
|
||||
"de": "Agent-Klasse:",
|
||||
"fr": "Classe d'agent :",
|
||||
"it": "Classe agente:",
|
||||
"pt": "Classe do agente:",
|
||||
"es": "Clase de agente:",
|
||||
"tr": "Ajan Sınıfı:"
|
||||
},
|
||||
"SYSTEM_MESSAGE_MODAL$OPENHANDS_VERSION": {
|
||||
"en": "OpenHands Version:",
|
||||
"zh-CN": "OpenHands 版本:",
|
||||
"zh-TW": "OpenHands 版本:",
|
||||
"ko-KR": "OpenHands 버전:",
|
||||
"ja": "OpenHands バージョン:",
|
||||
"no": "OpenHands-versjon:",
|
||||
"ar": "إصدار OpenHands:",
|
||||
"de": "OpenHands-Version:",
|
||||
"fr": "Version OpenHands :",
|
||||
"it": "Versione OpenHands:",
|
||||
"pt": "Versão OpenHands:",
|
||||
"es": "Versión de OpenHands:",
|
||||
"tr": "OpenHands Sürümü:"
|
||||
},
|
||||
"SYSTEM_MESSAGE_MODAL$SYSTEM_MESSAGE_TAB": {
|
||||
"en": "System Message",
|
||||
"zh-CN": "系统消息",
|
||||
"zh-TW": "系統訊息",
|
||||
"ko-KR": "시스템 메시지",
|
||||
"ja": "システムメッセージ",
|
||||
"no": "Systemmelding",
|
||||
"ar": "رسالة النظام",
|
||||
"de": "Systemnachricht",
|
||||
"fr": "Message système",
|
||||
"it": "Messaggio di sistema",
|
||||
"pt": "Mensagem do sistema",
|
||||
"es": "Mensaje del sistema",
|
||||
"tr": "Sistem Mesajı"
|
||||
},
|
||||
"SYSTEM_MESSAGE_MODAL$TOOLS_TAB": {
|
||||
"en": "Available Tools",
|
||||
"zh-CN": "可用工具",
|
||||
"zh-TW": "可用工具",
|
||||
"ko-KR": "사용 가능한 도구",
|
||||
"ja": "利用可能なツール",
|
||||
"no": "Tilgjengelige verktøy",
|
||||
"ar": "الأدوات المتاحة",
|
||||
"de": "Verfügbare Tools",
|
||||
"fr": "Outils disponibles",
|
||||
"it": "Strumenti disponibili",
|
||||
"pt": "Ferramentas disponíveis",
|
||||
"es": "Herramientas disponibles",
|
||||
"tr": "Kullanılabilir Araçlar"
|
||||
},
|
||||
"SYSTEM_MESSAGE_MODAL$PARAMETERS": {
|
||||
"en": "Parameters:",
|
||||
"zh-CN": "参数:",
|
||||
"zh-TW": "參數:",
|
||||
"ko-KR": "매개변수:",
|
||||
"ja": "パラメータ:",
|
||||
"no": "Parametere:",
|
||||
"ar": "المعلمات:",
|
||||
"de": "Parameter:",
|
||||
"fr": "Paramètres :",
|
||||
"it": "Parametri:",
|
||||
"pt": "Parâmetros:",
|
||||
"es": "Parámetros:",
|
||||
"tr": "Parametreler:"
|
||||
},
|
||||
"SYSTEM_MESSAGE_MODAL$NO_TOOLS": {
|
||||
"en": "No tools available for this agent",
|
||||
"zh-CN": "此代理没有可用的工具",
|
||||
"zh-TW": "此代理沒有可用的工具",
|
||||
"ko-KR": "이 에이전트에 사용 가능한 도구가 없습니다",
|
||||
"ja": "このエージェントで利用可能なツールはありません",
|
||||
"no": "Ingen verktøy tilgjengelig for denne agenten",
|
||||
"ar": "لا توجد أدوات متاحة لهذا الوكيل",
|
||||
"de": "Keine Tools für diesen Agenten verfügbar",
|
||||
"fr": "Aucun outil disponible pour cet agent",
|
||||
"it": "Nessuno strumento disponibile per questo agente",
|
||||
"pt": "Nenhuma ferramenta disponível para este agente",
|
||||
"es": "No hay herramientas disponibles para este agente",
|
||||
"tr": "Bu ajan için kullanılabilir araç yok"
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,15 @@ import {
|
||||
RecallObservation,
|
||||
} from "#/types/core/observations";
|
||||
|
||||
type SliceState = { messages: Message[] };
|
||||
type SliceState = {
|
||||
messages: Message[];
|
||||
systemMessage: {
|
||||
content: string;
|
||||
tools: Array<Record<string, unknown>> | null;
|
||||
openhands_version: string | null;
|
||||
agent_class: string | null;
|
||||
} | null;
|
||||
};
|
||||
|
||||
const MAX_CONTENT_LENGTH = 1000;
|
||||
|
||||
@ -25,6 +33,7 @@ const HANDLED_ACTIONS: OpenHandsEventType[] = [
|
||||
"edit",
|
||||
"recall",
|
||||
"think",
|
||||
"system",
|
||||
];
|
||||
|
||||
function getRiskText(risk: ActionSecurityRisk) {
|
||||
@ -43,6 +52,7 @@ function getRiskText(risk: ActionSecurityRisk) {
|
||||
|
||||
const initialState: SliceState = {
|
||||
messages: [],
|
||||
systemMessage: null,
|
||||
};
|
||||
|
||||
export const chatSlice = createSlice({
|
||||
@ -100,6 +110,18 @@ export const chatSlice = createSlice({
|
||||
}
|
||||
const translationID = `ACTION_MESSAGE$${actionID.toUpperCase()}`;
|
||||
let text = "";
|
||||
|
||||
if (actionID === "system") {
|
||||
// Store the system message in the state
|
||||
state.systemMessage = {
|
||||
content: action.payload.args.content,
|
||||
tools: action.payload.args.tools,
|
||||
openhands_version: action.payload.args.openhands_version,
|
||||
agent_class: action.payload.args.agent_class,
|
||||
};
|
||||
// Don't add a message for system actions
|
||||
return;
|
||||
}
|
||||
if (actionID === "run") {
|
||||
text = `Command:\n\`${action.payload.args.command}\``;
|
||||
} else if (actionID === "run_ipython") {
|
||||
@ -295,6 +317,7 @@ export const chatSlice = createSlice({
|
||||
|
||||
clearMessages(state: SliceState) {
|
||||
state.messages = [];
|
||||
state.systemMessage = null;
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -307,4 +330,9 @@ export const {
|
||||
addErrorMessage,
|
||||
clearMessages,
|
||||
} = chatSlice.actions;
|
||||
|
||||
// Selectors
|
||||
export const selectSystemMessage = (state: { chat: SliceState }) =>
|
||||
state.chat.systemMessage;
|
||||
|
||||
export default chatSlice.reducer;
|
||||
|
||||
@ -5,6 +5,9 @@ enum ActionType {
|
||||
// Represents a message from the user or agent.
|
||||
MESSAGE = "message",
|
||||
|
||||
// Represents a system message for an agent, including the system prompt and available tools.
|
||||
SYSTEM = "system",
|
||||
|
||||
// Reads the contents of a file.
|
||||
READ = "read",
|
||||
|
||||
|
||||
@ -9,6 +9,16 @@ export interface UserMessageAction extends OpenHandsActionEvent<"message"> {
|
||||
};
|
||||
}
|
||||
|
||||
export interface SystemMessageAction extends OpenHandsActionEvent<"system"> {
|
||||
source: "agent";
|
||||
args: {
|
||||
content: string;
|
||||
tools: Array<Record<string, unknown>> | null;
|
||||
openhands_version: string | null;
|
||||
agent_class: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CommandAction extends OpenHandsActionEvent<"run"> {
|
||||
source: "agent";
|
||||
args: {
|
||||
@ -145,6 +155,7 @@ export interface RecallAction extends OpenHandsActionEvent<"recall"> {
|
||||
export type OpenHandsAction =
|
||||
| UserMessageAction
|
||||
| AssistantMessageAction
|
||||
| SystemMessageAction
|
||||
| CommandAction
|
||||
| IPythonAction
|
||||
| ThinkAction
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export type OpenHandsEventType =
|
||||
| "message"
|
||||
| "system"
|
||||
| "agent_state_changed"
|
||||
| "run"
|
||||
| "read"
|
||||
|
||||
@ -68,7 +68,7 @@ class Agent(ABC):
|
||||
tools = getattr(self, 'tools', None)
|
||||
|
||||
system_message_action = SystemMessageAction(
|
||||
content=system_message, tools=tools
|
||||
content=system_message, tools=tools, agent_class=self.name
|
||||
)
|
||||
# Set the source attribute
|
||||
system_message_action._source = EventSource.AGENT # type: ignore
|
||||
|
||||
@ -46,6 +46,7 @@ class SystemMessageAction(Action):
|
||||
content: str
|
||||
tools: list[Any] | None = None
|
||||
openhands_version: str | None = openhands.__version__
|
||||
agent_class: str | None = None
|
||||
action: ActionType = ActionType.SYSTEM
|
||||
|
||||
@property
|
||||
@ -57,4 +58,6 @@ class SystemMessageAction(Action):
|
||||
ret += f'CONTENT: {self.content}'
|
||||
if self.tools:
|
||||
ret += f'\nTOOLS: {len(self.tools)} tools available'
|
||||
if self.agent_class:
|
||||
ret += f'\nAGENT_CLASS: {self.agent_class}'
|
||||
return ret
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user