mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
chore: load paginated versions of workflow
This commit is contained in:
parent
d2a119c38a
commit
0282eb3225
9 changed files with 93 additions and 14 deletions
|
|
@ -300,10 +300,18 @@ class WorkflowClient(BaseDBClient):
|
|||
async def get_workflow_versions(
|
||||
self,
|
||||
workflow_id: int,
|
||||
limit: int | None = None,
|
||||
offset: int = 0,
|
||||
) -> list[WorkflowDefinitionModel]:
|
||||
"""List all versions for a workflow, newest first."""
|
||||
"""List versions for a workflow, newest first.
|
||||
|
||||
When `limit` is provided, returns at most `limit` rows starting from
|
||||
`offset` — used by the version history panel to page through long
|
||||
histories without dragging the full `workflow_json` payload for every
|
||||
version on every open.
|
||||
"""
|
||||
async with self.async_session() as session:
|
||||
result = await session.execute(
|
||||
query = (
|
||||
select(WorkflowDefinitionModel)
|
||||
.where(
|
||||
WorkflowDefinitionModel.workflow_id == workflow_id,
|
||||
|
|
@ -313,6 +321,11 @@ class WorkflowClient(BaseDBClient):
|
|||
)
|
||||
.order_by(WorkflowDefinitionModel.version_number.desc())
|
||||
)
|
||||
if offset:
|
||||
query = query.offset(offset)
|
||||
if limit is not None:
|
||||
query = query.limit(limit)
|
||||
result = await session.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
async def get_all_workflows(
|
||||
|
|
|
|||
|
|
@ -703,9 +703,15 @@ async def get_workflow(
|
|||
@router.get("/{workflow_id}/versions")
|
||||
async def get_workflow_versions(
|
||||
workflow_id: int,
|
||||
limit: int | None = Query(None, ge=1, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
user: UserModel = Depends(get_user),
|
||||
) -> list[WorkflowVersionResponse]:
|
||||
"""List all versions for a workflow, newest first."""
|
||||
"""List versions for a workflow, newest first.
|
||||
|
||||
Pass `limit`/`offset` to page through long histories. With no `limit`,
|
||||
returns every version (legacy behavior).
|
||||
"""
|
||||
workflow = await db_client.get_workflow(
|
||||
workflow_id, organization_id=user.selected_organization_id
|
||||
)
|
||||
|
|
@ -714,7 +720,9 @@ async def get_workflow_versions(
|
|||
status_code=404, detail=f"Workflow with id {workflow_id} not found"
|
||||
)
|
||||
|
||||
versions = await db_client.get_workflow_versions(workflow_id)
|
||||
versions = await db_client.get_workflow_versions(
|
||||
workflow_id, limit=limit, offset=offset
|
||||
)
|
||||
return [
|
||||
WorkflowVersionResponse(
|
||||
id=v.id,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class BusyWaitProcessor(FrameProcessor):
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_interruption_with_blocked_end_frame():
|
||||
busy_wait_processor = BusyWaitProcessor(wait_time=0.5)
|
||||
busy_wait_processor = BusyWaitProcessor(wait_time=5.0)
|
||||
transport = MockTransport()
|
||||
pipeline = Pipeline([transport, busy_wait_processor])
|
||||
|
||||
|
|
@ -84,8 +84,6 @@ async def test_interruption_with_blocked_end_frame():
|
|||
)
|
||||
|
||||
# If there are pending tasks, we timed out
|
||||
# FIXME: Currently I have creaetd an issue on pipecat which talks about
|
||||
# how this behaviour is not good. https://github.com/pipecat-ai/pipecat/issues/4412
|
||||
if pending:
|
||||
# Cancel all pending tasks
|
||||
for t in pending:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
2
pipecat
2
pipecat
|
|
@ -1 +1 @@
|
|||
Subproject commit e8f59a6a58977f4f5598e8de7bfbbf9b6d474eb8
|
||||
Subproject commit 97b3b041bda0099dbe48b6f20daf49ce113711f3
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# generated by datamodel-codegen:
|
||||
# filename: dograh-openapi-XXXXXX.json.r8rR0xozEB
|
||||
# timestamp: 2026-05-06T12:17:39+00:00
|
||||
# filename: dograh-openapi-XXXXXX.json.x7226hNjaY
|
||||
# timestamp: 2026-05-07T08:13:29+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ const edgeTypes = {
|
|||
custom: CustomEdge,
|
||||
};
|
||||
|
||||
const VERSIONS_PAGE_SIZE = 10;
|
||||
|
||||
interface RenderWorkflowProps {
|
||||
initialWorkflowName: string;
|
||||
workflowId: number;
|
||||
|
|
@ -65,6 +67,8 @@ function RenderWorkflow({ initialWorkflowName, workflowId, workflowUuid, initial
|
|||
const [isVersionPanelOpen, setIsVersionPanelOpen] = useState(false);
|
||||
const [versions, setVersions] = useState<WorkflowVersion[]>([]);
|
||||
const [versionsLoading, setVersionsLoading] = useState(false);
|
||||
const [versionsLoadingMore, setVersionsLoadingMore] = useState(false);
|
||||
const [versionsHasMore, setVersionsHasMore] = useState(false);
|
||||
const [activeVersionId, setActiveVersionId] = useState<number | null>(null);
|
||||
// Version info that updates immediately from the GET/save/publish responses.
|
||||
const [currentVersionNumber, setCurrentVersionNumber] = useState<number | null>(initialVersionNumber ?? null);
|
||||
|
|
@ -104,18 +108,24 @@ function RenderWorkflow({ initialWorkflowName, workflowId, workflowUuid, initial
|
|||
// Derive hasDraft from the current version status
|
||||
const hasDraft = currentVersionStatus === "draft";
|
||||
|
||||
// Fetch workflow versions, optionally forcing a refresh
|
||||
// Fetch the first page of workflow versions, optionally forcing a refresh.
|
||||
// Pagination keeps the panel snappy when a workflow has accumulated a long
|
||||
// history — `workflow_json` is shipped per row, so loading hundreds at once
|
||||
// is expensive on the wire.
|
||||
const fetchVersions = useCallback(async (force = false) => {
|
||||
if (versionsFetched.current && !force) return;
|
||||
setVersionsLoading(true);
|
||||
try {
|
||||
const response = await getWorkflowVersionsApiV1WorkflowWorkflowIdVersionsGet({
|
||||
path: { workflow_id: workflowId },
|
||||
query: { limit: VERSIONS_PAGE_SIZE, offset: 0 },
|
||||
});
|
||||
const data = response.data as WorkflowVersion[] | undefined;
|
||||
if (data) {
|
||||
setVersions(data);
|
||||
// Set active version to draft if exists, else published
|
||||
setVersionsHasMore(data.length === VERSIONS_PAGE_SIZE);
|
||||
// Set active version to draft if exists, else published.
|
||||
// Both live on the newest page so the first fetch always sees them.
|
||||
const current = data.find((v) => v.status === "draft") ?? data.find((v) => v.status === "published");
|
||||
if (current) {
|
||||
setActiveVersionId(current.id);
|
||||
|
|
@ -129,6 +139,24 @@ function RenderWorkflow({ initialWorkflowName, workflowId, workflowUuid, initial
|
|||
}
|
||||
}, [workflowId]);
|
||||
|
||||
const handleLoadMoreVersions = useCallback(async () => {
|
||||
if (versionsLoadingMore || !versionsHasMore) return;
|
||||
setVersionsLoadingMore(true);
|
||||
try {
|
||||
const response = await getWorkflowVersionsApiV1WorkflowWorkflowIdVersionsGet({
|
||||
path: { workflow_id: workflowId },
|
||||
query: { limit: VERSIONS_PAGE_SIZE, offset: versions.length },
|
||||
});
|
||||
const data = response.data as WorkflowVersion[] | undefined;
|
||||
if (data) {
|
||||
setVersions((prev) => [...prev, ...data]);
|
||||
setVersionsHasMore(data.length === VERSIONS_PAGE_SIZE);
|
||||
}
|
||||
} finally {
|
||||
setVersionsLoadingMore(false);
|
||||
}
|
||||
}, [workflowId, versions.length, versionsLoadingMore, versionsHasMore]);
|
||||
|
||||
const handleOpenVersionPanel = useCallback(() => {
|
||||
setIsVersionPanelOpen(true);
|
||||
fetchVersions();
|
||||
|
|
@ -484,6 +512,9 @@ function RenderWorkflow({ initialWorkflowName, workflowId, workflowUuid, initial
|
|||
loading={versionsLoading}
|
||||
activeVersionId={activeVersionId}
|
||||
onSelectVersion={handleSelectVersion}
|
||||
hasMore={versionsHasMore}
|
||||
loadingMore={versionsLoadingMore}
|
||||
onLoadMore={handleLoadMoreVersions}
|
||||
/>
|
||||
|
||||
<PhoneCallDialog
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ interface VersionHistoryPanelProps {
|
|||
loading: boolean;
|
||||
activeVersionId: number | null;
|
||||
onSelectVersion: (version: WorkflowVersion) => void;
|
||||
hasMore: boolean;
|
||||
loadingMore: boolean;
|
||||
onLoadMore: () => void;
|
||||
}
|
||||
|
||||
const statusLabel: Record<string, string> = {
|
||||
|
|
@ -45,6 +48,9 @@ export const VersionHistoryPanel = ({
|
|||
loading,
|
||||
activeVersionId,
|
||||
onSelectVersion,
|
||||
hasMore,
|
||||
loadingMore,
|
||||
onLoadMore,
|
||||
}: VersionHistoryPanelProps) => {
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
|
|
@ -125,6 +131,20 @@ export const VersionHistoryPanel = ({
|
|||
</button>
|
||||
);
|
||||
})}
|
||||
{hasMore && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={onLoadMore}
|
||||
disabled={loadingMore}
|
||||
className="w-full text-sm text-gray-300 hover:text-white hover:bg-[#2a2a2a]"
|
||||
>
|
||||
{loadingMore ? (
|
||||
<LoaderCircle className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
"Load more"
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5694,7 +5694,16 @@ export type GetWorkflowVersionsApiV1WorkflowWorkflowIdVersionsGetData = {
|
|||
*/
|
||||
workflow_id: number;
|
||||
};
|
||||
query?: never;
|
||||
query?: {
|
||||
/**
|
||||
* Limit
|
||||
*/
|
||||
limit?: number | null;
|
||||
/**
|
||||
* Offset
|
||||
*/
|
||||
offset?: number;
|
||||
};
|
||||
url: '/api/v1/workflow/{workflow_id}/versions';
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue