Files
AutoGPT/autogpt_platform/backend/backend/copilot/tools/base.py
Zamil Majdy 1d042c4f10 refactor(platform): remove long-running tool infrastructure and fix copilot bugs
Remove the entire long-running/async tool execution infrastructure that
became redundant after the executor system was introduced. All tools now
execute uniformly through execute_tool() with heartbeat support.

Backend cleanup:
- Remove _execute_long_running_tool_with_streaming and is_long_running
- Delete completion_consumer.py and completion_handler.py (dead code)
- Remove _generate_llm_continuation (both variants), _update_pending_operation,
  _mark_operation_started/completed
- Remove operation_id plumbing from stream_registry, routes, executor
- Remove LongRunningCallback from SDK tool_adapter
- Remove dead config fields (stream_completion_name, etc.)

Frontend cleanup:
- Remove OperationInProgressResponse checks from CreateAgent/EditAgent
- Remove debug logging from useCopilotPage
- Delete dead task-level stream route and useAsymptoticProgress hook
- Regenerate OpenAPI types

Bug fixes:
- #3: Pessimistic input lock — disable chat while session loading or errored
- #3: Replace O(N) SCAN with O(1) direct lookup in get_active_task_for_session
- #1: Content-based deduplication to prevent intro message replay on resume
- Fix parallel_tool_calls_test to match new _yield_tool_call signature
2026-02-23 18:34:46 +07:00

120 lines
3.5 KiB
Python

"""Base classes and shared utilities for chat tools."""
import logging
from typing import Any
from openai.types.chat import ChatCompletionToolParam
from backend.copilot.model import ChatSession
from backend.copilot.response_model import StreamToolOutputAvailable
from .models import ErrorResponse, NeedLoginResponse, ToolResponseBase
logger = logging.getLogger(__name__)
class BaseTool:
"""Base class for all chat tools."""
@property
def name(self) -> str:
"""Tool name for OpenAI function calling."""
raise NotImplementedError
@property
def description(self) -> str:
"""Tool description for OpenAI."""
raise NotImplementedError
@property
def parameters(self) -> dict[str, Any]:
"""Tool parameters schema for OpenAI."""
raise NotImplementedError
@property
def requires_auth(self) -> bool:
"""Whether this tool requires authentication."""
return False
def as_openai_tool(self) -> ChatCompletionToolParam:
"""Convert to OpenAI tool format."""
return ChatCompletionToolParam(
type="function",
function={
"name": self.name,
"description": self.description,
"parameters": self.parameters,
},
)
async def execute(
self,
user_id: str | None,
session: ChatSession,
tool_call_id: str,
**kwargs,
) -> StreamToolOutputAvailable:
"""Execute the tool with authentication check.
Args:
user_id: User ID (may be anonymous like "anon_123")
session_id: Chat session ID
**kwargs: Tool-specific parameters
Returns:
Pydantic response object
"""
if self.requires_auth and not user_id:
logger.error(
f"Attempted tool call for {self.name} but user not authenticated"
)
return StreamToolOutputAvailable(
toolCallId=tool_call_id,
toolName=self.name,
output=NeedLoginResponse(
message=f"Please sign in to use {self.name}",
session_id=session.session_id,
).model_dump_json(),
success=False,
)
try:
result = await self._execute(user_id, session, **kwargs)
return StreamToolOutputAvailable(
toolCallId=tool_call_id,
toolName=self.name,
output=result.model_dump_json(),
)
except Exception as e:
logger.error(f"Error in {self.name}: {e}", exc_info=True)
return StreamToolOutputAvailable(
toolCallId=tool_call_id,
toolName=self.name,
output=ErrorResponse(
message=f"An error occurred while executing {self.name}",
error=str(e),
session_id=session.session_id,
).model_dump_json(),
success=False,
)
async def _execute(
self,
user_id: str | None,
session: ChatSession,
**kwargs,
) -> ToolResponseBase:
"""Internal execution logic to be implemented by subclasses.
Args:
user_id: User ID (authenticated or anonymous)
session_id: Chat session ID
**kwargs: Tool-specific parameters
Returns:
Pydantic response object
"""
raise NotImplementedError