sunholo 0.89.1__py3-none-any.whl → 0.89.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,6 +20,12 @@ from .langserve import prepare_request_data
20
20
 
21
21
  from .route import route_endpoint
22
22
 
23
+ try:
24
+ from langfuse import Langfuse
25
+ langfuse = Langfuse()
26
+ except ImportError:
27
+ langfuse = None
28
+
23
29
  def prep_request_payload(user_input, chat_history, vector_name, stream, **kwargs):
24
30
  """
25
31
  Prepares the request payload for sending a query to the QA system.
@@ -79,9 +85,21 @@ def prep_request_payload(user_input, chat_history, vector_name, stream, **kwargs
79
85
 
80
86
  if 'vector_name' not in qna_data:
81
87
  qna_data['vector_name'] = vector_name
88
+
89
+ qna_data['trace_id'] = add_langfuse_trace(qna_endpoint)
82
90
 
83
91
  return qna_endpoint, qna_data
84
92
 
93
+ def add_langfuse_trace(qna_endpoint):
94
+ if not langfuse:
95
+ return None
96
+
97
+ trace = langfuse.trace(name = f'auto/{qna_endpoint}')
98
+
99
+ log.info('Adding langfuse trace {trace.id}')
100
+
101
+ return trace.id
102
+
85
103
  def send_to_qa(user_input, vector_name, chat_history, stream=False, **kwargs):
86
104
  """
87
105
  Sends a query to the QA system synchronously.
@@ -2,6 +2,7 @@ import json
2
2
  import traceback
3
3
  import datetime
4
4
  import uuid
5
+ import random
5
6
 
6
7
  from ...agents import extract_chat_history, handle_special_commands
7
8
  from ...qna.parsers import parse_output
@@ -18,12 +19,12 @@ from datetime import timedelta
18
19
  try:
19
20
  from flask import request, jsonify, Response
20
21
  except ImportError:
21
- pass
22
+ pass
22
23
 
23
24
  try:
24
- from langfuse.decorators import langfuse_context, observe
25
+ from ..pubsub import PubSubManager
25
26
  except ImportError:
26
- pass
27
+ PubSubManager = None
27
28
 
28
29
  # Cache dictionary to store validated API keys
29
30
  api_key_cache = {}
@@ -161,7 +162,7 @@ if __name__ == "__main__":
161
162
 
162
163
 
163
164
  def handle_stream_vac(self, vector_name):
164
- observed_stream_interpreter = observe()(self.stream_interpreter)
165
+ observed_stream_interpreter = self.stream_interpreter
165
166
  prep = self.prep_vac(request, vector_name)
166
167
  log.info(f"Processing prep: {prep}")
167
168
  trace = prep["trace"]
@@ -221,11 +222,41 @@ if __name__ == "__main__":
221
222
  generation.end(output=response)
222
223
  span.end(output=response)
223
224
  trace.update(output=response)
225
+ self.langfuse_eval_response(trace.id, all_input.get('eval_percent', 0.01))
224
226
 
225
227
  return response
228
+
229
+ def langfuse_eval_response(trace_id, eval_percent=0.01):
230
+ """
231
+ Sends an evaluation message based on a probability defined by eval_percent.
232
+
233
+ Args:
234
+ eval_percent (float): The probability (0 to 1) of triggering the evaluation.
235
+ trace_id (str): The trace identifier for the evaluation.
236
+
237
+ Returns:
238
+ None
239
+ """
240
+ if eval_percent > 1 or eval_percent < 0:
241
+ raise ValueError("eval_percent must be a float between 0 and 1.")
242
+
243
+ # Generate a random float between 0 and 1
244
+ random_value = random.random()
245
+
246
+ # Check if evaluation should be triggered
247
+ if random_value < eval_percent:
248
+ if PubSubManager:
249
+ try:
250
+ pubsub_manager = PubSubManager("langfuse_evals", pubsub_topic="topicid-to-langfuse-eval")
251
+ the_data = {"trace_id": trace_id}
252
+ pubsub_manager.publish_message(the_data)
253
+ except Exception as e:
254
+ log.warning(f"Could not publish message for 'langfuse_evals' to topicid-to-langfuse-eval - {str(e)}")
255
+ else:
256
+ log.info(f"Did not do Langfuse eval due to random sampling not passed: {eval_percent=}")
226
257
 
