feat: add qa node in workflow builder (#172)

* feat: add qa node in workflow builder

* feat: add qa analysis token usage in usage_info

* fix: mask the API key in QA node

* feat: add advanced configuration in QA node
This commit is contained in:
Abhishek 2026-02-25 13:53:30 +05:30 committed by GitHub
parent f1f4830012
commit a836825b83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1619 additions and 265 deletions

View file

@ -45,8 +45,6 @@ class SuperuserWorkflowRunResponse(BaseModel):
cost_info: Optional[dict]
initial_context: Optional[dict]
gathered_context: Optional[dict]
admin_comment: Optional[str]
admin_comment_ts: Optional[datetime]
created_at: datetime
@ -151,49 +149,3 @@ async def get_workflow_runs(
limit=limit,
total_pages=total_pages,
)
# ------------------ Admin Comment ------------------
class AdminCommentRequest(BaseModel):
admin_comment: str
class AdminCommentResponse(BaseModel):
success: bool
admin_comment: str
admin_comment_ts: datetime
# ------------------ Routes ------------------
@router.post("/workflow-runs/{run_id}/comment", response_model=AdminCommentResponse)
async def set_admin_comment(
run_id: int,
request: AdminCommentRequest,
user: UserModel = Depends(get_superuser),
):
"""Add or update an *admin-only* comment for a workflow run.
The comment is stored inside the ``annotations`` JSON column under the
``admin_comment`` key so that it does not interfere with any other
annotations recorded by the system.
"""
await db_client.update_admin_comment(
run_id=run_id, admin_comment=request.admin_comment
)
# Fetch the updated run to get the timestamp from annotations
updated_run = await db_client.get_workflow_run_by_id(run_id)
admin_comment_ts = None
if updated_run and updated_run.annotations:
admin_comment_ts = updated_run.annotations.get("admin_comment_ts")
return AdminCommentResponse(
success=True,
admin_comment=request.admin_comment,
admin_comment_ts=admin_comment_ts,
)

View file

@ -15,6 +15,10 @@ from api.db.workflow_template_client import WorkflowTemplateClient
from api.enums import CallType
from api.schemas.workflow import WorkflowRunResponseSchema
from api.services.auth.depends import get_user
from api.services.configuration.masking import (
mask_workflow_definition,
merge_workflow_api_keys,
)
from api.services.mps_service_key_client import mps_service_key_client
from api.services.workflow.dto import ReactFlowDTO
from api.services.workflow.errors import ItemKind, WorkflowError
@ -273,7 +277,9 @@ async def create_workflow(
"name": workflow.name,
"status": workflow.status,
"created_at": workflow.created_at,
"workflow_definition": workflow.workflow_definition_with_fallback,
"workflow_definition": mask_workflow_definition(
workflow.workflow_definition_with_fallback
),
"current_definition_id": workflow.current_definition_id,
"template_context_variables": workflow.template_context_variables,
"call_disposition_codes": workflow.call_disposition_codes,
@ -351,7 +357,9 @@ async def create_workflow_from_template(
"name": workflow.name,
"status": workflow.status,
"created_at": workflow.created_at,
"workflow_definition": workflow.workflow_definition_with_fallback,
"workflow_definition": mask_workflow_definition(
workflow.workflow_definition_with_fallback
),
"current_definition_id": workflow.current_definition_id,
"template_context_variables": workflow.template_context_variables,
"call_disposition_codes": workflow.call_disposition_codes,
@ -462,7 +470,9 @@ async def get_workflow(
"name": workflow.name,
"status": workflow.status,
"created_at": workflow.created_at,
"workflow_definition": workflow.workflow_definition_with_fallback,
"workflow_definition": mask_workflow_definition(
workflow.workflow_definition_with_fallback
),
"current_definition_id": workflow.current_definition_id,
"template_context_variables": workflow.template_context_variables,
"call_disposition_codes": workflow.call_disposition_codes,
@ -512,7 +522,9 @@ async def update_workflow_status(
"name": workflow.name,
"status": workflow.status,
"created_at": workflow.created_at,
"workflow_definition": workflow.workflow_definition_with_fallback,
"workflow_definition": mask_workflow_definition(
workflow.workflow_definition_with_fallback
),
"current_definition_id": workflow.current_definition_id,
"template_context_variables": workflow.template_context_variables,
"call_disposition_codes": workflow.call_disposition_codes,
@ -545,18 +557,30 @@ async def update_workflow(
HTTPException: If the workflow is not found or if there's a database error
"""
try:
# Restore real API keys where the incoming definition has masked placeholders
workflow_definition = request.workflow_definition
if workflow_definition:
existing_workflow = await db_client.get_workflow(
workflow_id, organization_id=user.selected_organization_id
)
if existing_workflow:
workflow_definition = merge_workflow_api_keys(
workflow_definition,
existing_workflow.workflow_definition_with_fallback,
)
workflow = await db_client.update_workflow(
workflow_id=workflow_id,
name=request.name,
workflow_definition=request.workflow_definition,
workflow_definition=workflow_definition,
template_context_variables=request.template_context_variables,
workflow_configurations=request.workflow_configurations,
organization_id=user.selected_organization_id,
)
# Sync agent triggers if workflow definition was updated
if request.workflow_definition:
trigger_paths = extract_trigger_paths(request.workflow_definition)
if workflow_definition:
trigger_paths = extract_trigger_paths(workflow_definition)
await db_client.sync_triggers_for_workflow(
workflow_id=workflow.id,
organization_id=user.selected_organization_id,
@ -568,7 +592,9 @@ async def update_workflow(
"name": workflow.name,
"status": workflow.status,
"created_at": workflow.created_at,
"workflow_definition": workflow.workflow_definition_with_fallback,
"workflow_definition": mask_workflow_definition(
workflow.workflow_definition_with_fallback
),
"current_definition_id": workflow.current_definition_id,
"template_context_variables": workflow.template_context_variables,
"call_disposition_codes": workflow.call_disposition_codes,
@ -798,7 +824,9 @@ async def duplicate_workflow_template(
"name": workflow.name,
"status": workflow.status,
"created_at": workflow.created_at,
"workflow_definition": workflow.workflow_definition_with_fallback,
"workflow_definition": mask_workflow_definition(
workflow.workflow_definition_with_fallback
),
"current_definition_id": workflow.current_definition_id,
"template_context_variables": workflow.template_context_variables,
"call_disposition_codes": workflow.call_disposition_codes,