diff --git a/frontend/src/api/open-hands.ts b/frontend/src/api/open-hands.ts index b3ce52a566..072588ce47 100644 --- a/frontend/src/api/open-hands.ts +++ b/frontend/src/api/open-hands.ts @@ -183,6 +183,13 @@ class OpenHands { static async getVSCodeUrl(): Promise { return request(`/api/vscode-url`, {}, false, false, 1); } + + static async getRuntimeId(): Promise<{ runtime_id: string }> { + const response = await request("/api/config"); + const data = await response.json(); + + return data; + } } export default OpenHands; diff --git a/frontend/src/components/chat-interface.tsx b/frontend/src/components/chat-interface.tsx index 70f13121ff..361ea744ca 100644 --- a/frontend/src/components/chat-interface.tsx +++ b/frontend/src/components/chat-interface.tsx @@ -21,14 +21,18 @@ import { ScrollToBottomButton } from "./scroll-to-bottom-button"; import { Suggestions } from "./suggestions"; import { SUGGESTIONS } from "#/utils/suggestions"; import BuildIt from "#/icons/build-it.svg?react"; -import { useWsClient } from "#/context/ws-client-provider"; +import { + useWsClient, + WsClientProviderStatus, +} from "#/context/ws-client-provider"; +import OpenHands from "#/api/open-hands"; const isErrorMessage = ( message: Message | ErrorMessage, ): message is ErrorMessage => "error" in message; export function ChatInterface() { - const { send, isLoadingMessages } = useWsClient(); + const { send, status, isLoadingMessages } = useWsClient(); const dispatch = useDispatch(); const scrollRef = React.useRef(null); @@ -44,6 +48,23 @@ export function ChatInterface() { const [feedbackModalIsOpen, setFeedbackModalIsOpen] = React.useState(false); const [messageToSend, setMessageToSend] = React.useState(null); + React.useEffect(() => { + if (status === WsClientProviderStatus.ACTIVE) { + try { + OpenHands.getRuntimeId().then(({ runtime_id }) => { + // eslint-disable-next-line no-console + console.log( + "Runtime ID: %c%s", + "background: #444; color: #ffeb3b; font-weight: bold; padding: 2px 4px; border-radius: 4px;", + runtime_id, + ); + }); + } catch (e) { + console.warn("Runtime ID not available in this environment"); + } + } + }, [status]); + const handleSendMessage = async (content: string, files: File[]) => { posthog.capture("user_message_sent", { current_message_count: messages.length, diff --git a/openhands/server/listen.py b/openhands/server/listen.py index d18bea2774..c1b178b9c2 100644 --- a/openhands/server/listen.py +++ b/openhands/server/listen.py @@ -11,6 +11,7 @@ import requests from pathspec import PathSpec from pathspec.patterns import GitWildMatchPattern +from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.security.options import SecurityAnalyzers from openhands.server.data_models.feedback import FeedbackDataModel, store_feedback from openhands.server.github import ( @@ -564,6 +565,29 @@ def sanitize_filename(filename): return filename +@app.get('/api/config') +async def get_remote_runtime_config(request: Request): + """Retrieve the remote runtime configuration. + + Currently, this is the runtime ID. + """ + try: + runtime = request.state.conversation.runtime + if isinstance(runtime, RemoteRuntime): + return JSONResponse(content={'runtime_id': runtime.runtime_id}) + else: + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={'error': 'Runtime ID not available in this environment'}, + ) + except Exception as e: + logger.error(e) + return JSONResponse( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + content={'error': 'Something went wrong'}, + ) + + @app.post('/api/upload-files') async def upload_file(request: Request, files: list[UploadFile]): """Upload a list of files to the workspace.