* 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",
|
||||
response_model=OperationsListResponse,
|
||||
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",
|
||||
tags=["Operations"],
|
||||
)
|
||||
async def api_list_operations(
|
||||
bank_id: str,
|
||||
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"),
|
||||
offset: int = Query(default=0, ge=0, description="Number of operations to skip"),
|
||||
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."""
|
||||
try:
|
||||
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(
|
||||
bank_id=bank_id,
|
||||
|
|
|
|||
|
|
@ -6840,6 +6840,7 @@ class MemoryEngine(MemoryEngineInterface):
|
|||
bank_id: str,
|
||||
*,
|
||||
status: str | None = None,
|
||||
task_type: str | None = None,
|
||||
limit: int = 20,
|
||||
offset: int = 0,
|
||||
request_context: "RequestContext",
|
||||
|
|
@ -6849,6 +6850,7 @@ class MemoryEngine(MemoryEngineInterface):
|
|||
Args:
|
||||
bank_id: Bank identifier
|
||||
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)
|
||||
offset: Number of operations to skip (default 0)
|
||||
request_context: Request context for authentication
|
||||
|
|
@ -6877,6 +6879,10 @@ class MemoryEngine(MemoryEngineInterface):
|
|||
where_conditions.append(f"status = ${len(params) + 1}")
|
||||
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)
|
||||
|
||||
# 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>)> {
|
||||
self.runtime.block_on(async {
|
||||
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();
|
||||
|
||||
// Find our operation
|
||||
|
|
@ -329,7 +329,7 @@ impl ApiClient {
|
|||
|
||||
pub fn list_operations(&self, agent_id: &str, _verbose: bool) -> Result<OperationsResponse> {
|
||||
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();
|
||||
// Convert to JSON Value first, then parse into our type
|
||||
let json_value = serde_json::to_value(&value)?;
|
||||
|
|
|
|||
|
|
@ -1600,7 +1600,8 @@ paths:
|
|||
/v1/default/banks/{bank_id}/operations:
|
||||
get:
|
||||
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
|
||||
parameters:
|
||||
- explode: false
|
||||
|
|
@ -1620,6 +1621,16 @@ paths:
|
|||
nullable: true
|
||||
type: string
|
||||
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
|
||||
explode: true
|
||||
in: query
|
||||
|
|
|
|||
|
|
@ -280,6 +280,7 @@ type ApiListOperationsRequest struct {
|
|||
ApiService *OperationsAPIService
|
||||
bankId string
|
||||
status *string
|
||||
type_ *string
|
||||
limit *int32
|
||||
offset *int32
|
||||
authorization *string
|
||||
|
|
@ -291,6 +292,12 @@ func (r ApiListOperationsRequest) Status(status string) ApiListOperationsRequest
|
|||
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
|
||||
func (r ApiListOperationsRequest) Limit(limit int32) ApiListOperationsRequest {
|
||||
r.limit = &limit
|
||||
|
|
@ -315,7 +322,7 @@ func (r ApiListOperationsRequest) Execute() (*OperationsListResponse, *http.Resp
|
|||
/*
|
||||
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 bankId
|
||||
|
|
@ -354,6 +361,9 @@ func (a *OperationsAPIService) ListOperationsExecute(r ApiListOperationsRequest)
|
|||
if r.status != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "status", r.status, "form", "")
|
||||
}
|
||||
if r.type_ != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "type", r.type_, "form", "")
|
||||
}
|
||||
if r.limit != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -632,6 +632,7 @@ class OperationsApi:
|
|||
self,
|
||||
bank_id: StrictStr,
|
||||
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,
|
||||
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
||||
authorization: Optional[StrictStr] = None,
|
||||
|
|
@ -650,12 +651,14 @@ class OperationsApi:
|
|||
) -> OperationsListResponse:
|
||||
"""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)
|
||||
:type bank_id: str
|
||||
:param status: Filter by status: pending, completed, or failed
|
||||
: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
|
||||
:type limit: int
|
||||
:param offset: Number of operations to skip
|
||||
|
|
@ -687,6 +690,7 @@ class OperationsApi:
|
|||
_param = self._list_operations_serialize(
|
||||
bank_id=bank_id,
|
||||
status=status,
|
||||
type=type,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
authorization=authorization,
|
||||
|
|
@ -716,6 +720,7 @@ class OperationsApi:
|
|||
self,
|
||||
bank_id: StrictStr,
|
||||
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,
|
||||
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
||||
authorization: Optional[StrictStr] = None,
|
||||
|
|
@ -734,12 +739,14 @@ class OperationsApi:
|
|||
) -> ApiResponse[OperationsListResponse]:
|
||||
"""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)
|
||||
:type bank_id: str
|
||||
:param status: Filter by status: pending, completed, or failed
|
||||
: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
|
||||
:type limit: int
|
||||
:param offset: Number of operations to skip
|
||||
|
|
@ -771,6 +778,7 @@ class OperationsApi:
|
|||
_param = self._list_operations_serialize(
|
||||
bank_id=bank_id,
|
||||
status=status,
|
||||
type=type,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
authorization=authorization,
|
||||
|
|
@ -800,6 +808,7 @@ class OperationsApi:
|
|||
self,
|
||||
bank_id: StrictStr,
|
||||
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,
|
||||
offset: Annotated[Optional[Annotated[int, Field(strict=True, ge=0)]], Field(description="Number of operations to skip")] = None,
|
||||
authorization: Optional[StrictStr] = None,
|
||||
|
|
@ -818,12 +827,14 @@ class OperationsApi:
|
|||
) -> RESTResponseType:
|
||||
"""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)
|
||||
:type bank_id: str
|
||||
:param status: Filter by status: pending, completed, or failed
|
||||
: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
|
||||
:type limit: int
|
||||
:param offset: Number of operations to skip
|
||||
|
|
@ -855,6 +866,7 @@ class OperationsApi:
|
|||
_param = self._list_operations_serialize(
|
||||
bank_id=bank_id,
|
||||
status=status,
|
||||
type=type,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
authorization=authorization,
|
||||
|
|
@ -879,6 +891,7 @@ class OperationsApi:
|
|||
self,
|
||||
bank_id,
|
||||
status,
|
||||
type,
|
||||
limit,
|
||||
offset,
|
||||
authorization,
|
||||
|
|
@ -910,6 +923,10 @@ class OperationsApi:
|
|||
|
||||
_query_params.append(('status', status))
|
||||
|
||||
if type is not None:
|
||||
|
||||
_query_params.append(('type', type))
|
||||
|
||||
if limit is not None:
|
||||
|
||||
_query_params.append(('limit', limit))
|
||||
|
|
|
|||
|
|
@ -738,7 +738,7 @@ export const getChunk = <ThrowOnError extends boolean = false>(
|
|||
/**
|
||||
* 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>(
|
||||
options: Options<ListOperationsData, ThrowOnError>,
|
||||
|
|
|
|||
|
|
@ -3715,6 +3715,12 @@ export type ListOperationsData = {
|
|||
* Filter by status: pending, completed, or failed
|
||||
*/
|
||||
status?: string | null;
|
||||
/**
|
||||
* Type
|
||||
*
|
||||
* Filter by operation type: retain, consolidation, refresh_mental_model, file_convert_retain, webhook_delivery
|
||||
*/
|
||||
type?: string | null;
|
||||
/**
|
||||
* Limit
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ export async function GET(
|
|||
const { agentId } = await params;
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const status = searchParams.get("status") || undefined;
|
||||
const type = searchParams.get("type") || undefined;
|
||||
const limit = searchParams.get("limit") ? parseInt(searchParams.get("limit")!) : undefined;
|
||||
const offset = searchParams.get("offset") ? parseInt(searchParams.get("offset")!) : undefined;
|
||||
|
||||
const response = await sdk.listOperations({
|
||||
client: lowLevelClient,
|
||||
path: { bank_id: agentId },
|
||||
query: { status, limit, offset },
|
||||
query: { status, type, limit, offset },
|
||||
});
|
||||
return NextResponse.json(response.data || {}, { status: 200 });
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useBank } from "@/lib/bank-context";
|
||||
import { client } from "@/lib/api";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -19,6 +19,13 @@ import {
|
|||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { RefreshCw, Clock, AlertCircle, CheckCircle, Loader2, X } from "lucide-react";
|
||||
|
||||
interface Operation {
|
||||
|
|
@ -71,11 +78,21 @@ type OperationDetails =
|
|||
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() {
|
||||
const { currentBank } = useBank();
|
||||
const [operations, setOperations] = useState<Operation[]>([]);
|
||||
const [totalOperations, setTotalOperations] = useState(0);
|
||||
const [statusFilter, setStatusFilter] = useState<string | null>(null);
|
||||
const [taskTypeFilter, setTaskTypeFilter] = useState<string | null>(null);
|
||||
const [limit] = useState(10);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [cancellingOpId, setCancellingOpId] = useState<string | null>(null);
|
||||
|
|
@ -84,9 +101,11 @@ export function BankOperationsView() {
|
|||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [loadingDetails, setLoadingDetails] = useState(false);
|
||||
|
||||
const loadOperations = async (
|
||||
const loadOperations = useCallback(
|
||||
async (
|
||||
newStatusFilter: string | null = statusFilter,
|
||||
newOffset: number = offset
|
||||
newOffset: number = offset,
|
||||
newTaskTypeFilter: string | null = taskTypeFilter
|
||||
) => {
|
||||
if (!currentBank) return;
|
||||
|
||||
|
|
@ -94,6 +113,7 @@ export function BankOperationsView() {
|
|||
try {
|
||||
const opsData = await client.listOperations(currentBank, {
|
||||
status: newStatusFilter || undefined,
|
||||
type: newTaskTypeFilter || undefined,
|
||||
limit,
|
||||
offset: newOffset,
|
||||
});
|
||||
|
|
@ -104,17 +124,25 @@ export function BankOperationsView() {
|
|||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
},
|
||||
[currentBank, statusFilter, offset, taskTypeFilter, limit]
|
||||
);
|
||||
|
||||
const handleFilterChange = (newFilter: string | null) => {
|
||||
setStatusFilter(newFilter);
|
||||
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) => {
|
||||
setOffset(newOffset);
|
||||
loadOperations(statusFilter, newOffset);
|
||||
loadOperations(statusFilter, newOffset, taskTypeFilter);
|
||||
};
|
||||
|
||||
const handleCancelOperation = async (operationId: string) => {
|
||||
|
|
@ -149,12 +177,14 @@ export function BankOperationsView() {
|
|||
|
||||
useEffect(() => {
|
||||
if (currentBank) {
|
||||
loadOperations();
|
||||
// Refresh operations every 5 seconds
|
||||
const interval = setInterval(() => loadOperations(), 5000);
|
||||
loadOperations(statusFilter, offset, taskTypeFilter);
|
||||
const interval = setInterval(
|
||||
() => loadOperations(statusFilter, offset, taskTypeFilter),
|
||||
5000
|
||||
);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [currentBank]);
|
||||
}, [currentBank, statusFilter, offset, taskTypeFilter]);
|
||||
|
||||
if (!currentBank) return null;
|
||||
|
||||
|
|
@ -180,6 +210,27 @@ export function BankOperationsView() {
|
|||
{statusFilter ? ` (${statusFilter})` : ""}
|
||||
</p>
|
||||
</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">
|
||||
{[
|
||||
{ value: null, label: "All" },
|
||||
|
|
@ -201,6 +252,7 @@ export function BankOperationsView() {
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{operations.length > 0 ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -248,10 +248,11 @@ export class ControlPlaneClient {
|
|||
*/
|
||||
async listOperations(
|
||||
bankId: string,
|
||||
options?: { status?: string; limit?: number; offset?: number }
|
||||
options?: { status?: string; type?: string; limit?: number; offset?: number }
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
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?.offset) params.append("offset", options.offset.toString());
|
||||
const query = params.toString();
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@
|
|||
".next-50432/types/**/*.ts",
|
||||
".next-50432/dev/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": [
|
||||
"node_modules"
|
||||
|
|
|
|||
|
|
@ -2357,7 +2357,7 @@
|
|||
"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",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -2387,6 +2387,24 @@
|
|||
},
|
||||
"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",
|
||||
"in": "query",
|
||||
|
|
|
|||
Loading…
Reference in a new issue