Workflow Copilot: review and approve/reject changes (#4559)
This commit is contained in:
committed by
GitHub
parent
cb2a72775d
commit
c0f361bb6e
@@ -140,6 +140,7 @@ from skyvern.schemas.workflows import BlockStatus, BlockType, WorkflowStatus
|
||||
from skyvern.webeye.actions.actions import Action
|
||||
|
||||
LOG = structlog.get_logger()
|
||||
_UNSET = object()
|
||||
|
||||
|
||||
def _serialize_proxy_location(proxy_location: ProxyLocationInput) -> str | None:
|
||||
@@ -3667,6 +3668,33 @@ class AgentDB(BaseAlchemyDB):
|
||||
await session.refresh(new_chat)
|
||||
return WorkflowCopilotChat.model_validate(new_chat)
|
||||
|
||||
async def update_workflow_copilot_chat(
|
||||
self,
|
||||
organization_id: str,
|
||||
workflow_copilot_chat_id: str,
|
||||
proposed_workflow: dict | None | object = _UNSET,
|
||||
auto_accept: bool | None = None,
|
||||
) -> WorkflowCopilotChat | None:
|
||||
async with self.Session() as session:
|
||||
chat = (
|
||||
await session.scalars(
|
||||
select(WorkflowCopilotChatModel)
|
||||
.where(WorkflowCopilotChatModel.organization_id == organization_id)
|
||||
.where(WorkflowCopilotChatModel.workflow_copilot_chat_id == workflow_copilot_chat_id)
|
||||
)
|
||||
).first()
|
||||
if not chat:
|
||||
return None
|
||||
|
||||
if proposed_workflow is not _UNSET:
|
||||
chat.proposed_workflow = proposed_workflow
|
||||
if auto_accept is not None:
|
||||
chat.auto_accept = auto_accept
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(chat)
|
||||
return WorkflowCopilotChat.model_validate(chat)
|
||||
|
||||
async def create_workflow_copilot_chat_message(
|
||||
self,
|
||||
organization_id: str,
|
||||
|
||||
@@ -1098,6 +1098,8 @@ class WorkflowCopilotChatModel(Base):
|
||||
workflow_copilot_chat_id = Column(String, primary_key=True, default=generate_workflow_copilot_chat_id)
|
||||
organization_id = Column(String, nullable=False)
|
||||
workflow_permanent_id = Column(String, nullable=False, index=True)
|
||||
proposed_workflow = Column(JSON, nullable=True)
|
||||
auto_accept = Column(Boolean, nullable=True, default=False)
|
||||
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
||||
modified_at = Column(
|
||||
|
||||
@@ -26,6 +26,7 @@ from skyvern.forge.sdk.schemas.workflow_copilot import (
|
||||
WorkflowCopilotChatMessage,
|
||||
WorkflowCopilotChatRequest,
|
||||
WorkflowCopilotChatSender,
|
||||
WorkflowCopilotClearProposedWorkflowRequest,
|
||||
WorkflowCopilotProcessingUpdate,
|
||||
WorkflowCopilotStreamErrorUpdate,
|
||||
WorkflowCopilotStreamMessageType,
|
||||
@@ -448,6 +449,13 @@ async def workflow_copilot_chat_post(
|
||||
)
|
||||
return
|
||||
|
||||
if updated_workflow and chat.auto_accept is not True:
|
||||
await app.DATABASE.update_workflow_copilot_chat(
|
||||
organization_id=chat.organization_id,
|
||||
workflow_copilot_chat_id=chat.workflow_copilot_chat_id,
|
||||
proposed_workflow=updated_workflow.model_dump(mode="json"),
|
||||
)
|
||||
|
||||
await app.DATABASE.create_workflow_copilot_chat_message(
|
||||
organization_id=chat.organization_id,
|
||||
workflow_copilot_chat_id=chat.workflow_copilot_chat_id,
|
||||
@@ -518,17 +526,35 @@ async def workflow_copilot_chat_history(
|
||||
organization_id=organization.organization_id,
|
||||
workflow_permanent_id=workflow_permanent_id,
|
||||
)
|
||||
if not latest_chat:
|
||||
return WorkflowCopilotChatHistoryResponse(workflow_copilot_chat_id=None, chat_history=[])
|
||||
chat_messages = await app.DATABASE.get_workflow_copilot_chat_messages(
|
||||
workflow_copilot_chat_id=latest_chat.workflow_copilot_chat_id,
|
||||
)
|
||||
if latest_chat:
|
||||
chat_messages = await app.DATABASE.get_workflow_copilot_chat_messages(latest_chat.workflow_copilot_chat_id)
|
||||
else:
|
||||
chat_messages = []
|
||||
return WorkflowCopilotChatHistoryResponse(
|
||||
workflow_copilot_chat_id=latest_chat.workflow_copilot_chat_id,
|
||||
workflow_copilot_chat_id=latest_chat.workflow_copilot_chat_id if latest_chat else None,
|
||||
chat_history=convert_to_history_messages(chat_messages),
|
||||
proposed_workflow=latest_chat.proposed_workflow if latest_chat else None,
|
||||
auto_accept=latest_chat.auto_accept if latest_chat else None,
|
||||
)
|
||||
|
||||
|
||||
@base_router.post(
|
||||
"/workflow/copilot/clear-proposed-workflow", include_in_schema=False, status_code=status.HTTP_204_NO_CONTENT
|
||||
)
|
||||
async def workflow_copilot_clear_proposed_workflow(
|
||||
clear_request: WorkflowCopilotClearProposedWorkflowRequest,
|
||||
organization: Organization = Depends(org_auth_service.get_current_org),
|
||||
) -> None:
|
||||
updated_chat = await app.DATABASE.update_workflow_copilot_chat(
|
||||
organization_id=organization.organization_id,
|
||||
workflow_copilot_chat_id=clear_request.workflow_copilot_chat_id,
|
||||
proposed_workflow=None,
|
||||
auto_accept=clear_request.auto_accept,
|
||||
)
|
||||
if not updated_chat:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Chat not found")
|
||||
|
||||
|
||||
def convert_to_history_messages(
|
||||
messages: list[WorkflowCopilotChatMessage],
|
||||
) -> list[WorkflowCopilotChatHistoryMessage]:
|
||||
|
||||
@@ -10,6 +10,8 @@ class WorkflowCopilotChat(BaseModel):
|
||||
workflow_copilot_chat_id: str = Field(..., description="ID for the workflow copilot chat")
|
||||
organization_id: str = Field(..., description="Organization ID for the chat")
|
||||
workflow_permanent_id: str = Field(..., description="Workflow permanent ID for the chat")
|
||||
proposed_workflow: dict | None = Field(None, description="Latest workflow proposed by the copilot")
|
||||
auto_accept: bool | None = Field(False, description="Whether copilot auto-accepts workflow updates")
|
||||
created_at: datetime = Field(..., description="When the chat was created")
|
||||
modified_at: datetime = Field(..., description="When the chat was last modified")
|
||||
|
||||
@@ -40,6 +42,11 @@ class WorkflowCopilotChatRequest(BaseModel):
|
||||
workflow_yaml: str = Field(..., description="Current workflow YAML including unsaved changes")
|
||||
|
||||
|
||||
class WorkflowCopilotClearProposedWorkflowRequest(BaseModel):
|
||||
workflow_copilot_chat_id: str = Field(..., description="The chat ID to update")
|
||||
auto_accept: bool = Field(..., description="Whether to auto-accept future workflow updates")
|
||||
|
||||
|
||||
class WorkflowCopilotChatHistoryMessage(BaseModel):
|
||||
sender: WorkflowCopilotChatSender = Field(..., description="Message sender")
|
||||
content: str = Field(..., description="Message content")
|
||||
@@ -49,6 +56,8 @@ class WorkflowCopilotChatHistoryMessage(BaseModel):
|
||||
class WorkflowCopilotChatHistoryResponse(BaseModel):
|
||||
workflow_copilot_chat_id: str | None = Field(None, description="Latest chat ID for the workflow")
|
||||
chat_history: list[WorkflowCopilotChatHistoryMessage] = Field(default_factory=list, description="Chat messages")
|
||||
proposed_workflow: dict | None = Field(None, description="Latest workflow proposed by the copilot")
|
||||
auto_accept: bool | None = Field(None, description="Whether copilot auto-accepts workflow updates")
|
||||
|
||||
|
||||
class WorkflowCopilotStreamMessageType(StrEnum):
|
||||
|
||||
Reference in New Issue
Block a user