227
258
  def handle_process_vac(self, vector_name):
228
- observed_vac_interpreter = observe()(self.vac_interpreter)
259
+ observed_vac_interpreter = self.vac_interpreter
229
260
  prep = self.prep_vac(request, vector_name)
230
261
  log.debug(f"Processing prep: {prep}")
231
262
  trace = prep["trace"]
@@ -268,6 +299,7 @@ if __name__ == "__main__":
268
299
  if trace:
269
300
  span.end(output=jsonify(bot_output))
270
301
  trace.update(output=jsonify(bot_output))
302
+ self.langfuse_eval_response(trace.id, all_input.get('eval_percent', 0.01))
271
303
 
272
304
  # {'answer': 'output'}
273
305
  return jsonify(bot_output)
@@ -365,7 +397,7 @@ if __name__ == "__main__":
365
397
  "kwargs": data
366
398
  }
367
399
 
368
- observed_stream_interpreter = observe()(self.stream_interpreter)
400
+ observed_stream_interpreter = self.stream_interpreter
369
401
 
370
402
  response_id = str(uuid.uuid4())
371
403
 
@@ -416,7 +448,7 @@ if __name__ == "__main__":
416
448
  return Response(generate_response_content(), content_type='text/plain; charset=utf-8')
417
449
 
418
450
  try:
419
- observed_vac_interpreter = observe()(self.vac_interpreter)
451
+ observed_vac_interpreter = self.vac_interpreter
420
452
  bot_output = observed_vac_interpreter(
421
453
  question=user_message,
422
454
  vector_name=vector_name,
@@ -437,7 +469,7 @@ if __name__ == "__main__":
437
469
  return self.make_openai_response(user_message, vector_name, f'ERROR: {str(err)}')
438
470
 
439
471
 
440
- def create_langfuse_trace(self, request, vector_name):
472
+ def create_langfuse_trace(self, request, vector_name, trace_id):
441
473
  try:
442
474
  from langfuse import Langfuse
443
475
  langfuse = Langfuse()
@@ -451,11 +483,12 @@ if __name__ == "__main__":
451
483
  message_source = request.headers.get("X-Message-Source")
452
484
 
453
485
  package_version = sunholo_version()
454
- tags = [package_version]
486
+ tags = [package_version, "autogenerated"]
455
487
  if message_source:
456
488
  tags.append(message_source)
457
489
 
458
490
  return langfuse.trace(
491
+ id=trace_id,
459
492
  name = f"/vac/{vector_name}",
460
493
  user_id = user_id,
461
494
  session_id = session_id,
@@ -464,8 +497,6 @@ if __name__ == "__main__":
464
497
  )
465
498
 
466
499
  def prep_vac(self, request, vector_name):
467
- trace = self.create_langfuse_trace(request, vector_name)
468
- span = None
469
500
 
470
501
  if request.content_type.startswith('application/json'):
471
502
  data = request.get_json()
@@ -490,6 +521,10 @@ if __name__ == "__main__":
490
521
 
491
522
  log.info(f"vac/{vector_name} got data: {data}")
492
523
 
524
+ trace_id = data.get('trace_id')
525
+ trace = self.create_langfuse_trace(request, vector_name, trace_id)
526
+ span = None
527
+
493
528
  config, _ = load_config("config/llm_config.yaml")
494
529
  vac_configs = config.get("vac")
495
530
  if vac_configs:
sunholo/langfuse/evals.py CHANGED
@@ -29,18 +29,30 @@ def pubsub_to_evals(data: dict, eval_funcs: list=[eval_length]) -> dict:
29
29
 
30
30
  if 'trace_id' not in message_data:
31
31
  raise ValueError('No trace_id found in message data')
32
-
32
+
33
+ trace_id = message_data.pop('trace_id', None)
34
+
35
+ return do_evals(trace_id, eval_funcs, **message_data)
36
+
37
+
38
+ def direct_langfuse_evals(data, eval_funcs: list=[eval_length]):
39
+ if 'trace_id' not in data:
40
+ raise ValueError('No trace_id found in data')
41
+ trace_id = data.pop('trace_id', None)
42
+
43
+ return do_evals(trace_id, eval_funcs, **data)
44
+
45
+
46
+ def do_evals(trace_id, eval_funcs: list=[eval_length], **kwargs) -> dict:
33
47
  # Initialize Langfuse with environment variables
34
48
  langfuse = Langfuse(
35
49
  secret_key=os.environ["LANGFUSE_SECRET_KEY"],
36
50
  public_key=os.environ["LANGFUSE_PUBLIC_KEY"],
37
51
  host=os.environ["LANGFUSE_HOST"]
38
52
  )
39
-
40
- trace_id = message_data.pop('trace_id', None)
41
53
 
42
54
  # Fetch the latest trace (or modify as needed to fetch a specific trace)
43
- trace = langfuse.fetch_trace(id=trace_id)
55
+ trace = langfuse.get_trace(id=trace_id)
44
56
 
45
57
  if trace.output is None:
46
58
  raise ValueError("Trace {trace.name} had no generated output, it was skipped")
@@ -53,7 +65,7 @@ def pubsub_to_evals(data: dict, eval_funcs: list=[eval_length]) -> dict:
53
65
 
54
66
  eval_name = eval_func.__name__
55
67
 
56
- if 'score' or 'reason' not in eval_result:
68
+ if 'score' and 'reason' not in eval_result:
57
69
  raise ValueError(f"Trace {trace.name} using {eval_name=} did not return a dict with 'score' and 'reason': {eval_result=}")
58
70
 
59
71
  log.info(f"TraceId {trace.id} with name {trace.name} had {eval_name=} with score {eval_result=}")
@@ -64,7 +76,7 @@ def pubsub_to_evals(data: dict, eval_funcs: list=[eval_length]) -> dict:
64
76
  name=eval_name, # Use the function name as the evaluation name
65
77
  value=eval_result["score"],
66
78
  comment=eval_result["reason"],
67
- **message_data
79
+ **kwargs
68
80
  )
