* feat: filter operations by type + fix stale closure in auto-refresh - Add `type` query param to GET /operations endpoint and engine layer - Add operation type dropdown filter in Background Operations UI - Fix auto-refresh interval using stale statusFilter/offset closure by adding filter state to useEffect deps and wrapping loadOperations in useCallback (fixes #522) - Regenerate OpenAPI spec and all SDK clients * fix: update Rust CLI list_operations call with new type parameter
This commit is contained in:
parent
7accac94b2
commit
f7a60f898d
13 changed files with 188 additions and 60 deletions
|
|
@ -3427,13 +3427,17 @@ def _register_routes(app: FastAPI):
|
||||||
"/v1/default/banks/{bank_id}/operations",
|
"/v1/default/banks/{bank_id}/operations",
|
||||||
response_model=OperationsListResponse,
|
response_model=OperationsListResponse,
|
||||||
summary="List async operations",
|
summary="List async operations",
|
||||||
description="Get a list of async operations for a specific agent, with optional filtering by status. Results are sorted by most recent first.",
|
description="Get a list of async operations for a specific agent, with optional filtering by status and operation type. Results are sorted by most recent first.",
|
||||||
operation_id="list_operations",
|
operation_id="list_operations",
|
||||||
tags=["Operations"],
|
tags=["Operations"],
|
||||||
)
|
)
|
||||||
async def api_list_operations(
|
async def api_list_operations(
|
||||||
bank_id: str,
|
bank_id: str,
|
||||||
status: str | None = Query(default=None, description="Filter by status: pending, completed, or failed"),
|
status: str | None = Query(default=None, description="Filter by status: pending, completed, or failed"),
|
||||||
|
type: str | None = Query(
|
||||||
|
default=None,
|
||||||
|
description="Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery",
|
||||||
|
),
|
||||||
limit: int = Query(default=20, ge=1, le=100, description="Maximum number of operations to return"),
|
limit: int = Query(default=20, ge=1, le=100, description="Maximum number of operations to return"),
|
||||||
offset: int = Query(default=0, ge=0, description="Number of operations to skip"),
|
offset: int = Query(default=0, ge=0, description="Number of operations to skip"),
|
||||||
request_context: RequestContext = Depends(get_request_context),
|
request_context: RequestContext = Depends(get_request_context),
|
||||||
|
|
@ -3441,7 +3445,7 @@ def _register_routes(app: FastAPI):
|
||||||
"""List async operations for a memory bank with optional filtering and pagination."""
|
"""List async operations for a memory bank with optional filtering and pagination."""
|
||||||
try:
|
try:
|
||||||
result = await app.state.memory.list_operations(
|
result = await app.state.memory.list_operations(
|
||||||
bank_id, status=status, limit=limit, offset=offset, request_context=request_context
|
bank_id, status=status, task_type=type, limit=limit, offset=offset, request_context=request_context
|
||||||
)
|
)
|
||||||
return OperationsListResponse(
|
return OperationsListResponse(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
|
|
|
||||||
|
|
@ -6840,6 +6840,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
bank_id: str,
|
bank_id: str,
|
||||||
*,
|
*,
|
||||||
status: str | None = None,
|
status: str | None = None,
|
||||||
|
task_type: str | None = None,
|
||||||
limit: int = 20,
|
limit: int = 20,
|
||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
request_context: "RequestContext",
|
request_context: "RequestContext",
|
||||||
|
|
@ -6849,6 +6850,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
Args:
|
Args:
|
||||||
bank_id: Bank identifier
|
bank_id: Bank identifier
|
||||||
status: Optional status filter (pending, completed, failed)
|
status: Optional status filter (pending, completed, failed)
|
||||||
|
task_type: Optional operation type filter (retain, consolidation, etc.)
|
||||||
limit: Maximum number of operations to return (default 20)
|
limit: Maximum number of operations to return (default 20)
|
||||||
offset: Number of operations to skip (default 0)
|
offset: Number of operations to skip (default 0)
|
||||||
request_context: Request context for authentication
|
request_context: Request context for authentication
|
||||||
|
|
@ -6877,6 +6879,10 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
where_conditions.append(f"status = ${len(params) + 1}")
|
where_conditions.append(f"status = ${len(params) + 1}")
|
||||||
params.append(status)
|
params.append(status)
|
||||||
|
|
||||||
|
if task_type:
|
||||||
|
where_conditions.append(f"operation_type = ${len(params) + 1}")
|
||||||
|
params.append(task_type)
|
||||||
|
|
||||||
where_clause = " AND ".join(where_conditions)
|
where_clause = " AND ".join(where_conditions)
|
||||||
|
|
||||||
# Get total count (with filter)
|
# Get total count (with filter)
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ impl ApiClient {
|
||||||
pub fn poll_operation(&self, agent_id: &str, operation_id: &str, verbose: bool) -> Result<(bool, Option<String>)> {
|
pub fn poll_operation(&self, agent_id: &str, operation_id: &str, verbose: bool) -> Result<(bool, Option<String>)> {
|
||||||
self.runtime.block_on(async {
|
self.runtime.block_on(async {
|
||||||
loop {
|
loop {
|
||||||
let response = self.client.list_operations(agent_id, None, None, None, None).await?;
|
let response = self.client.list_operations(agent_id, None, None, None, None, None).await?;
|
||||||
let ops = response.into_inner();
|
let ops = response.into_inner();
|
||||||
|
|
||||||
// Find our operation
|
// Find our operation
|
||||||
|
|
@ -329,7 +329,7 @@ impl ApiClient {
|
||||||
|
|
||||||
pub fn list_operations(&self, agent_id: &str, _verbose: bool) -> Result<OperationsResponse> {
|
pub fn list_operations(&self, agent_id: &str, _verbose: bool) -> Result<OperationsResponse> {
|
||||||
self.runtime.block_on(async {
|
self.runtime.block_on(async {
|
||||||
let response = self.client.list_operations(agent_id, None, None, None, None).await?;
|
let response = self.client.list_operations(agent_id, None, None, None, None, None).await?;
|
||||||
let value = response.into_inner();
|
let value = response.into_inner();
|
||||||
// Convert to JSON Value first, then parse into our type
|
// Convert to JSON Value first, then parse into our type
|
||||||
let json_value = serde_json::to_value(&value)?;
|
let json_value = serde_json::to_value(&value)?;
|
||||||
|
|
|
||||||
|
|
@ -1600,7 +1600,8 @@ paths:
|
||||||
/v1/default/banks/{bank_id}/operations:
|
/v1/default/banks/{bank_id}/operations:
|
||||||
get:
|
get:
|
||||||
description: "Get a list of async operations for a specific agent, with optional\
|
description: "Get a list of async operations for a specific agent, with optional\
|
||||||
\ filtering by status. Results are sorted by most recent first."
|
\ filtering by status and operation type. Results are sorted by most recent\
|
||||||
|
\ first."
|
||||||
operationId: list_operations
|
operationId: list_operations
|
||||||
parameters:
|
parameters:
|
||||||
- explode: false
|
- explode: false
|
||||||
|
|
@ -1620,6 +1621,16 @@ paths:
|
||||||
nullable: true
|
nullable: true
|
||||||
type: string
|
type: string
|
||||||
style: form
|
style: form
|
||||||
|
- description: "Filter by operation type: retain, consolidation, refresh_mental_model,\
|
||||||
|
\ file_convert_retain, webhook_delivery"
|
||||||
|
explode: true
|
||||||
|
in: query
|
||||||
|
name: type
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
nullable: true
|
||||||
|
type: string
|
||||||
|
style: form
|
||||||
- description: Maximum number of operations to return
|
- description: Maximum number of operations to return
|
||||||
explode: true
|
explode: true
|
||||||
in: query
|
in: query
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,7 @@ type ApiListOperationsRequest struct {
|
||||||
ApiService *OperationsAPIService
|
ApiService *OperationsAPIService
|
||||||
bankId string
|
bankId string
|
||||||
status *string
|
status *string
|
||||||
|
type_ *string
|
||||||
limit *int32
|
limit *int32
|
||||||
offset *int32
|
offset *int32
|
||||||
authorization *string
|
authorization *string
|
||||||
|
|
@ -291,6 +292,12 @@ func (r ApiListOperationsRequest) Status(status string) ApiListOperationsRequest
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery
|
||||||
|
func (r ApiListOperationsRequest) Type_(type_ string) ApiListOperationsRequest {
|
||||||
|
r.type_ = &type_
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
// Maximum number of operations to return
|
// Maximum number of operations to return
|
||||||
func (r ApiListOperationsRequest) Limit(limit int32) ApiListOperationsRequest {
|
func (r ApiListOperationsRequest) Limit(limit int32) ApiListOperationsRequest {
|
||||||
r.limit = &limit
|
r.limit = &limit
|
||||||
|
|
@ -315,7 +322,7 @@ func (r ApiListOperationsRequest) Execute() (*OperationsListResponse, *http.Resp
|
||||||
/*
|
/*
|
||||||
ListOperations List async operations
|
ListOperations List async operations
|
||||||
|
|
||||||
Get a list of async operations for a specific agent, with optional filtering by status. Results are sorted by most recent first.
|
Get a list of async operations for a specific agent, with optional filtering by status and operation type. Results are sorted by most recent first.
|
||||||
|
|
||||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||||
@param bankId
|
@param bankId
|
||||||
|
|
@ -354,6 +361,9 @@ func (a *OperationsAPIService) ListOperationsExecute(r ApiListOperationsRequest)
|
||||||
if r.status != nil {
|
if r.status != nil {
|
||||||
parameterAddToHeaderOrQuery(localVarQueryParams, "status", r.status, "form", "")
|
parameterAddToHeaderOrQuery(localVarQueryParams, "status", r.status, "form", "")
|
||||||
}
|
}
|
||||||
|
if r.type_ != nil {
|
||||||
|
parameterAddToHeaderOrQuery(localVarQueryParams, "type", r.type_, "form", "")
|
||||||
|
}
|
||||||
if r.limit != nil {
|
if r.limit != nil {
|
||||||
parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "")
|
parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -632,6 +632,7 @@ class OperationsApi:
|
||||||
self,
|
self,
|
||||||
bank_id: StrictStr,
|
bank_id: StrictStr,
|
||||||
status: Annotated[Optional[StrictStr], Field(description="Filter by status: pending, completed, or failed")] = None,
|
status: Annotated[Optional[StrictStr], Field(description="Filter by status: pending, completed, or failed")] = None,
|
||||||
|
type: Annotated[Optional[StrictStr], Field(description="Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery")] = None,
|
||||||
limit: Annotated[Optional[Annotated[int, Field(le=100, strict=True, ge=1)]], Field(description="Maximum number of operations to return")] = None,
|
limit: Annotated[Optional[Annotated[int, Field(le=100, strict=True, ge=1)]], Field(description="Maximum number of operations to return")] = None,
|
||||||
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
||||||
authorization: Optional[StrictStr] = None,
|
authorization: Optional[StrictStr] = None,
|
||||||
|
|
@ -650,12 +651,14 @@ class OperationsApi:
|
||||||
) -> OperationsListResponse:
|
) -> OperationsListResponse:
|
||||||
"""List async operations
|
"""List async operations
|
||||||
|
|
||||||
Get a list of async operations for a specific agent, with optional filtering by status. Results are sorted by most recent first.
|
Get a list of async operations for a specific agent, with optional filtering by status and operation type. Results are sorted by most recent first.
|
||||||
|
|
||||||
:param bank_id: (required)
|
:param bank_id: (required)
|
||||||
:type bank_id: str
|
:type bank_id: str
|
||||||
:param status: Filter by status: pending, completed, or failed
|
:param status: Filter by status: pending, completed, or failed
|
||||||
:type status: str
|
:type status: str
|
||||||
|
:param type: Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery
|
||||||
|
:type type: str
|
||||||
:param limit: Maximum number of operations to return
|
:param limit: Maximum number of operations to return
|
||||||
:type limit: int
|
:type limit: int
|
||||||
:param offset: Number of operations to skip
|
:param offset: Number of operations to skip
|
||||||
|
|
@ -687,6 +690,7 @@ class OperationsApi:
|
||||||
_param = self._list_operations_serialize(
|
_param = self._list_operations_serialize(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
status=status,
|
status=status,
|
||||||
|
type=type,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
authorization=authorization,
|
authorization=authorization,
|
||||||
|
|
@ -716,6 +720,7 @@ class OperationsApi:
|
||||||
self,
|
self,
|
||||||
bank_id: StrictStr,
|
bank_id: StrictStr,
|
||||||
status: Annotated[Optional[StrictStr], Field(description="Filter by status: pending, completed, or failed")] = None,
|
status: Annotated[Optional[StrictStr], Field(description="Filter by status: pending, completed, or failed")] = None,
|
||||||
|
type: Annotated[Optional[StrictStr], Field(description="Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery")] = None,
|
||||||
limit: Annotated[Optional[Annotated[int, Field(le=100, strict=True, ge=1)]], Field(description="Maximum number of operations to return")] = None,
|
limit: Annotated[Optional[Annotated[int, Field(le=100, strict=True, ge=1)]], Field(description="Maximum number of operations to return")] = None,
|
||||||
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
||||||
authorization: Optional[StrictStr] = None,
|
authorization: Optional[StrictStr] = None,
|
||||||
|
|
@ -734,12 +739,14 @@ class OperationsApi:
|
||||||
) -> ApiResponse[OperationsListResponse]:
|
) -> ApiResponse[OperationsListResponse]:
|
||||||
"""List async operations
|
"""List async operations
|
||||||
|
|
||||||
Get a list of async operations for a specific agent, with optional filtering by status. Results are sorted by most recent first.
|
Get a list of async operations for a specific agent, with optional filtering by status and operation type. Results are sorted by most recent first.
|
||||||
|
|
||||||
:param bank_id: (required)
|
:param bank_id: (required)
|
||||||
:type bank_id: str
|
:type bank_id: str
|
||||||
:param status: Filter by status: pending, completed, or failed
|
:param status: Filter by status: pending, completed, or failed
|
||||||
:type status: str
|
:type status: str
|
||||||
|
:param type: Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery
|
||||||
|
:type type: str
|
||||||
:param limit: Maximum number of operations to return
|
:param limit: Maximum number of operations to return
|
||||||
:type limit: int
|
:type limit: int
|
||||||
:param offset: Number of operations to skip
|
:param offset: Number of operations to skip
|
||||||
|
|
@ -771,6 +778,7 @@ class OperationsApi:
|
||||||
_param = self._list_operations_serialize(
|
_param = self._list_operations_serialize(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
status=status,
|
status=status,
|
||||||
|
type=type,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
authorization=authorization,
|
authorization=authorization,
|
||||||
|
|
@ -800,6 +808,7 @@ class OperationsApi:
|
||||||
self,
|
self,
|
||||||
bank_id: StrictStr,
|
bank_id: StrictStr,
|
||||||
status: Annotated[Optional[StrictStr], Field(description="Filter by status: pending, completed, or failed")] = None,
|
status: Annotated[Optional[StrictStr], Field(description="Filter by status: pending, completed, or failed")] = None,
|
||||||
|
type: Annotated[Optional[StrictStr], Field(description="Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery")] = None,
|
||||||
limit: Annotated[Optional[Annotated[int, Field(le=100, strict=True, ge=1)]], Field(description="Maximum number of operations to return")] = None,
|
limit: Annotated[Optional[Annotated[int, Field(le=100, strict=True, ge=1)]], Field(description="Maximum number of operations to return")] = None,
|
||||||
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
||||||
authorization: Optional[StrictStr] = None,
|
authorization: Optional[StrictStr] = None,
|
||||||
|
|
@ -818,12 +827,14 @@ class OperationsApi:
|
||||||
) -> RESTResponseType:
|
) -> RESTResponseType:
|
||||||
"""List async operations
|
"""List async operations
|
||||||
|
|
||||||
Get a list of async operations for a specific agent, with optional filtering by status. Results are sorted by most recent first.
|
Get a list of async operations for a specific agent, with optional filtering by status and operation type. Results are sorted by most recent first.
|
||||||
|
|
||||||
:param bank_id: (required)
|
:param bank_id: (required)
|
||||||
:type bank_id: str
|
:type bank_id: str
|
||||||
:param status: Filter by status: pending, completed, or failed
|
:param status: Filter by status: pending, completed, or failed
|
||||||
:type status: str
|
:type status: str
|
||||||
|
:param type: Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery
|
||||||
|
:type type: str
|
||||||
:param limit: Maximum number of operations to return
|
:param limit: Maximum number of operations to return
|
||||||
:type limit: int
|
:type limit: int
|
||||||
:param offset: Number of operations to skip
|
:param offset: Number of operations to skip
|
||||||
|
|
@ -855,6 +866,7 @@ class OperationsApi:
|
||||||
_param = self._list_operations_serialize(
|
_param = self._list_operations_serialize(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
status=status,
|
status=status,
|
||||||
|
type=type,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
authorization=authorization,
|
authorization=authorization,
|
||||||
|
|
@ -879,6 +891,7 @@ class OperationsApi:
|
||||||
self,
|
self,
|
||||||
bank_id,
|
bank_id,
|
||||||
status,
|
status,
|
||||||
|
type,
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
authorization,
|
authorization,
|
||||||
|
|
@ -910,6 +923,10 @@ class OperationsApi:
|
||||||
|
|
||||||
_query_params.append(('status', status))
|
_query_params.append(('status', status))
|
||||||
|
|
||||||
|
if type is not None:
|
||||||
|
|
||||||
|
_query_params.append(('type', type))
|
||||||
|
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
|
|
||||||
_query_params.append(('limit', limit))
|
_query_params.append(('limit', limit))
|
||||||
|
|
|
||||||
|
|
@ -738,7 +738,7 @@ export const getChunk = <ThrowOnError extends boolean = false>(
|
||||||
/**
|
/**
|
||||||
* List async operations
|
* List async operations
|
||||||
*
|
*
|
||||||
* Get a list of async operations for a specific agent, with optional filtering by status. Results are sorted by most recent first.
|
* Get a list of async operations for a specific agent, with optional filtering by status and operation type. Results are sorted by most recent first.
|
||||||
*/
|
*/
|
||||||
export const listOperations = <ThrowOnError extends boolean = false>(
|
export const listOperations = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<ListOperationsData, ThrowOnError>,
|
options: Options<ListOperationsData, ThrowOnError>,
|
||||||
|
|
|
||||||
|
|
@ -3715,6 +3715,12 @@ export type ListOperationsData = {
|
||||||
* Filter by status: pending, completed, or failed
|
* Filter by status: pending, completed, or failed
|
||||||
*/
|
*/
|
||||||
status?: string | null;
|
status?: string | null;
|
||||||
|
/**
|
||||||
|
* Type
|
||||||
|
*
|
||||||
|
* Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery
|
||||||
|
*/
|
||||||
|
type?: string | null;
|
||||||
/**
|
/**
|
||||||
* Limit
|
* Limit
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ export async function GET(
|
||||||
const { agentId } = await params;
|
const { agentId } = await params;
|
||||||
const searchParams = request.nextUrl.searchParams;
|
const searchParams = request.nextUrl.searchParams;
|
||||||
const status = searchParams.get("status") || undefined;
|
const status = searchParams.get("status") || undefined;
|
||||||
|
const type = searchParams.get("type") || undefined;
|
||||||
const limit = searchParams.get("limit") ? parseInt(searchParams.get("limit")!) : undefined;
|
const limit = searchParams.get("limit") ? parseInt(searchParams.get("limit")!) : undefined;
|
||||||
const offset = searchParams.get("offset") ? parseInt(searchParams.get("offset")!) : undefined;
|
const offset = searchParams.get("offset") ? parseInt(searchParams.get("offset")!) : undefined;
|
||||||
|
|
||||||
const response = await sdk.listOperations({
|
const response = await sdk.listOperations({
|
||||||
client: lowLevelClient,
|
client: lowLevelClient,
|
||||||
path: { bank_id: agentId },
|
path: { bank_id: agentId },
|
||||||
query: { status, limit, offset },
|
query: { status, type, limit, offset },
|
||||||
});
|
});
|
||||||
return NextResponse.json(response.data || {}, { status: 200 });
|
return NextResponse.json(response.data || {}, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { useBank } from "@/lib/bank-context";
|
import { useBank } from "@/lib/bank-context";
|
||||||
import { client } from "@/lib/api";
|
import { client } from "@/lib/api";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
@ -19,6 +19,13 @@ import {
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { RefreshCw, Clock, AlertCircle, CheckCircle, Loader2, X } from "lucide-react";
|
import { RefreshCw, Clock, AlertCircle, CheckCircle, Loader2, X } from "lucide-react";
|
||||||
|
|
||||||
interface Operation {
|
interface Operation {
|
||||||
|
|
@ -71,11 +78,21 @@ type OperationDetails =
|
||||||
child_operations?: never;
|
child_operations?: never;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const OPERATION_TYPE_OPTIONS = [
|
||||||
|
{ value: "all", label: "All types" },
|
||||||
|
{ value: "retain", label: "Retain" },
|
||||||
|
{ value: "consolidation", label: "Consolidation" },
|
||||||
|
{ value: "refresh_mental_model", label: "Mental Model Refresh" },
|
||||||
|
{ value: "file_convert_retain", label: "File Convert & Retain" },
|
||||||
|
{ value: "webhook_delivery", label: "Webhook Delivery" },
|
||||||
|
];
|
||||||
|
|
||||||
export function BankOperationsView() {
|
export function BankOperationsView() {
|
||||||
const { currentBank } = useBank();
|
const { currentBank } = useBank();
|
||||||
const [operations, setOperations] = useState<Operation[]>([]);
|
const [operations, setOperations] = useState<Operation[]>([]);
|
||||||
const [totalOperations, setTotalOperations] = useState(0);
|
const [totalOperations, setTotalOperations] = useState(0);
|
||||||
const [statusFilter, setStatusFilter] = useState<string | null>(null);
|
const [statusFilter, setStatusFilter] = useState<string | null>(null);
|
||||||
|
const [taskTypeFilter, setTaskTypeFilter] = useState<string | null>(null);
|
||||||
const [limit] = useState(10);
|
const [limit] = useState(10);
|
||||||
const [offset, setOffset] = useState(0);
|
const [offset, setOffset] = useState(0);
|
||||||
const [cancellingOpId, setCancellingOpId] = useState<string | null>(null);
|
const [cancellingOpId, setCancellingOpId] = useState<string | null>(null);
|
||||||
|
|
@ -84,9 +101,11 @@ export function BankOperationsView() {
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const [loadingDetails, setLoadingDetails] = useState(false);
|
const [loadingDetails, setLoadingDetails] = useState(false);
|
||||||
|
|
||||||
const loadOperations = async (
|
const loadOperations = useCallback(
|
||||||
|
async (
|
||||||
newStatusFilter: string | null = statusFilter,
|
newStatusFilter: string | null = statusFilter,
|
||||||
newOffset: number = offset
|
newOffset: number = offset,
|
||||||
|
newTaskTypeFilter: string | null = taskTypeFilter
|
||||||
) => {
|
) => {
|
||||||
if (!currentBank) return;
|
if (!currentBank) return;
|
||||||
|
|
||||||
|
|
@ -94,6 +113,7 @@ export function BankOperationsView() {
|
||||||
try {
|
try {
|
||||||
const opsData = await client.listOperations(currentBank, {
|
const opsData = await client.listOperations(currentBank, {
|
||||||
status: newStatusFilter || undefined,
|
status: newStatusFilter || undefined,
|
||||||
|
type: newTaskTypeFilter || undefined,
|
||||||
limit,
|
limit,
|
||||||
offset: newOffset,
|
offset: newOffset,
|
||||||
});
|
});
|
||||||
|
|
@ -104,17 +124,25 @@ export function BankOperationsView() {
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
[currentBank, statusFilter, offset, taskTypeFilter, limit]
|
||||||
|
);
|
||||||
|
|
||||||
const handleFilterChange = (newFilter: string | null) => {
|
const handleFilterChange = (newFilter: string | null) => {
|
||||||
setStatusFilter(newFilter);
|
setStatusFilter(newFilter);
|
||||||
setOffset(0);
|
setOffset(0);
|
||||||
loadOperations(newFilter, 0);
|
loadOperations(newFilter, 0, taskTypeFilter);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTaskTypeFilterChange = (newTaskType: string | null) => {
|
||||||
|
setTaskTypeFilter(newTaskType);
|
||||||
|
setOffset(0);
|
||||||
|
loadOperations(statusFilter, 0, newTaskType);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageChange = (newOffset: number) => {
|
const handlePageChange = (newOffset: number) => {
|
||||||
setOffset(newOffset);
|
setOffset(newOffset);
|
||||||
loadOperations(statusFilter, newOffset);
|
loadOperations(statusFilter, newOffset, taskTypeFilter);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancelOperation = async (operationId: string) => {
|
const handleCancelOperation = async (operationId: string) => {
|
||||||
|
|
@ -149,12 +177,14 @@ export function BankOperationsView() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentBank) {
|
if (currentBank) {
|
||||||
loadOperations();
|
loadOperations(statusFilter, offset, taskTypeFilter);
|
||||||
// Refresh operations every 5 seconds
|
const interval = setInterval(
|
||||||
const interval = setInterval(() => loadOperations(), 5000);
|
() => loadOperations(statusFilter, offset, taskTypeFilter),
|
||||||
|
5000
|
||||||
|
);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}
|
}
|
||||||
}, [currentBank]);
|
}, [currentBank, statusFilter, offset, taskTypeFilter]);
|
||||||
|
|
||||||
if (!currentBank) return null;
|
if (!currentBank) return null;
|
||||||
|
|
||||||
|
|
@ -180,6 +210,27 @@ export function BankOperationsView() {
|
||||||
{statusFilter ? ` (${statusFilter})` : ""}
|
{statusFilter ? ` (${statusFilter})` : ""}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Select
|
||||||
|
value={taskTypeFilter ?? "all"}
|
||||||
|
onValueChange={(val) => handleTaskTypeFilterChange(val === "all" ? null : val)}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-9 w-[180px] text-sm">
|
||||||
|
<SelectValue placeholder="All types" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{OPERATION_TYPE_OPTIONS.map((opt) => (
|
||||||
|
<SelectItem key={opt.value} value={opt.value}>
|
||||||
|
<div>
|
||||||
|
<div>{opt.label}</div>
|
||||||
|
{opt.value !== "all" && (
|
||||||
|
<div className="text-xs text-muted-foreground font-mono">{opt.value}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<div className="flex gap-1 bg-muted p-1 rounded-lg">
|
<div className="flex gap-1 bg-muted p-1 rounded-lg">
|
||||||
{[
|
{[
|
||||||
{ value: null, label: "All" },
|
{ value: null, label: "All" },
|
||||||
|
|
@ -201,6 +252,7 @@ export function BankOperationsView() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{operations.length > 0 ? (
|
{operations.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -248,10 +248,11 @@ export class ControlPlaneClient {
|
||||||
*/
|
*/
|
||||||
async listOperations(
|
async listOperations(
|
||||||
bankId: string,
|
bankId: string,
|
||||||
options?: { status?: string; limit?: number; offset?: number }
|
options?: { status?: string; type?: string; limit?: number; offset?: number }
|
||||||
) {
|
) {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (options?.status) params.append("status", options.status);
|
if (options?.status) params.append("status", options.status);
|
||||||
|
if (options?.type) params.append("type", options.type);
|
||||||
if (options?.limit) params.append("limit", options.limit.toString());
|
if (options?.limit) params.append("limit", options.limit.toString());
|
||||||
if (options?.offset) params.append("offset", options.offset.toString());
|
if (options?.offset) params.append("offset", options.offset.toString());
|
||||||
const query = params.toString();
|
const query = params.toString();
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,9 @@
|
||||||
".next-50432/types/**/*.ts",
|
".next-50432/types/**/*.ts",
|
||||||
".next-50432/dev/types/**/*.ts",
|
".next-50432/dev/types/**/*.ts",
|
||||||
".next-54840/types/**/*.ts",
|
".next-54840/types/**/*.ts",
|
||||||
".next-54840/dev/types/**/*.ts"
|
".next-54840/dev/types/**/*.ts",
|
||||||
|
".next-64856/types/**/*.ts",
|
||||||
|
".next-64856/dev/types/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
|
|
||||||
|
|
@ -2357,7 +2357,7 @@
|
||||||
"Operations"
|
"Operations"
|
||||||
],
|
],
|
||||||
"summary": "List async operations",
|
"summary": "List async operations",
|
||||||
"description": "Get a list of async operations for a specific agent, with optional filtering by status. Results are sorted by most recent first.",
|
"description": "Get a list of async operations for a specific agent, with optional filtering by status and operation type. Results are sorted by most recent first.",
|
||||||
"operationId": "list_operations",
|
"operationId": "list_operations",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
|
|
@ -2387,6 +2387,24 @@
|
||||||
},
|
},
|
||||||
"description": "Filter by status: pending, completed, or failed"
|
"description": "Filter by status: pending, completed, or failed"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery",
|
||||||
|
"title": "Type"
|
||||||
|
},
|
||||||
|
"description": "Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "limit",
|
"name": "limit",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue