77from pydantic import ValidationError
88from qdrant_client import QdrantClient
99
10+ from src .logger import get_logger
1011from src .mapper .data_type_mapper import map_to_data_type_schema
1112from src .mapper .flow_mapper import map_to_grpc_flow , map_to_flow_schema
1213from src .mapper .flow_types_mapper import map_to_flow_type_schema
2122from src .store .function_store import FunctionStore
2223from src .store .model_store import ModelStore
2324
25+ log = get_logger ("generate_endpoint" )
26+
2427
2528class GenerateService (pb2_grpc .GenerateServiceServicer ):
2629
2730 def __init__ (self ):
31+ log .info ("Initializing GenerateService..." )
2832 self .memory_client = QdrantClient (":memory:" )
2933 self .vector_model = load_vector_model ()
3034 self .function_store = FunctionStore (self .memory_client , self .vector_model )
@@ -33,29 +37,35 @@ def __init__(self):
3337 self .model_store = ModelStore ()
3438 self .prompt_orchestrator = PromptOrchestrator ()
3539 self .flow_orchestrator = FlowOrchestrator ()
36- pass
40+ log . success ( "GenerateService ready" ) # type: ignore[attr-defined]
3741
3842 def Prompt (self , request : pb2 .PromptRequest , context ) -> pb2 .FlowResponse :
43+ prompt_preview = request .prompt [:60 ].replace ("\n " , " " ) + ("…" if len (request .prompt ) > 60 else "" )
44+ log .info (f"[Prompt] project={ request .project_id } model={ request .model_identifier } prompt=\" { prompt_preview } \" " )
3945
4046 if not request .project_id :
47+ log .warning ("[Prompt] Rejected — missing project_id" )
4148 context .abort (
4249 code = grpc .StatusCode .INVALID_ARGUMENT ,
4350 details = "The 'project_id' field cannot be empty. Please provide a valid project_id for flow generation."
4451 )
4552
4653 if not request .prompt or not request .prompt .strip ():
54+ log .warning ("[Prompt] Rejected — empty prompt" )
4755 context .abort (
4856 code = grpc .StatusCode .INVALID_ARGUMENT ,
4957 details = "The 'prompt' field cannot be empty. Please provide a valid prompt for flow generation."
5058 )
5159
5260 if not request .model_identifier or not request .model_identifier .strip ():
61+ log .warning ("[Prompt] Rejected — missing model_identifier" )
5362 context .abort (
5463 code = grpc .StatusCode .INVALID_ARGUMENT ,
5564 details = "The 'model_identifier' field cannot be empty. Please provide a valid model_identifier for flow generation."
5665 )
5766
5867 if self .model_store .find (identifier = request .model_identifier ) is None :
68+ log .warning (f"[Prompt] Rejected — unknown model '{ request .model_identifier } '" )
5969 context .abort (
6070 code = grpc .StatusCode .INVALID_ARGUMENT ,
6171 details = f"The specified model_identifier '{ request .model_identifier } ' does not exist. Please provide a valid model_identifier for flow generation."
@@ -66,6 +76,7 @@ def Prompt(self, request: pb2.PromptRequest, context) -> pb2.FlowResponse:
6676 group_identifier = str (request .project_id )
6777 )
6878 ) <= 0 and (len (request .functions ) <= 0 ):
79+ log .warning (f"[Prompt] Rejected — no functions for project={ request .project_id } " )
6980 context .abort (
7081 code = grpc .StatusCode .ABORTED ,
7182 details = "No functions found for the given project_id. Please add functions before requesting a prompt generation."
@@ -76,6 +87,7 @@ def Prompt(self, request: pb2.PromptRequest, context) -> pb2.FlowResponse:
7687 group_identifier = str (request .project_id )
7788 )
7889 ) <= 0 and (len (request .flow_types ) <= 0 ):
90+ log .warning (f"[Prompt] Rejected — no flow types for project={ request .project_id } " )
7991 context .abort (
8092 code = grpc .StatusCode .ABORTED ,
8193 details = "No flow types found for the given project_id. Please add flow_types before requesting a prompt generation."
@@ -128,6 +140,12 @@ def Prompt(self, request: pb2.PromptRequest, context) -> pb2.FlowResponse:
128140 limit = 2
129141 )
130142
143+ log .debug (
144+ f"[Prompt] Context — functions={ len (prompt_functions )} "
145+ f"flow_types={ len (prompt_flow_types )} "
146+ f"few_shots={ len (prompt_few_shots )} "
147+ )
148+
131149 few_shot_function_ids = list ({
132150 node .function_identifier
133151 for fS in prompt_few_shots
@@ -139,6 +157,11 @@ def Prompt(self, request: pb2.PromptRequest, context) -> pb2.FlowResponse:
139157 if fS .flow .type
140158 })
141159
160+ if few_shot_function_ids :
161+ log .debug (f"[Prompt] Few-shot functions: { few_shot_function_ids } " )
162+ if few_shot_flow_type_ids :
163+ log .debug (f"[Prompt] Few-shot flow types: { few_shot_flow_type_ids } " )
164+
142165 few_shot_functions = self .function_store .find_all (
143166 group_identifier = str (request .project_id ),
144167 identifiers = few_shot_function_ids
@@ -157,51 +180,70 @@ def Prompt(self, request: pb2.PromptRequest, context) -> pb2.FlowResponse:
157180 )
158181 ]
159182
183+ combined_functions = self .function_store .combine (prompt_functions , few_shot_functions )
184+ combined_flow_types = self .flow_type_store .combine (prompt_flow_types , few_shots_flow_types )
185+
186+ log .debug (
187+ f"[Prompt] Combined — functions={ len (combined_functions )} "
188+ f"flow_types={ len (combined_flow_types )} "
189+ )
190+ log .info (f"[Prompt] Generating flow..." )
191+
192+ t0 = time .time ()
160193 try :
161194 generated_flow , completion = self .prompt_orchestrator .generate (
162195 model = self .model_store .find (identifier = request .model_identifier ),
163196 prompt = request .prompt ,
164197 few_shots = few_shots ,
165- available_functions = self .function_store .combine (prompt_functions , few_shot_functions ),
166- available_flow_types = self .flow_type_store .combine (prompt_flow_types , few_shots_flow_types )
198+ available_functions = combined_functions ,
199+ available_flow_types = combined_flow_types
200+ )
201+
202+ elapsed = time .time () - t0
203+ log .success ( # type: ignore[attr-defined]
204+ f"[Prompt] Generated '{ generated_flow .name } ' in { elapsed :.2f} s | tokens={ completion .usage .total_tokens } "
167205 )
168206
169207 current_time_ms = int (time .time () * 1000 )
170208 return pb2 .FlowResponse (
171209 flow = map_to_grpc_flow (
172210 flow_postprocessing (
173211 generated_flow ,
174- self .flow_type_store .combine (prompt_flow_types ,
175- few_shots_flow_types ),
176- self .function_store .combine (prompt_functions ,
177- few_shot_functions )
212+ combined_flow_types ,
213+ combined_functions
178214 )
179215 ),
180216 cached_until = current_time_ms + 300000 ,
181217 usage = completion .usage .total_tokens
182218 )
183219 except Exception as e :
184- import traceback
185- traceback . print_exc ( )
220+ elapsed = time . time () - t0
221+ log . error ( f"[Prompt] Generation failed after { elapsed :.2f } s: { e } " , exc_info = True )
186222 context .abort (
187223 code = grpc .StatusCode .INTERNAL ,
188224 details = "An unexpected error occurred during flow generation."
189225 )
190226
191227 def Flow (self , request : pb2 .FlowRequest , context ) -> pb2 .FlowResponse :
228+ prompt_preview = request .prompt [:60 ].replace ("\n " , " " ) + ("…" if len (request .prompt ) > 60 else "" )
229+ log .info (f"[Flow] project={ request .project_id } model={ request .model_identifier } prompt=\" { prompt_preview } \" " )
230+
192231 if not request .project_id :
232+ log .warning ("[Flow] Rejected — missing project_id" )
193233 context .abort (
194234 code = grpc .StatusCode .INVALID_ARGUMENT ,
195235 details = "The 'project_id' field cannot be empty. Please provide a valid project_id for flow generation."
196236 )
197237
198238 if not request .prompt or not request .prompt .strip ():
239+ log .warning ("[Flow] Rejected — empty prompt" )
199240 context .abort (
200241 code = grpc .StatusCode .INVALID_ARGUMENT ,
201242 details = "The 'prompt' field cannot be empty. Please provide a valid prompt for flow generation."
202243 )
203244
204245 if not request .flow :
246+ log .warning ("[Flow] Rejected — missing flow" )
205247 context .abort (
206248 code = grpc .StatusCode .INVALID_ARGUMENT ,
207249 details = "The 'flow' field is invalid. Please provide a valid flow for flow generation."
@@ -210,18 +252,21 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
210252 try :
211253 Flow .model_validate (map_to_flow_schema (request .flow ))
212254 except ValidationError as e :
255+ log .warning (f"[Flow] Rejected — invalid flow schema: { e } " )
213256 context .abort (
214257 code = grpc .StatusCode .INVALID_ARGUMENT ,
215258 details = f"The 'flow' field is invalid. Please provide a valid flow for flow generation."
216259 )
217260
218261 if not request .model_identifier or not request .model_identifier .strip ():
262+ log .warning ("[Flow] Rejected — missing model_identifier" )
219263 context .abort (
220264 code = grpc .StatusCode .INVALID_ARGUMENT ,
221265 details = "The 'model_identifier' field cannot be empty. Please provide a valid model_identifier for flow generation."
222266 )
223267
224268 if self .model_store .find (identifier = request .model_identifier ) is None :
269+ log .warning (f"[Flow] Rejected — unknown model '{ request .model_identifier } '" )
225270 context .abort (
226271 code = grpc .StatusCode .INVALID_ARGUMENT ,
227272 details = f"The specified model_identifier '{ request .model_identifier } ' does not exist. Please provide a valid model_identifier for flow generation."
@@ -232,6 +277,7 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
232277 group_identifier = str (request .project_id )
233278 )
234279 ) <= 0 and (len (request .functions ) <= 0 ):
280+ log .warning (f"[Flow] Rejected — no functions for project={ request .project_id } " )
235281 context .abort (
236282 code = grpc .StatusCode .ABORTED ,
237283 details = "No functions found for the given project_id. Please add functions before requesting a prompt generation."
@@ -242,6 +288,7 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
242288 group_identifier = str (request .project_id )
243289 )
244290 ) <= 0 and (len (request .flow_types ) <= 0 ):
291+ log .warning (f"[Flow] Rejected — no flow types for project={ request .project_id } " )
245292 context .abort (
246293 code = grpc .StatusCode .ABORTED ,
247294 details = "No flow types found for the given project_id. Please add flow_types before requesting a prompt generation."
@@ -294,6 +341,12 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
294341 limit = 2
295342 )
296343
344+ log .debug (
345+ f"[Flow] Context — functions={ len (prompt_functions )} "
346+ f"flow_types={ len (prompt_flow_types )} "
347+ f"few_shots={ len (flow_few_shots )} "
348+ )
349+
297350 flow_few_shot_function_ids = list ({
298351 node .function_identifier
299352 for fS in flow_few_shots
@@ -305,6 +358,11 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
305358 if fS .flow .type
306359 })
307360
361+ if flow_few_shot_function_ids :
362+ log .debug (f"[Flow] Few-shot functions: { flow_few_shot_function_ids } " )
363+ if flow_few_shot_flow_type_ids :
364+ log .debug (f"[Flow] Few-shot flow types: { flow_few_shot_flow_type_ids } " )
365+
308366 flow_few_shot_functions = self .function_store .find_all (
309367 group_identifier = str (request .project_id ),
310368 identifiers = flow_few_shot_function_ids
@@ -323,29 +381,46 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
323381 )
324382 ]
325383
384+ combined_functions = self .function_store .combine (prompt_functions , flow_few_shot_functions )
385+ combined_flow_types = self .flow_type_store .combine (prompt_flow_types , flow_few_shots_flow_types )
386+
387+ log .debug (
388+ f"[Flow] Combined — functions={ len (combined_functions )} "
389+ f"flow_types={ len (combined_flow_types )} "
390+ )
391+ log .info (f"[Flow] Modifying flow..." )
392+
393+ t0 = time .time ()
326394 try :
327395 generated_flow , completion = self .flow_orchestrator .generate (
328396 model = self .model_store .find (identifier = request .model_identifier ),
329397 prompt = request .prompt ,
330398 flow = map_to_flow_schema (request .flow ),
331399 few_shots = few_shots ,
332- available_functions = self .function_store .combine (prompt_functions , flow_few_shot_functions ),
333- available_flow_types = self .flow_type_store .combine (prompt_flow_types , flow_few_shots_flow_types )
400+ available_functions = combined_functions ,
401+ available_flow_types = combined_flow_types
402+ )
403+
404+ elapsed = time .time () - t0
405+ log .success ( # type: ignore[attr-defined]
406+ f"[Flow] Modified '{ generated_flow .name } ' in { elapsed :.2f} s | tokens={ completion .usage .total_tokens } "
334407 )
335408
336409 current_time_ms = int (time .time () * 1000 )
337410 return pb2 .FlowResponse (
338411 flow = map_to_grpc_flow (
339412 flow_postprocessing (
340413 generated_flow ,
341- self . flow_type_store . combine ( prompt_flow_types , flow_few_shots_flow_types ) ,
342- self . function_store . combine ( prompt_functions , flow_few_shot_functions )
414+ combined_flow_types ,
415+ combined_functions
343416 )
344417 ),
345418 cached_until = current_time_ms + 300000 ,
346419 usage = completion .usage .total_tokens
347420 )
348421 except Exception as e :
422+ elapsed = time .time () - t0
423+ log .error (f"[Flow] Generation failed after { elapsed :.2f} s: { e } " , exc_info = True )
349424 context .abort (
350425 code = grpc .StatusCode .INTERNAL ,
351426 details = "An unexpected error occurred during flow generation."
0 commit comments