69
81
 
70
82
  return {"trace_id": trace.id, "eval_results": eval_results}
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.89.1
3
+ Version: 0.89.4
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Home-page: https://github.com/sunholo-data/sunholo-py
6
- Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.89.1.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.89.4.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -2,7 +2,7 @@ sunholo/__init__.py,sha256=lLuVyilzmDbTaiAptR8SZzpbUNsgwHFsp4Ejbr5EApI,1136
2
2
  sunholo/custom_logging.py,sha256=YfIN1oP3dOEkkYkyRBU8BGS3uJFGwUDsFCl8mIVbwvE,12225
3
3
  sunholo/agents/__init__.py,sha256=X2I3pPkGeKWjc3d0QgSpkTyqD8J8JtrEWqwrumf1MMc,391
4
4
  sunholo/agents/chat_history.py,sha256=Gph_CdlP2otYnNdR1q1Umyyyvcad2F6K3LxU5yBQ9l0,5387
5
- sunholo/agents/dispatch_to_qa.py,sha256=49-10UGxwcqA65Lm-S2ofTBaDHZLRZgSWy-Jj8OwKHs,8381
5
+ sunholo/agents/dispatch_to_qa.py,sha256=Z2q0ygYxfgBr-EGydq_H5y4Y-bKlY4ZCBCwkGpYwjFY,8766
6
6
  sunholo/agents/langserve.py,sha256=C46ph2mnygr6bdHijYWYyfQDI9ylAF0_9Kx2PfcCJpU,4414
7
7
  sunholo/agents/pubsub.py,sha256=TscZN_6am6DfaQkC-Yl18ZIBOoLE-0nDSiil6GpQEh4,1344
8
8
  sunholo/agents/route.py,sha256=Mo-YOHDsHOiQXfb6VmCH0BPThAD-0jZGIkIKAHxSDdc,2986
