mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
### Why / What / How **Why:** The copilot can ask clarifying questions in plain text, but that text gets collapsed into hidden "reasoning" UI when the LLM also calls tools in the same turn. This makes clarification questions invisible to users. The existing `ClarificationNeededResponse` model and `ClarificationQuestionsCard` UI component were built for this purpose but had no tool wiring them up. **What:** Adds a generic `ask_question` tool that produces a visible, interactive clarification card instead of collapsible plain text. Unlike the agent-generation-specific `clarify_agent_request` proposed in #12601, this tool is workflow-agnostic — usable for agent building, editing, troubleshooting, or any flow needing user input. **How:** - Backend: New `AskQuestionTool` reuses existing `ClarificationNeededResponse` model. Registered in `TOOL_REGISTRY` and `ToolName` permissions. - Frontend: New `AskQuestion/` renderer reuses `ClarificationQuestionsCard` from CreateAgent. Registered in `CUSTOM_TOOL_TYPES` (prevents collapse into reasoning) and `MessagePartRenderer`. - Guide: `agent_generation_guide.md` updated to reference `ask_question` for the clarification step. ### Changes 🏗️ - **`copilot/tools/ask_question.py`** — New generic tool: takes `question`, optional `options[]` and `keyword`, returns `ClarificationNeededResponse` - **`copilot/tools/__init__.py`** — Register `ask_question` in `TOOL_REGISTRY` - **`copilot/permissions.py`** — Add `ask_question` to `ToolName` literal - **`copilot/sdk/agent_generation_guide.md`** — Reference `ask_question` tool in clarification step - **`ChatMessagesContainer/helpers.ts`** — Add `tool-ask_question` to `CUSTOM_TOOL_TYPES` - **`MessagePartRenderer.tsx`** — Add switch case for `tool-ask_question` - **`AskQuestion/AskQuestion.tsx`** — Renderer reusing `ClarificationQuestionsCard` - **`AskQuestion/helpers.ts`** — Output parsing and animation text ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Backend format + pyright pass - [x] Frontend lint + types pass - [x] Pre-commit hooks pass - [ ] Manual test: copilot uses `ask_question` and card renders visibly (not collapsed)
94 lines
3.2 KiB
Python
94 lines
3.2 KiB
Python
"""AskQuestionTool - Ask the user a clarifying question before proceeding."""
|
|
|
|
from typing import Any
|
|
|
|
from backend.copilot.model import ChatSession
|
|
|
|
from .base import BaseTool
|
|
from .models import ClarificationNeededResponse, ClarifyingQuestion, ToolResponseBase
|
|
|
|
|
|
class AskQuestionTool(BaseTool):
|
|
"""Ask the user a clarifying question and wait for their answer.
|
|
|
|
Use this tool when the user's request is ambiguous and you need more
|
|
information before proceeding. Call find_block or other discovery tools
|
|
first to ground your question in real platform options, then call this
|
|
tool with a concrete question listing those options.
|
|
"""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "ask_question"
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
return (
|
|
"Ask the user a clarifying question. Use when the request is "
|
|
"ambiguous and you need to confirm intent, choose between options, "
|
|
"or gather missing details before proceeding."
|
|
)
|
|
|
|
@property
|
|
def parameters(self) -> dict[str, Any]:
|
|
return {
|
|
"type": "object",
|
|
"properties": {
|
|
"question": {
|
|
"type": "string",
|
|
"description": (
|
|
"The concrete question to ask the user. Should list "
|
|
"real options when applicable."
|
|
),
|
|
},
|
|
"options": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
"description": (
|
|
"Options for the user to choose from "
|
|
"(e.g. ['Email', 'Slack', 'Google Docs'])."
|
|
),
|
|
},
|
|
"keyword": {
|
|
"type": "string",
|
|
"description": "Short label identifying what the question is about.",
|
|
},
|
|
},
|
|
"required": ["question"],
|
|
}
|
|
|
|
@property
|
|
def requires_auth(self) -> bool:
|
|
return False
|
|
|
|
async def _execute(
|
|
self,
|
|
user_id: str | None,
|
|
session: ChatSession,
|
|
**kwargs: Any,
|
|
) -> ToolResponseBase:
|
|
del user_id # unused; required by BaseTool contract
|
|
question_raw = kwargs.get("question")
|
|
if not isinstance(question_raw, str) or not question_raw.strip():
|
|
raise ValueError("ask_question requires a non-empty 'question' string")
|
|
question = question_raw.strip()
|
|
raw_options = kwargs.get("options", [])
|
|
if not isinstance(raw_options, list):
|
|
raw_options = []
|
|
options: list[str] = [str(o) for o in raw_options if o]
|
|
raw_keyword = kwargs.get("keyword", "")
|
|
keyword: str = str(raw_keyword) if raw_keyword else ""
|
|
session_id = session.session_id if session else None
|
|
|
|
example = ", ".join(options) if options else None
|
|
clarifying_question = ClarifyingQuestion(
|
|
question=question,
|
|
keyword=keyword,
|
|
example=example,
|
|
)
|
|
return ClarificationNeededResponse(
|
|
message=question,
|
|
session_id=session_id,
|
|
questions=[clarifying_question],
|
|
)
|