Skip to content

Commit fc94107

Browse files
committed
feat: logging
1 parent b2895e7 commit fc94107

11 files changed

Lines changed: 286 additions & 29 deletions

File tree

main.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import grpc
77

88
from src.interceptor.auth_interceptor import AuthInterceptor
9+
from src.logger import get_logger
10+
11+
log = get_logger("velorum")
912

1013
current_dir = os.path.dirname(os.path.abspath(__file__))
1114
venv_site_packages = os.path.join(current_dir, ".venv", "lib", "python3.12", "site-packages")
@@ -36,7 +39,7 @@
3639

3740
port = os.getenv("HOST", "0.0.0.0") + ":" + os.getenv("PORT", "50051")
3841
server.add_insecure_port(port)
39-
print(f"Velorum GenerateService läuft auf {port}...")
42+
log.success(f"Velorum listening on {port}") # type: ignore[attr-defined]
4043

4144
SERVICE_NAMES = (
4245
generate_pb2.DESCRIPTOR.services_by_name['GenerateService'].full_name,
@@ -51,5 +54,5 @@
5154
while True:
5255
time.sleep(86400)
5356
except KeyboardInterrupt:
54-
print("\nServer wird sauber heruntergefahren...")
57+
log.info("Shutting down gracefully...")
5558
server.stop(0)

src/endpoint/generation/generate_endpoint.py

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pydantic import ValidationError
88
from qdrant_client import QdrantClient
99

10+
from src.logger import get_logger
1011
from src.mapper.data_type_mapper import map_to_data_type_schema
1112
from src.mapper.flow_mapper import map_to_grpc_flow, map_to_flow_schema
1213
from src.mapper.flow_types_mapper import map_to_flow_type_schema
@@ -21,10 +22,13 @@
2122
from src.store.function_store import FunctionStore
2223
from src.store.model_store import ModelStore
2324

25+
log = get_logger("generate_endpoint")
26+
2427

2528
class 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."

src/endpoint/info/info_endpoint.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import tucana.generated.velorum.info_pb2 as pb2
22
import tucana.generated.velorum.info_pb2_grpc as pb2_grpc
33

4+
from src.logger import get_logger
45
from src.mapper.model_mapper import map_to_grpc_model
56
from src.store.model_store import ModelStore
67

8+
log = get_logger("info_endpoint")
9+
710

811
class InfoService(pb2_grpc.InfoServiceServicer):
912
def __init__(self):
1013
self.model_store = ModelStore()
1114

1215
def Models(self, request: pb2.ModelsRequest, context) -> pb2.ModelsResponse:
13-
return pb2.ModelsResponse(models=[map_to_grpc_model(model) for model in self.model_store.get_all()])
16+
models = self.model_store.get_all()
17+
log.debug(f"[Models] Returning {len(models)} model(s)")
18+
return pb2.ModelsResponse(models=[map_to_grpc_model(model) for model in models])

src/interceptor/auth_interceptor.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33
import grpc
44
import jwt
55

6+
from src.logger import get_logger
7+
68
VALID_TOKEN = os.getenv('SECURITY_TOKEN', 'velorum-internal-communication-secret')
79

10+
log = get_logger("auth_interceptor")
11+
12+
813
class AuthInterceptor(grpc.ServerInterceptor):
914

1015
def intercept_service(self, continuation, handler_call_details):
1116
metadata = dict(handler_call_details.invocation_metadata or [])
1217
auth_token = metadata.get('authorization', '')
1318

1419
if not auth_token:
20+
log.warning(f"Unauthorized request — no token | method={handler_call_details.method}")
1521
return self._abort_handler()
1622

1723
try:
@@ -21,7 +27,7 @@ def intercept_service(self, continuation, handler_call_details):
2127
algorithms=['HS256']
2228
)
2329
except Exception as e:
24-
print(e)
30+
log.warning(f"Invalid token | method={handler_call_details.method} error={e}")
2531
return self._abort_handler()
2632

2733
return continuation(handler_call_details)

0 commit comments

Comments
 (0)