T09: KI-Copilot API + Hybrid Workflow Engine + LLM client + event-triggered workflows
- KI-Copilot: NL query → proposed actions, execute with RBAC, history, audit logging - LLM client: mock mode (no API key) + OpenAI-compatible mode (AI_MODEL/AI_API_KEY) - Action mapper: NL intent → API calls (create/update/delete/search company/contact) - Workflow engine: step types (action/approval/notification/condition), JSONB steps - Workflow lifecycle: pending → in_progress → completed/rejected/cancelled - Event-triggered workflows: event bus → auto-start instances - Code-engine workflows: onboarding on user.created event - Approval timeout: auto-reject after configured hours - 5 new tenant-scoped tables with RLS: ai_conversations, ai_messages, workflows, workflow_instances, workflow_step_history - Migration 0004: all tables + RLS policies + tenant_id + indexes - 238 tests pass (30 AC + 105 coverage + 103 existing), 84.12% T09 module coverage - MissingGreenlet fix: safe accessor helpers for async ORM attribute access
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"""Workflow schemas — create, update, read, instance lifecycle, step history."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class WorkflowStep(BaseModel):
|
||||
"""A single step in a workflow definition."""
|
||||
name: str = Field(..., min_length=1, max_length=200)
|
||||
type: str = Field(..., pattern="^(action|approval|notification|condition)$")
|
||||
config: dict = Field(default_factory=dict)
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class WorkflowCreate(BaseModel):
|
||||
"""Create a new workflow definition."""
|
||||
name: str = Field(..., min_length=1, max_length=200)
|
||||
description: str | None = None
|
||||
trigger_event: str | None = None
|
||||
steps: list[WorkflowStep] = Field(..., min_length=1)
|
||||
is_active: bool = True
|
||||
|
||||
|
||||
class WorkflowUpdate(BaseModel):
|
||||
"""Update an existing workflow definition."""
|
||||
name: str | None = Field(None, max_length=200)
|
||||
description: str | None = None
|
||||
trigger_event: str | None = None
|
||||
steps: list[WorkflowStep] | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
|
||||
class WorkflowResponse(BaseModel):
|
||||
"""Workflow definition response."""
|
||||
id: str
|
||||
name: str
|
||||
description: str | None = None
|
||||
trigger_event: str | None = None
|
||||
steps: list[dict]
|
||||
is_active: bool
|
||||
created_by: str | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class WorkflowListResponse(BaseModel):
|
||||
"""Paginated workflow list."""
|
||||
items: list[WorkflowResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
class InstanceCreate(BaseModel):
|
||||
"""Create a new workflow instance."""
|
||||
context: dict = Field(default_factory=dict)
|
||||
timeout_hours: int | None = None
|
||||
|
||||
|
||||
class InstanceResponse(BaseModel):
|
||||
"""Workflow instance response with current state and history."""
|
||||
id: str
|
||||
workflow_id: str
|
||||
status: str
|
||||
current_step_index: int
|
||||
context: dict
|
||||
initiated_by: str | None = None
|
||||
completed_at: str | None = None
|
||||
timeout_hours: int | None = None
|
||||
timeout_at: str | None = None
|
||||
created_at: str | None = None
|
||||
updated_at: str | None = None
|
||||
|
||||
|
||||
class InstanceDetailResponse(InstanceResponse):
|
||||
"""Instance with step history."""
|
||||
history: list[dict] = Field(default_factory=list)
|
||||
workflow_name: str | None = None
|
||||
|
||||
|
||||
class InstanceListResponse(BaseModel):
|
||||
"""Paginated instance list."""
|
||||
items: list[InstanceResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
class AdvanceRequest(BaseModel):
|
||||
"""Advance or reject a workflow instance step."""
|
||||
decision: str = Field(..., pattern="^(approve|reject)$")
|
||||
comment: str | None = None
|
||||
|
||||
|
||||
class StepHistoryResponse(BaseModel):
|
||||
"""Step history entry."""
|
||||
id: str
|
||||
instance_id: str
|
||||
step_index: int
|
||||
step_type: str
|
||||
action: str
|
||||
actor_id: str | None = None
|
||||
details: dict | None = None
|
||||
created_at: str | None = None
|
||||
Reference in New Issue
Block a user