2025-08-28 18:40:32 +08:00
#
2025-09-05 18:50:46 +08:00
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
2025-08-28 18:40:32 +08:00
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
2025-12-09 19:23:14 +08:00
import asyncio
2025-08-28 18:40:32 +08:00
import datetime
import json
import logging
import random
2025-10-09 12:36:19 +08:00
from timeit import default_timer as timer
2025-08-28 18:40:32 +08:00
from agent . canvas import Graph
from api . db . services . document_service import DocumentService
2025-10-09 12:36:19 +08:00
from api . db . services . task_service import has_canceled , TaskService , CANVAS_DEBUG_DOC_ID
2025-08-28 18:40:32 +08:00
from rag . utils . redis_conn import REDIS_CONN
class Pipeline ( Graph ) :
2026-07-08 20:08:14 +08:00
def __init__ ( self , dsl : str | dict , tenant_id = None , doc_id = None , task_id = None , flow_id = None , language = None ) :
2025-10-09 12:36:19 +08:00
if isinstance ( dsl , dict ) :
dsl = json . dumps ( dsl , ensure_ascii = False )
2025-08-28 18:40:32 +08:00
super ( ) . __init__ ( dsl , tenant_id , task_id )
2025-10-09 12:36:19 +08:00
if doc_id == CANVAS_DEBUG_DOC_ID :
doc_id = None
2025-08-28 18:40:32 +08:00
self . _doc_id = doc_id
self . _flow_id = flow_id
2026-07-08 20:08:14 +08:00
self . _language = language
2025-08-28 18:40:32 +08:00
self . _kb_id = None
2025-10-09 12:36:19 +08:00
if self . _doc_id :
2025-08-28 18:40:32 +08:00
self . _kb_id = DocumentService . get_knowledgebase_id ( doc_id )
2025-10-09 12:36:19 +08:00
if not self . _kb_id :
self . _doc_id = None
2025-08-28 18:40:32 +08:00
2025-09-05 18:50:46 +08:00
def callback ( self , component_name : str , progress : float | int | None = None , message : str = " " ) - > None :
2025-11-06 16:12:20 +08:00
from common . exceptions import TaskCanceledException
2026-07-03 12:53:39 +08:00
2025-08-28 18:40:32 +08:00
log_key = f " { self . _flow_id } - { self . task_id } -logs "
2025-10-09 12:36:19 +08:00
timestamp = timer ( )
if has_canceled ( self . task_id ) :
progress = - 1
message + = " [CANCEL] "
2025-08-28 18:40:32 +08:00
try :
bin = REDIS_CONN . get ( log_key )
2026-05-27 21:54:17 +08:00
obj = json . loads ( bin . encode ( " utf-8 " ) ) if bin else [ ]
2025-08-28 18:40:32 +08:00
if obj :
2025-10-09 12:36:19 +08:00
if obj [ - 1 ] [ " component_id " ] == component_name :
obj [ - 1 ] [ " trace " ] . append (
{
" progress " : progress ,
" message " : message ,
" datetime " : datetime . datetime . now ( ) . strftime ( " % H: % M: % S " ) ,
" timestamp " : timestamp ,
" elapsed_time " : timestamp - obj [ - 1 ] [ " trace " ] [ - 1 ] [ " timestamp " ] ,
}
)
2025-08-28 18:40:32 +08:00
else :
2025-10-09 12:36:19 +08:00
obj . append (
{
" component_id " : component_name ,
" trace " : [ { " progress " : progress , " message " : message , " datetime " : datetime . datetime . now ( ) . strftime ( " % H: % M: % S " ) , " timestamp " : timestamp , " elapsed_time " : 0 } ] ,
}
)
2025-08-28 18:40:32 +08:00
else :
2025-10-09 12:36:19 +08:00
obj = [
{
" component_id " : component_name ,
" trace " : [ { " progress " : progress , " message " : message , " datetime " : datetime . datetime . now ( ) . strftime ( " % H: % M: % S " ) , " timestamp " : timestamp , " elapsed_time " : 0 } ] ,
}
]
if component_name != " END " and self . _doc_id and self . task_id :
percentage = 1.0 / len ( self . components . items ( ) )
finished = 0.0
for o in obj :
for t in o [ " trace " ] :
if t [ " progress " ] < 0 :
finished = - 1
break
if finished < 0 :
break
finished + = o [ " trace " ] [ - 1 ] [ " progress " ] * percentage
msg = " "
if len ( obj [ - 1 ] [ " trace " ] ) == 1 :
msg + = f " \n ------------------------------------- \n [ { self . get_component_name ( o [ ' component_id ' ] ) } ]: \n "
t = obj [ - 1 ] [ " trace " ] [ - 1 ]
msg + = " %s : %s \n " % ( t [ " datetime " ] , t [ " message " ] )
TaskService . update_progress ( self . task_id , { " progress " : finished , " progress_msg " : msg } )
elif component_name == " END " and not self . _doc_id :
obj [ - 1 ] [ " trace " ] [ - 1 ] [ " dsl " ] = json . loads ( str ( self ) )
REDIS_CONN . set_obj ( log_key , obj , 60 * 30 )
2025-08-28 18:40:32 +08:00
except Exception as e :
logging . exception ( e )
2025-10-09 12:36:19 +08:00
if has_canceled ( self . task_id ) :
raise TaskCanceledException ( message )
2025-08-28 18:40:32 +08:00
def fetch_logs ( self ) :
log_key = f " { self . _flow_id } - { self . task_id } -logs "
try :
bin = REDIS_CONN . get ( log_key )
if bin :
return json . loads ( bin . encode ( " utf-8 " ) )
except Exception as e :
logging . exception ( e )
return [ ]
2025-10-09 12:36:19 +08:00
async def run ( self , * * kwargs ) :
2025-08-28 18:40:32 +08:00
log_key = f " { self . _flow_id } - { self . task_id } -logs "
try :
2025-09-05 18:50:46 +08:00
REDIS_CONN . set_obj ( log_key , [ ] , 60 * 10 )
2025-08-28 18:40:32 +08:00
except Exception as e :
logging . exception ( e )
2025-10-09 12:36:19 +08:00
self . error = " "
2025-08-28 18:40:32 +08:00
if not self . path :
2025-09-05 18:50:46 +08:00
self . path . append ( " File " )
2025-08-28 18:40:32 +08:00
cpn_obj = self . get_component_obj ( self . path [ 0 ] )
await cpn_obj . invoke ( * * kwargs )
if cpn_obj . error ( ) :
self . error = " [ERROR] " + cpn_obj . error ( )
2025-10-09 12:36:19 +08:00
self . callback ( cpn_obj . component_name , - 1 , self . error )
if self . _doc_id :
2026-07-03 12:53:39 +08:00
TaskService . update_progress (
self . task_id , { " progress " : random . randint ( 0 , 5 ) / 100.0 , " progress_msg " : " Start the pipeline... " , " begin_at " : datetime . datetime . now ( ) . strftime ( " % Y- % m- %d % H: % M: % S " ) }
)
2025-10-09 12:36:19 +08:00
idx = len ( self . path ) - 1
cpn_obj = self . get_component_obj ( self . path [ idx ] )
idx + = 1
self . path . extend ( cpn_obj . get_downstream ( ) )
2025-08-28 18:40:32 +08:00
while idx < len ( self . path ) and not self . error :
2025-09-05 18:50:46 +08:00
last_cpn = self . get_component_obj ( self . path [ idx - 1 ] )
2025-08-28 18:40:32 +08:00
cpn_obj = self . get_component_obj ( self . path [ idx ] )
2025-09-05 18:50:46 +08:00
2025-08-28 18:40:32 +08:00
async def invoke ( ) :
nonlocal last_cpn , cpn_obj
await cpn_obj . invoke ( * * last_cpn . output ( ) )
2026-07-03 12:53:39 +08:00
# if inspect.iscoroutinefunction(cpn_obj.invoke):
2025-10-09 12:36:19 +08:00
# await cpn_obj.invoke(**last_cpn.output())
2026-07-03 12:53:39 +08:00
# else:
2025-10-09 12:36:19 +08:00
# cpn_obj.invoke(**last_cpn.output())
2025-09-05 18:50:46 +08:00
2025-12-09 19:23:14 +08:00
tasks = [ ]
tasks . append ( asyncio . create_task ( invoke ( ) ) )
await asyncio . gather ( * tasks )
2025-10-09 12:36:19 +08:00
2025-08-28 18:40:32 +08:00
if cpn_obj . error ( ) :
self . error = " [ERROR] " + cpn_obj . error ( )
2025-10-09 12:36:19 +08:00
self . callback ( cpn_obj . _id , - 1 , self . error )
2025-08-28 18:40:32 +08:00
break
idx + = 1
self . path . extend ( cpn_obj . get_downstream ( ) )
2025-10-09 12:36:19 +08:00
self . callback ( " END " , 1 if not self . error else - 1 , json . dumps ( self . get_component_obj ( self . path [ - 1 ] ) . output ( ) , ensure_ascii = False ) )
if not self . error :
return self . get_component_obj ( self . path [ - 1 ] ) . output ( )
2026-07-03 12:53:39 +08:00
TaskService . update_progress ( self . task_id , { " progress " : - 1 , " progress_msg " : f " [ERROR]: { self . error } " } )
2025-10-09 12:36:19 +08:00
return { }