jaf-py 2.2.4__py3-none-any.whl → 2.3.0__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.
- jaf/core/engine.py +6 -2
- jaf/core/tracing.py +187 -37
- jaf/core/types.py +4 -0
- {jaf_py-2.2.4.dist-info → jaf_py-2.3.0.dist-info}/METADATA +1 -1
- {jaf_py-2.2.4.dist-info → jaf_py-2.3.0.dist-info}/RECORD +9 -9
- {jaf_py-2.2.4.dist-info → jaf_py-2.3.0.dist-info}/WHEEL +0 -0
- {jaf_py-2.2.4.dist-info → jaf_py-2.3.0.dist-info}/entry_points.txt +0 -0
- {jaf_py-2.2.4.dist-info → jaf_py-2.3.0.dist-info}/licenses/LICENSE +0 -0
- {jaf_py-2.2.4.dist-info → jaf_py-2.3.0.dist-info}/top_level.txt +0 -0
jaf/core/engine.py
CHANGED
|
@@ -94,7 +94,9 @@ async def run(
|
|
|
94
94
|
config.on_event(RunStartEvent(data=to_event_data(RunStartEventData(
|
|
95
95
|
run_id=initial_state.run_id,
|
|
96
96
|
trace_id=initial_state.trace_id,
|
|
97
|
-
session_id=config.conversation_id
|
|
97
|
+
session_id=config.conversation_id,
|
|
98
|
+
context=initial_state.context,
|
|
99
|
+
messages=initial_state.messages
|
|
98
100
|
))))
|
|
99
101
|
|
|
100
102
|
state_with_memory = await _load_conversation_history(initial_state, config)
|
|
@@ -293,7 +295,9 @@ async def _run_internal(
|
|
|
293
295
|
agent_name=current_agent.name,
|
|
294
296
|
model=model,
|
|
295
297
|
trace_id=state.trace_id,
|
|
296
|
-
run_id=state.run_id
|
|
298
|
+
run_id=state.run_id,
|
|
299
|
+
context=state.context,
|
|
300
|
+
messages=state.messages
|
|
297
301
|
))))
|
|
298
302
|
|
|
299
303
|
# Get completion from model provider
|
jaf/core/tracing.py
CHANGED
|
@@ -358,15 +358,99 @@ class LangfuseTraceCollector:
|
|
|
358
358
|
if event.type == "run_start":
|
|
359
359
|
# Start a new trace for the entire run
|
|
360
360
|
print(f"[LANGFUSE] Starting trace for run: {trace_id}")
|
|
361
|
+
|
|
362
|
+
# Extract user query from the run_start data
|
|
363
|
+
user_query = None
|
|
364
|
+
user_id = None
|
|
365
|
+
|
|
366
|
+
# Debug: Print the event data structure to understand what we're working with
|
|
367
|
+
print(f"[LANGFUSE DEBUG] Event data keys: {list(event.data.keys()) if event.data else 'No data'}")
|
|
368
|
+
if event.data.get("context"):
|
|
369
|
+
context = event.data["context"]
|
|
370
|
+
print(f"[LANGFUSE DEBUG] Context type: {type(context)}")
|
|
371
|
+
print(f"[LANGFUSE DEBUG] Context attributes: {dir(context) if hasattr(context, '__dict__') else 'Not an object'}")
|
|
372
|
+
if hasattr(context, '__dict__'):
|
|
373
|
+
print(f"[LANGFUSE DEBUG] Context dict: {context.__dict__}")
|
|
374
|
+
|
|
375
|
+
# Try to extract from context first
|
|
376
|
+
context = event.data.get("context")
|
|
377
|
+
if context:
|
|
378
|
+
# Try direct attribute access
|
|
379
|
+
if hasattr(context, 'query'):
|
|
380
|
+
user_query = context.query
|
|
381
|
+
print(f"[LANGFUSE DEBUG] Found user_query from context.query: {user_query}")
|
|
382
|
+
|
|
383
|
+
# Try to extract from combined_history
|
|
384
|
+
if hasattr(context, 'combined_history') and context.combined_history:
|
|
385
|
+
history = context.combined_history
|
|
386
|
+
print(f"[LANGFUSE DEBUG] Found combined_history with {len(history)} messages")
|
|
387
|
+
for i, msg in enumerate(reversed(history)):
|
|
388
|
+
print(f"[LANGFUSE DEBUG] History message {i}: {msg}")
|
|
389
|
+
if isinstance(msg, dict) and msg.get("role") == "user":
|
|
390
|
+
user_query = msg.get("content", "")
|
|
391
|
+
print(f"[LANGFUSE DEBUG] Found user_query from history: {user_query}")
|
|
392
|
+
break
|
|
393
|
+
|
|
394
|
+
# Try to extract user_id from token_response
|
|
395
|
+
if hasattr(context, 'token_response'):
|
|
396
|
+
token_response = context.token_response
|
|
397
|
+
print(f"[LANGFUSE DEBUG] Found token_response: {type(token_response)}")
|
|
398
|
+
if isinstance(token_response, dict):
|
|
399
|
+
user_id = token_response.get("email") or token_response.get("username")
|
|
400
|
+
print(f"[LANGFUSE DEBUG] Extracted user_id: {user_id}")
|
|
401
|
+
elif hasattr(token_response, 'email'):
|
|
402
|
+
user_id = token_response.email
|
|
403
|
+
print(f"[LANGFUSE DEBUG] Extracted user_id from attr: {user_id}")
|
|
404
|
+
|
|
405
|
+
# Fallback: try to extract from messages if context didn't work
|
|
406
|
+
if not user_query and event.data.get("messages"):
|
|
407
|
+
print(f"[LANGFUSE DEBUG] Trying fallback from messages")
|
|
408
|
+
messages = event.data["messages"]
|
|
409
|
+
print(f"[LANGFUSE DEBUG] Found {len(messages)} messages")
|
|
410
|
+
# Find the last user message which should be the current query
|
|
411
|
+
for i, msg in enumerate(reversed(messages)):
|
|
412
|
+
print(f"[LANGFUSE DEBUG] Message {i}: {msg}")
|
|
413
|
+
if isinstance(msg, dict) and msg.get("role") == "user":
|
|
414
|
+
user_query = msg.get("content", "")
|
|
415
|
+
print(f"[LANGFUSE DEBUG] Found user_query from messages: {user_query}")
|
|
416
|
+
break
|
|
417
|
+
elif hasattr(msg, 'role') and msg.role == 'user':
|
|
418
|
+
user_query = msg.content
|
|
419
|
+
print(f"[LANGFUSE DEBUG] Found user_query from message attr: {user_query}")
|
|
420
|
+
break
|
|
421
|
+
|
|
422
|
+
print(f"[LANGFUSE DEBUG] Final extracted - user_query: {user_query}, user_id: {user_id}")
|
|
423
|
+
|
|
424
|
+
# Create comprehensive input data for the trace
|
|
425
|
+
trace_input = {
|
|
426
|
+
"user_query": user_query,
|
|
427
|
+
"run_id": str(trace_id),
|
|
428
|
+
"agent_name": event.data.get("agent_name", "analytics_agent_jaf"),
|
|
429
|
+
"session_info": {
|
|
430
|
+
"session_id": event.data.get("session_id"),
|
|
431
|
+
"user_id": user_id or event.data.get("user_id")
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
361
435
|
trace = self.langfuse.trace(
|
|
362
436
|
name=f"jaf-run-{trace_id}",
|
|
363
|
-
user_id=event.data.get("user_id"),
|
|
437
|
+
user_id=user_id or event.data.get("user_id"),
|
|
364
438
|
session_id=event.data.get("session_id"),
|
|
365
|
-
input=
|
|
366
|
-
metadata={
|
|
439
|
+
input=trace_input,
|
|
440
|
+
metadata={
|
|
441
|
+
"framework": "jaf",
|
|
442
|
+
"event_type": "run_start",
|
|
443
|
+
"trace_id": str(trace_id),
|
|
444
|
+
"user_query": user_query,
|
|
445
|
+
"user_id": user_id or event.data.get("user_id"),
|
|
446
|
+
"agent_name": event.data.get("agent_name", "analytics_agent_jaf")
|
|
447
|
+
}
|
|
367
448
|
)
|
|
368
449
|
self.trace_spans[trace_id] = trace
|
|
369
|
-
|
|
450
|
+
# Store user_id and user_query for later use in generations
|
|
451
|
+
trace._user_id = user_id or event.data.get("user_id")
|
|
452
|
+
trace._user_query = user_query
|
|
453
|
+
print(f"[LANGFUSE] Created trace with user query: {user_query[:100] if user_query else 'None'}...")
|
|
370
454
|
|
|
371
455
|
elif event.type == "run_end":
|
|
372
456
|
if trace_id in self.trace_spans:
|
|
@@ -386,10 +470,21 @@ class LangfuseTraceCollector:
|
|
|
386
470
|
# Start a generation for LLM calls
|
|
387
471
|
model = event.data.get("model", "unknown")
|
|
388
472
|
print(f"[LANGFUSE] Starting generation for LLM call with model: {model}")
|
|
389
|
-
|
|
473
|
+
|
|
474
|
+
# Get stored user information from the trace
|
|
475
|
+
trace = self.trace_spans[trace_id]
|
|
476
|
+
user_id = getattr(trace, '_user_id', None)
|
|
477
|
+
user_query = getattr(trace, '_user_query', None)
|
|
478
|
+
|
|
479
|
+
generation = trace.generation(
|
|
390
480
|
name=f"llm-call-{model}",
|
|
391
481
|
input=event.data.get("messages"),
|
|
392
|
-
metadata={
|
|
482
|
+
metadata={
|
|
483
|
+
"agent_name": event.data.get("agent_name"),
|
|
484
|
+
"model": model,
|
|
485
|
+
"user_id": user_id,
|
|
486
|
+
"user_query": user_query
|
|
487
|
+
}
|
|
393
488
|
)
|
|
394
489
|
span_id = self._get_span_id(event)
|
|
395
490
|
self.active_spans[span_id] = generation
|
|
@@ -405,47 +500,111 @@ class LangfuseTraceCollector:
|
|
|
405
500
|
|
|
406
501
|
# Extract usage from the event data
|
|
407
502
|
usage = event.data.get("usage", {})
|
|
408
|
-
|
|
409
|
-
#
|
|
503
|
+
|
|
504
|
+
# Extract model information from choice data or event data
|
|
505
|
+
model = choice.get("model", "unknown")
|
|
506
|
+
if model == "unknown":
|
|
507
|
+
# Try to get model from the choice response structure
|
|
508
|
+
if isinstance(choice, dict):
|
|
509
|
+
model = choice.get("model") or choice.get("id", "unknown")
|
|
510
|
+
|
|
511
|
+
# Convert to Langfuse v2 format - let Langfuse handle cost calculation automatically
|
|
410
512
|
langfuse_usage = None
|
|
411
513
|
if usage:
|
|
514
|
+
prompt_tokens = usage.get("prompt_tokens", 0)
|
|
515
|
+
completion_tokens = usage.get("completion_tokens", 0)
|
|
516
|
+
total_tokens = usage.get("total_tokens", 0)
|
|
517
|
+
|
|
412
518
|
langfuse_usage = {
|
|
413
|
-
"input":
|
|
414
|
-
"output":
|
|
415
|
-
"total":
|
|
519
|
+
"input": prompt_tokens,
|
|
520
|
+
"output": completion_tokens,
|
|
521
|
+
"total": total_tokens,
|
|
416
522
|
"unit": "TOKENS"
|
|
417
523
|
}
|
|
418
|
-
|
|
419
|
-
|
|
524
|
+
|
|
525
|
+
print(f"[LANGFUSE] Usage data for automatic cost calculation: {langfuse_usage}")
|
|
526
|
+
|
|
527
|
+
# Include model information in the generation end - Langfuse will calculate costs automatically
|
|
528
|
+
generation.end(
|
|
529
|
+
output=choice,
|
|
530
|
+
usage=langfuse_usage,
|
|
531
|
+
model=model, # Pass model directly for automatic cost calculation
|
|
532
|
+
metadata={
|
|
533
|
+
"model": model,
|
|
534
|
+
"system_fingerprint": choice.get("system_fingerprint"),
|
|
535
|
+
"created": choice.get("created"),
|
|
536
|
+
"response_id": choice.get("id")
|
|
537
|
+
}
|
|
538
|
+
)
|
|
420
539
|
|
|
421
540
|
# Clean up the span reference
|
|
422
541
|
del self.active_spans[span_id]
|
|
423
|
-
print(f"[LANGFUSE] Generation ended")
|
|
542
|
+
print(f"[LANGFUSE] Generation ended with cost tracking")
|
|
424
543
|
else:
|
|
425
544
|
print(f"[LANGFUSE] No generation found for llm_call_end: {span_id}")
|
|
426
545
|
|
|
427
546
|
elif event.type == "tool_call_start":
|
|
428
|
-
# Start a span for tool calls
|
|
429
|
-
|
|
547
|
+
# Start a span for tool calls with detailed input information
|
|
548
|
+
tool_name = event.data.get('tool_name', 'unknown')
|
|
549
|
+
tool_args = event.data.get("args", {})
|
|
550
|
+
|
|
551
|
+
print(f"[LANGFUSE] Starting span for tool call: {tool_name}")
|
|
552
|
+
|
|
553
|
+
# Create comprehensive input data for the tool call
|
|
554
|
+
tool_input = {
|
|
555
|
+
"tool_name": tool_name,
|
|
556
|
+
"arguments": tool_args,
|
|
557
|
+
"call_id": event.data.get("call_id"),
|
|
558
|
+
"timestamp": datetime.now().isoformat()
|
|
559
|
+
}
|
|
560
|
+
|
|
430
561
|
span = self.trace_spans[trace_id].span(
|
|
431
|
-
name=f"tool-{
|
|
432
|
-
input=
|
|
433
|
-
metadata={
|
|
562
|
+
name=f"tool-{tool_name}",
|
|
563
|
+
input=tool_input,
|
|
564
|
+
metadata={
|
|
565
|
+
"tool_name": tool_name,
|
|
566
|
+
"call_id": event.data.get("call_id"),
|
|
567
|
+
"framework": "jaf",
|
|
568
|
+
"event_type": "tool_call"
|
|
569
|
+
}
|
|
434
570
|
)
|
|
435
571
|
span_id = self._get_span_id(event)
|
|
436
572
|
self.active_spans[span_id] = span
|
|
437
|
-
print(f"[LANGFUSE] Created tool span: {
|
|
573
|
+
print(f"[LANGFUSE] Created tool span for {tool_name} with args: {str(tool_args)[:100]}...")
|
|
438
574
|
|
|
439
575
|
elif event.type == "tool_call_end":
|
|
440
576
|
span_id = self._get_span_id(event)
|
|
441
577
|
if span_id in self.active_spans:
|
|
442
|
-
|
|
443
|
-
|
|
578
|
+
tool_name = event.data.get('tool_name', 'unknown')
|
|
579
|
+
tool_result = event.data.get("result")
|
|
580
|
+
|
|
581
|
+
print(f"[LANGFUSE] Ending span for tool call: {tool_name}")
|
|
582
|
+
|
|
583
|
+
# Create comprehensive output data for the tool call
|
|
584
|
+
tool_output = {
|
|
585
|
+
"tool_name": tool_name,
|
|
586
|
+
"result": tool_result,
|
|
587
|
+
"call_id": event.data.get("call_id"),
|
|
588
|
+
"timestamp": datetime.now().isoformat(),
|
|
589
|
+
"status": "completed"
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
# End the span with detailed output
|
|
444
593
|
span = self.active_spans[span_id]
|
|
445
|
-
span.end(
|
|
594
|
+
span.end(
|
|
595
|
+
output=tool_output,
|
|
596
|
+
metadata={
|
|
597
|
+
"tool_name": tool_name,
|
|
598
|
+
"call_id": event.data.get("call_id"),
|
|
599
|
+
"result_length": len(str(tool_result)) if tool_result else 0,
|
|
600
|
+
"framework": "jaf",
|
|
601
|
+
"event_type": "tool_call_end"
|
|
602
|
+
}
|
|
603
|
+
)
|
|
604
|
+
|
|
446
605
|
# Clean up the span reference
|
|
447
606
|
del self.active_spans[span_id]
|
|
448
|
-
print(f"[LANGFUSE] Tool span ended")
|
|
607
|
+
print(f"[LANGFUSE] Tool span ended for {tool_name} with result length: {len(str(tool_result)) if tool_result else 0}")
|
|
449
608
|
else:
|
|
450
609
|
print(f"[LANGFUSE] No tool span found for tool_call_end: {span_id}")
|
|
451
610
|
|
|
@@ -502,19 +661,10 @@ class LangfuseTraceCollector:
|
|
|
502
661
|
tool_name = event.data.get('tool_name') or event.data.get('toolName', 'unknown')
|
|
503
662
|
return f"tool-{tool_name}-{trace_id}"
|
|
504
663
|
elif event.type.startswith('llm_call'):
|
|
505
|
-
# For LLM calls,
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if not model and event.type == 'llm_call_end':
|
|
510
|
-
choice = event.data.get('choice', {})
|
|
511
|
-
if isinstance(choice, dict):
|
|
512
|
-
model = choice.get('model')
|
|
513
|
-
|
|
514
|
-
# Handle case where model might be empty or None
|
|
515
|
-
if not model or model == '':
|
|
516
|
-
model = 'unknown'
|
|
517
|
-
return f"llm-{model}-{trace_id}"
|
|
664
|
+
# For LLM calls, use a simpler consistent ID that matches between start and end
|
|
665
|
+
# Get run_id for more consistent matching
|
|
666
|
+
run_id = event.data.get('run_id') or event.data.get('runId', trace_id)
|
|
667
|
+
return f"llm-{run_id}"
|
|
518
668
|
else:
|
|
519
669
|
return f"{event.type}-{trace_id}"
|
|
520
670
|
|
jaf/core/types.py
CHANGED
|
@@ -379,6 +379,8 @@ class RunStartEventData:
|
|
|
379
379
|
run_id: RunId
|
|
380
380
|
trace_id: TraceId
|
|
381
381
|
session_id: Optional[str] = None
|
|
382
|
+
context: Optional[Any] = None
|
|
383
|
+
messages: Optional[List[Message]] = None
|
|
382
384
|
|
|
383
385
|
@dataclass(frozen=True)
|
|
384
386
|
class RunStartEvent:
|
|
@@ -392,6 +394,8 @@ class LLMCallStartEventData:
|
|
|
392
394
|
model: str
|
|
393
395
|
trace_id: TraceId
|
|
394
396
|
run_id: RunId
|
|
397
|
+
context: Optional[Any] = None
|
|
398
|
+
messages: Optional[List[Message]] = None
|
|
395
399
|
|
|
396
400
|
@dataclass(frozen=True)
|
|
397
401
|
class LLMCallStartEvent:
|
|
@@ -42,7 +42,7 @@ jaf/core/__init__.py,sha256=rBvP_7TGbJICDJnA7a3qyX8yQErCDWaGAn5WzpyH4gU,1339
|
|
|
42
42
|
jaf/core/agent_tool.py,sha256=8TcBuSxGmDTW5F_GhBU_m5S43nYqkjO4qTrNERraAig,11656
|
|
43
43
|
jaf/core/analytics.py,sha256=NrUfOLLTDIhOzdfc65ZqS9AJ4ZAP9BtNtga69q0YdYw,23265
|
|
44
44
|
jaf/core/composition.py,sha256=IVxRO1Q9nK7JRH32qQ4p8WMIUu66BhqPNrlTNMGFVwE,26317
|
|
45
|
-
jaf/core/engine.py,sha256=
|
|
45
|
+
jaf/core/engine.py,sha256=v6yXp-zJF9Cj_9qlaW1rmCz7_N3YuDU02xxlzl-howM,27122
|
|
46
46
|
jaf/core/errors.py,sha256=5fwTNhkojKRQ4wZj3lZlgDnAsrYyjYOwXJkIr5EGNUc,5539
|
|
47
47
|
jaf/core/performance.py,sha256=jedQmTEkrKMD6_Aw1h8PdG-5TsdYSFFT7Or6k5dmN2g,9974
|
|
48
48
|
jaf/core/proxy.py,sha256=_WM3cpRlSQLYpgSBrnY30UPMe2iZtlqDQ65kppE-WY0,4609
|
|
@@ -50,8 +50,8 @@ jaf/core/proxy_helpers.py,sha256=i7a5fAX9rLmO4FMBX51-yRkTFwfWedzQNgnLmeLUd_A,437
|
|
|
50
50
|
jaf/core/streaming.py,sha256=c5o9iqpjoYV2LrUpG6qLWCYrWcP-DCcZsvMbyqKunp8,16089
|
|
51
51
|
jaf/core/tool_results.py,sha256=-bTOqOX02lMyslp5Z4Dmuhx0cLd5o7kgR88qK2HO_sw,11323
|
|
52
52
|
jaf/core/tools.py,sha256=SbJRRr4y_xxNYNTulZg6OiyNaHBlo_qXWYY510jxQEs,16489
|
|
53
|
-
jaf/core/tracing.py,sha256=
|
|
54
|
-
jaf/core/types.py,sha256=
|
|
53
|
+
jaf/core/tracing.py,sha256=1g5n-jRkSVSuQNONIgip0j3pHOfOnu2CBzoYhiknGo8,31902
|
|
54
|
+
jaf/core/types.py,sha256=9UXrPkepw7opgv1VGbPAC1Zx80RP4-ouRxtb6kVTA-A,17063
|
|
55
55
|
jaf/core/workflows.py,sha256=Ul-82gzjIXtkhnSMSPv-8igikjkMtW1EBo9yrfodtvI,26294
|
|
56
56
|
jaf/memory/__init__.py,sha256=-L98xlvihurGAzF0DnXtkueDVvO_wV2XxxEwAWdAj50,1400
|
|
57
57
|
jaf/memory/factory.py,sha256=Fh6JyvQtCKe38DZV5-NnC9vPRCvzBgSSPFIGaX7Nt5E,2958
|
|
@@ -79,9 +79,9 @@ jaf/visualization/functional_core.py,sha256=zedMDZbvjuOugWwnh6SJ2stvRNQX1Hlkb9Ab
|
|
|
79
79
|
jaf/visualization/graphviz.py,sha256=WTOM6UP72-lVKwI4_SAr5-GCC3ouckxHv88ypCDQWJ0,12056
|
|
80
80
|
jaf/visualization/imperative_shell.py,sha256=GpMrAlMnLo2IQgyB2nardCz09vMvAzaYI46MyrvJ0i4,2593
|
|
81
81
|
jaf/visualization/types.py,sha256=QQcbVeQJLuAOXk8ynd08DXIS-PVCnv3R-XVE9iAcglw,1389
|
|
82
|
-
jaf_py-2.
|
|
83
|
-
jaf_py-2.
|
|
84
|
-
jaf_py-2.
|
|
85
|
-
jaf_py-2.
|
|
86
|
-
jaf_py-2.
|
|
87
|
-
jaf_py-2.
|
|
82
|
+
jaf_py-2.3.0.dist-info/licenses/LICENSE,sha256=LXUQBJxdyr-7C4bk9cQBwvsF_xwA-UVstDTKabpcjlI,1063
|
|
83
|
+
jaf_py-2.3.0.dist-info/METADATA,sha256=_MyYA624bGLW5kwGHSXg4jNrbD_PJEd7UGwr2_JTkdA,27613
|
|
84
|
+
jaf_py-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
85
|
+
jaf_py-2.3.0.dist-info/entry_points.txt,sha256=OtIJeNJpb24kgGrqRx9szGgDx1vL9ayq8uHErmu7U5w,41
|
|
86
|
+
jaf_py-2.3.0.dist-info/top_level.txt,sha256=Xu1RZbGaM4_yQX7bpalo881hg7N_dybaOW282F15ruE,4
|
|
87
|
+
jaf_py-2.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|