@@ -14,7 +14,7 @@ sunholo/agents/fastapi/qna_routes.py,sha256=lKHkXPmwltu9EH3RMwmD153-J6pE7kWQ4BhB
14
14
  sunholo/agents/flask/__init__.py,sha256=poJDKMr2qj8qMb99JqCvCPSiEt1tj2tLQ3hKW3f2aVw,107
15
15
  sunholo/agents/flask/base.py,sha256=FgSaCODyoTtlstJtsqlLPScdgRUtv9_plxftdzHdVFo,809
16
16
  sunholo/agents/flask/qna_routes.py,sha256=uwUD1yrzOPH27m2AXpiQrPk_2VfJOQOM6dAynOWQtoQ,22532
17
- sunholo/agents/flask/vac_routes.py,sha256=aZ69r4V5s5EHzJ8Tht0KTuEiHrSPHFiwF8XYPOA_7Q0,21034
17
+ sunholo/agents/flask/vac_routes.py,sha256=U5JH1mj-3i2BbIVirnyUs_0s5oZ0tX1IWGkV_g7WW5k,22538
18
18
  sunholo/archive/__init__.py,sha256=qNHWm5rGPVOlxZBZCpA1wTYPbalizRT7f8X4rs2t290,31
19
19
  sunholo/archive/archive.py,sha256=PxVfDtO2_2ZEEbnhXSCbXLdeoHoQVImo4y3Jr2XkCFY,1204
20
20
  sunholo/auth/__init__.py,sha256=TeP-OY0XGxYV_8AQcVGoh35bvyWhNUcMRfhuD5l44Sk,91
@@ -94,7 +94,7 @@ sunholo/invoke/direct_vac_func.py,sha256=fuTJlH5PsqWhN_yVMaWisHCTZU1JEUz8I8yVbWs
94
94
  sunholo/invoke/invoke_vac_utils.py,sha256=sJc1edHTHMzMGXjji1N67c3iUaP7BmAL5nj82Qof63M,2053
95
95
  sunholo/langfuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
96
  sunholo/langfuse/callback.py,sha256=jl0SZsFS53uMW9DGeM9SOL_EsRZsba0wwFGLqKzu9_U,1684
97
- sunholo/langfuse/evals.py,sha256=wtaiv3WftElmg5_6U1eEGbjXXWxypG7D4MbJR_RHMCk,2541
97
+ sunholo/langfuse/evals.py,sha256=aGrU2DOR2hmI0ST613gBIT0v6IhEF1MIK1aOpuD2yR0,2909
98
98
  sunholo/langfuse/prompts.py,sha256=27BsVfihM6-h1jscbkGSO4HsATl-d4ZN6tcNCVztWoY,1300
99
99
  sunholo/llamaindex/__init__.py,sha256=DlY_cHWCsVEV1C5WBgDdHRgOMlJc8pDoCRukUJ8PT9w,88
100
100
  sunholo/llamaindex/get_files.py,sha256=6rhXCDqQ_lrIapISQ_OYQDjiSATXvS_9m3qq53-oIl0,781
@@ -144,9 +144,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
144
144
  sunholo/vertex/memory_tools.py,sha256=q_phxgGX2TG2j2MXNULF2xGzQnQPENwjPN9nZ_A9Gh0,7526
145
145
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
146
146
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
147
- sunholo-0.89.1.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
148
- sunholo-0.89.1.dist-info/METADATA,sha256=NwC2gl87pDNb54mwk-5c-5g6X4ip9NN9q-Ca6yZXxZA,7706
149
- sunholo-0.89.1.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
150
- sunholo-0.89.1.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
151
- sunholo-0.89.1.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
152
- sunholo-0.89.1.dist-info/RECORD,,
147
+ sunholo-0.89.4.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
148
+ sunholo-0.89.4.dist-info/METADATA,sha256=vp8lN-BGsMZzN8sUmHdoQuctkysZB_Iv52vLacNaAZg,7706
149
+ sunholo-0.89.4.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
150
+ sunholo-0.89.4.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
151
+ sunholo-0.89.4.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
152
+ sunholo-0.89.4.dist-info/RECORD,,