mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
feat(frontend): integrate with the API to create a sub-conversation for the planning agent (#11730)
This commit is contained in:
parent
833aae1833
commit
d6fab190bf
@ -60,6 +60,8 @@ class V1ConversationService {
|
||||
selected_branch?: string,
|
||||
conversationInstructions?: string,
|
||||
trigger?: ConversationTrigger,
|
||||
parent_conversation_id?: string,
|
||||
agent_type?: "default" | "plan",
|
||||
): Promise<V1AppConversationStartTask> {
|
||||
const body: V1AppConversationStartRequest = {
|
||||
selected_repository: selectedRepository,
|
||||
@ -67,6 +69,8 @@ class V1ConversationService {
|
||||
selected_branch,
|
||||
title: conversationInstructions,
|
||||
trigger,
|
||||
parent_conversation_id: parent_conversation_id || null,
|
||||
agent_type,
|
||||
};
|
||||
|
||||
// Add initial message if provided
|
||||
|
||||
@ -30,6 +30,8 @@ export interface V1AppConversationStartRequest {
|
||||
title?: string | null;
|
||||
trigger?: ConversationTrigger | null;
|
||||
pr_number?: number[];
|
||||
parent_conversation_id?: string | null;
|
||||
agent_type?: "default" | "plan";
|
||||
}
|
||||
|
||||
export type V1AppConversationStartTaskStatus =
|
||||
|
||||
@ -77,6 +77,7 @@ export interface Conversation {
|
||||
session_api_key: string | null;
|
||||
pr_number?: number[] | null;
|
||||
conversation_version?: "V0" | "V1";
|
||||
sub_conversation_ids?: string[];
|
||||
}
|
||||
|
||||
export interface ResultSet<T> {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useEffect } from "react";
|
||||
import React, { useMemo, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Typography } from "#/ui/typography";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
@ -11,10 +11,12 @@ import { cn } from "#/utils/utils";
|
||||
import { USE_PLANNING_AGENT } from "#/utils/feature-flags";
|
||||
import { useAgentState } from "#/hooks/use-agent-state";
|
||||
import { AgentState } from "#/types/agent-state";
|
||||
import { useActiveConversation } from "#/hooks/query/use-active-conversation";
|
||||
import { useCreateConversation } from "#/hooks/mutation/use-create-conversation";
|
||||
import { displaySuccessToast } from "#/utils/custom-toast-handlers";
|
||||
|
||||
export function ChangeAgentButton() {
|
||||
const { t } = useTranslation();
|
||||
const [contextMenuOpen, setContextMenuOpen] = React.useState(false);
|
||||
const [contextMenuOpen, setContextMenuOpen] = useState<boolean>(false);
|
||||
|
||||
const conversationMode = useConversationStore(
|
||||
(state) => state.conversationMode,
|
||||
@ -28,8 +30,14 @@ export function ChangeAgentButton() {
|
||||
|
||||
const { curAgentState } = useAgentState();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isAgentRunning = curAgentState === AgentState.RUNNING;
|
||||
|
||||
const { data: conversation } = useActiveConversation();
|
||||
const { mutate: createConversation, isPending: isCreatingConversation } =
|
||||
useCreateConversation();
|
||||
|
||||
// Close context menu when agent starts running
|
||||
useEffect(() => {
|
||||
if (isAgentRunning && contextMenuOpen) {
|
||||
@ -37,6 +45,40 @@ export function ChangeAgentButton() {
|
||||
}
|
||||
}, [isAgentRunning, contextMenuOpen]);
|
||||
|
||||
const handlePlanClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement> | KeyboardEvent,
|
||||
) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Set conversation mode to "plan" immediately
|
||||
setConversationMode("plan");
|
||||
|
||||
// Check if sub_conversation_ids is not empty
|
||||
if (
|
||||
(conversation?.sub_conversation_ids &&
|
||||
conversation.sub_conversation_ids.length > 0) ||
|
||||
!conversation?.conversation_id
|
||||
) {
|
||||
// Do nothing if both conditions are true
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new sub-conversation if we have a current conversation ID
|
||||
createConversation(
|
||||
{
|
||||
parentConversationId: conversation.conversation_id,
|
||||
agentType: "plan",
|
||||
},
|
||||
{
|
||||
onSuccess: () =>
|
||||
displaySuccessToast(
|
||||
t(I18nKey.PLANNING_AGENTT$PLANNING_AGENT_INITIALIZED),
|
||||
),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// Handle Shift + Tab keyboard shortcut to cycle through modes
|
||||
useEffect(() => {
|
||||
if (!shouldUsePlanningAgent || isAgentRunning) {
|
||||
@ -52,7 +94,11 @@ export function ChangeAgentButton() {
|
||||
|
||||
// Cycle between modes: code -> plan -> code
|
||||
const nextMode = conversationMode === "code" ? "plan" : "code";
|
||||
setConversationMode(nextMode);
|
||||
if (nextMode === "plan") {
|
||||
handlePlanClick(event);
|
||||
} else {
|
||||
setConversationMode(nextMode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -80,12 +126,6 @@ export function ChangeAgentButton() {
|
||||
setConversationMode("code");
|
||||
};
|
||||
|
||||
const handlePlanClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setConversationMode("plan");
|
||||
};
|
||||
|
||||
const isExecutionAgent = conversationMode === "code";
|
||||
|
||||
const buttonLabel = useMemo(() => {
|
||||
@ -102,6 +142,8 @@ export function ChangeAgentButton() {
|
||||
return <LessonPlanIcon width={18} height={18} color="#ffffff" />;
|
||||
}, [isExecutionAgent]);
|
||||
|
||||
const isButtonDisabled = isAgentRunning || isCreatingConversation;
|
||||
|
||||
if (!shouldUsePlanningAgent) {
|
||||
return null;
|
||||
}
|
||||
@ -111,11 +153,11 @@ export function ChangeAgentButton() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleButtonClick}
|
||||
disabled={isAgentRunning}
|
||||
disabled={isButtonDisabled}
|
||||
className={cn(
|
||||
"flex items-center border border-[#4B505F] rounded-[100px] transition-opacity",
|
||||
!isExecutionAgent && "border-[#597FF4] bg-[#4A67BD]",
|
||||
isAgentRunning
|
||||
isButtonDisabled
|
||||
? "opacity-50 cursor-not-allowed"
|
||||
: "cursor-pointer hover:opacity-80",
|
||||
)}
|
||||
|
||||
@ -17,6 +17,8 @@ interface CreateConversationVariables {
|
||||
suggestedTask?: SuggestedTask;
|
||||
conversationInstructions?: string;
|
||||
createMicroagent?: CreateMicroagent;
|
||||
parentConversationId?: string;
|
||||
agentType?: "default" | "plan";
|
||||
}
|
||||
|
||||
// Response type that combines both V1 and legacy responses
|
||||
@ -44,6 +46,8 @@ export const useCreateConversation = () => {
|
||||
suggestedTask,
|
||||
conversationInstructions,
|
||||
createMicroagent,
|
||||
parentConversationId,
|
||||
agentType,
|
||||
} = variables;
|
||||
|
||||
const useV1 = USE_V1_CONVERSATION_API() && !createMicroagent;
|
||||
@ -57,6 +61,8 @@ export const useCreateConversation = () => {
|
||||
repository?.branch,
|
||||
conversationInstructions,
|
||||
undefined, // trigger - will be set by backend
|
||||
parentConversationId,
|
||||
agentType,
|
||||
);
|
||||
|
||||
// Return a special task ID that the frontend will recognize
|
||||
|
||||
@ -946,4 +946,5 @@ export enum I18nKey {
|
||||
COMMON$LET_S_WORK_ON_A_PLAN = "COMMON$LET_S_WORK_ON_A_PLAN",
|
||||
COMMON$CODE_AGENT_DESCRIPTION = "COMMON$CODE_AGENT_DESCRIPTION",
|
||||
COMMON$PLAN_AGENT_DESCRIPTION = "COMMON$PLAN_AGENT_DESCRIPTION",
|
||||
PLANNING_AGENTT$PLANNING_AGENT_INITIALIZED = "PLANNING_AGENTT$PLANNING_AGENT_INITIALIZED",
|
||||
}
|
||||
|
||||
@ -15134,5 +15134,21 @@
|
||||
"tr": "Hedefleri belirtin, görevleri yapılandırın ve sonraki adımlarınızı belirleyin.",
|
||||
"de": "Umreißen Sie Ziele, strukturieren Sie Aufgaben und planen Sie Ihre nächsten Schritte.",
|
||||
"uk": "Окресліть цілі, структуруйте завдання та сплануйте наступні кроки."
|
||||
},
|
||||
"PLANNING_AGENTT$PLANNING_AGENT_INITIALIZED": {
|
||||
"en": "Planning agent initialized",
|
||||
"ja": "プランニングエージェントが初期化されました",
|
||||
"zh-CN": "规划代理已初始化",
|
||||
"zh-TW": "規劃代理已初始化",
|
||||
"ko-KR": "계획 에이전트가 초기화되었습니다",
|
||||
"no": "Planleggingsagent er initialisert",
|
||||
"it": "Agente di pianificazione inizializzato",
|
||||
"pt": "Agente de planejamento inicializado",
|
||||
"es": "Agente de planificación inicializado",
|
||||
"ar": "تم تهيئة وكيل التخطيط",
|
||||
"fr": "Agent de planification initialisé",
|
||||
"tr": "Planlama ajanı başlatıldı",
|
||||
"de": "Planungsagent wurde initialisiert",
|
||||
"uk": "Агент планування ініціалізовано"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user