jaf-py 2.6.0__py3-none-any.whl → 2.6.2__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/__init__.py CHANGED
@@ -201,7 +201,7 @@ def generate_run_id() -> RunId:
201
201
  return create_run_id(str(uuid.uuid4()))
202
202
 
203
203
 
204
- __version__ = "2.6.0"
204
+ __version__ = "2.6.2"
205
205
  __all__ = [
206
206
  # Core types and functions
207
207
  "TraceId",
jaf/core/engine.py CHANGED
@@ -395,6 +395,17 @@ async def _store_conversation_history(state: RunState[Ctx], config: RunConfig[Ct
395
395
  )
396
396
 
397
397
  messages_to_store = list(state.messages)
398
+
399
+ if config.before_memory_store:
400
+ if asyncio.iscoroutinefunction(config.before_memory_store):
401
+ messages_to_store = await config.before_memory_store(messages_to_store, state)
402
+ else:
403
+ result = config.before_memory_store(messages_to_store, state)
404
+ if asyncio.iscoroutine(result):
405
+ messages_to_store = await result
406
+ else:
407
+ messages_to_store = result
408
+
398
409
  if (
399
410
  config.memory.compression_threshold
400
411
  and len(messages_to_store) > config.memory.compression_threshold
jaf/core/tracing.py CHANGED
@@ -407,6 +407,7 @@ class LangfuseTraceCollector:
407
407
  httpx_client: Optional[httpx.Client] = None,
408
408
  proxy: Optional[str] = None,
409
409
  timeout: Optional[int] = None,
410
+ include_system_prompt: bool = False,
410
411
  ):
411
412
  """Initialize Langfuse trace collector.
412
413
 
@@ -417,6 +418,7 @@ class LangfuseTraceCollector:
417
418
  Only used if httpx_client is not provided.
418
419
  Falls back to LANGFUSE_PROXY environment variable.
419
420
  timeout: Optional timeout in seconds for HTTP requests. Defaults to 10.
421
+ include_system_prompt: Whether to include system prompt in trace metadata. Defaults to False.
420
422
  """
421
423
  public_key = os.environ.get("LANGFUSE_PUBLIC_KEY")
422
424
  secret_key = os.environ.get("LANGFUSE_SECRET_KEY")
@@ -467,10 +469,11 @@ class LangfuseTraceCollector:
467
469
  public_key=public_key,
468
470
  secret_key=secret_key,
469
471
  host=host,
470
- release="jaf-py-v2.6.0",
472
+ release="jaf-py-v2.6.2",
471
473
  httpx_client=client,
472
474
  )
473
475
  self._httpx_client = client
476
+ self.include_system_prompt = include_system_prompt
474
477
 
475
478
  # Detect Langfuse version (v2 has trace() method, v3 does not)
476
479
  self._is_langfuse_v3 = not hasattr(self.langfuse, "trace")
@@ -741,6 +744,17 @@ class LangfuseTraceCollector:
741
744
  f"[LANGFUSE DEBUG] Added to conversation history: role={role}, content_length={len(str(content))}, has_tool_calls={bool(msg_data.get('tool_calls'))}"
742
745
  )
743
746
 
747
+ # Extract system prompt from context if enabled
748
+ system_prompt = None
749
+ if self.include_system_prompt and context:
750
+ if isinstance(context, dict):
751
+ system_prompt = context.get("system_prompt")
752
+ elif hasattr(context, "system_prompt"):
753
+ system_prompt = context.system_prompt
754
+
755
+ if system_prompt:
756
+ print(f"[LANGFUSE DEBUG] Extracted system_prompt: {system_prompt[:100] if isinstance(system_prompt, str) else system_prompt}...")
757
+
744
758
  print(
745
759
  f"[LANGFUSE DEBUG] Final extracted - user_query: {user_query}, user_id: {user_id}"
746
760
  )
@@ -778,6 +792,27 @@ class LangfuseTraceCollector:
778
792
  # Extract agent_name for tagging
779
793
  agent_name = self._get_event_data(event, "agent_name") or "analytics_agent_jaf"
780
794
 
795
+ # Build metadata with optional system_prompt
796
+ metadata = {
797
+ "framework": "jaf",
798
+ "event_type": "run_start",
799
+ "trace_id": str(trace_id),
800
+ "user_query": user_query,
801
+ "user_id": user_id or self._get_event_data(event, "user_id"),
802
+ "agent_name": agent_name,
803
+ "conversation_history": conversation_history,
804
+ "tool_calls": [],
805
+ "tool_results": [],
806
+ "user_info": self._get_event_data(event, "context").user_info
807
+ if self._get_event_data(event, "context")
808
+ and hasattr(self._get_event_data(event, "context"), "user_info")
809
+ else None,
810
+ }
811
+
812
+ # Add system_prompt to metadata if enabled and available
813
+ if self.include_system_prompt and system_prompt:
814
+ metadata["system_prompt"] = system_prompt
815
+
781
816
  # Use compatibility layer to create trace (works with both v2 and v3)
782
817
  trace = self._create_trace(
783
818
  trace_id=trace_id,
@@ -786,27 +821,15 @@ class LangfuseTraceCollector:
786
821
  session_id=self._get_event_data(event, "session_id"),
787
822
  input=trace_input,
788
823
  tags=[agent_name], # Add agent_name as a tag for dashboard filtering
789
- metadata={
790
- "framework": "jaf",
791
- "event_type": "run_start",
792
- "trace_id": str(trace_id),
793
- "user_query": user_query,
794
- "user_id": user_id or self._get_event_data(event, "user_id"),
795
- "agent_name": agent_name,
796
- "conversation_history": conversation_history,
797
- "tool_calls": [],
798
- "tool_results": [],
799
- "user_info": self._get_event_data(event, "context").user_info
800
- if self._get_event_data(event, "context")
801
- and hasattr(self._get_event_data(event, "context"), "user_info")
802
- else None,
803
- },
824
+ metadata=metadata,
804
825
  )
805
826
  self.trace_spans[trace_id] = trace
806
- # Store user_id, user_query, and conversation_history for later use
827
+ # Store user_id, user_query, conversation_history, and system_prompt for later use
807
828
  trace._user_id = user_id or self._get_event_data(event, "user_id")
808
829
  trace._user_query = user_query
809
830
  trace._conversation_history = conversation_history
831
+ if self.include_system_prompt:
832
+ trace._system_prompt = system_prompt
810
833
  print(
811
834
  f"[LANGFUSE] Created trace with user query: {user_query[:100] if user_query else 'None'}..."
812
835
  )
@@ -833,6 +856,12 @@ class LangfuseTraceCollector:
833
856
  "tool_results": self.trace_tool_results.get(trace_id, []),
834
857
  }
835
858
 
859
+ # Add system_prompt to final metadata if enabled and available
860
+ if self.include_system_prompt:
861
+ system_prompt = getattr(self.trace_spans[trace_id], "_system_prompt", None)
862
+ if system_prompt:
863
+ final_metadata["system_prompt"] = system_prompt
864
+
836
865
  # End the trace with updated metadata
837
866
  self.trace_spans[trace_id].update(output=event.data, metadata=final_metadata)
838
867
 
@@ -1198,6 +1227,7 @@ def create_composite_trace_collector(
1198
1227
  otel_session: Optional[Any] = None,
1199
1228
  proxy: Optional[str] = None,
1200
1229
  timeout: Optional[int] = None,
1230
+ include_system_prompt: bool = False,
1201
1231
  ) -> TraceCollector:
1202
1232
  """Create a composite trace collector that forwards events to multiple collectors.
1203
1233
 
@@ -1213,6 +1243,7 @@ def create_composite_trace_collector(
1213
1243
  If not set, both respect standard HTTP_PROXY/HTTPS_PROXY environment variables.
1214
1244
  timeout: Optional timeout in seconds for HTTP requests (applies to both Langfuse and OTEL).
1215
1245
  For Langfuse: Falls back to LANGFUSE_TIMEOUT environment variable (default: 10).
1246
+ include_system_prompt: Whether to include system prompt in Langfuse trace metadata. Defaults to False.
1216
1247
  """
1217
1248
  collector_list = list(collectors)
1218
1249
 
@@ -1229,7 +1260,7 @@ def create_composite_trace_collector(
1229
1260
  # Automatically add Langfuse collector if keys are configured
1230
1261
  if os.getenv("LANGFUSE_PUBLIC_KEY") and os.getenv("LANGFUSE_SECRET_KEY"):
1231
1262
  langfuse_collector = LangfuseTraceCollector(
1232
- httpx_client=httpx_client, proxy=proxy, timeout=timeout
1263
+ httpx_client=httpx_client, proxy=proxy, timeout=timeout, include_system_prompt=include_system_prompt
1233
1264
  )
1234
1265
  collector_list.append(langfuse_collector)
1235
1266
 
jaf/core/types.py CHANGED
@@ -1115,6 +1115,12 @@ class RunConfig(Generic[Ctx]):
1115
1115
  Union[ModelCompletionResponse, Awaitable[ModelCompletionResponse]],
1116
1116
  ]
1117
1117
  ] = None # Callback after LLM call - can process response
1118
+ before_memory_store: Optional[
1119
+ Callable[
1120
+ [List[Message], RunState[Ctx]],
1121
+ Union[List[Message], Awaitable[List[Message]]],
1122
+ ]
1123
+ ] = None
1118
1124
  max_empty_response_retries: int = 3 # Maximum retries when LLM returns empty response
1119
1125
  empty_response_retry_delay: float = (
1120
1126
  1.0 # Initial delay in seconds before retrying empty response (uses exponential backoff)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaf-py
3
- Version: 2.6.0
3
+ Version: 2.6.2
4
4
  Summary: A purely functional agent framework with immutable state and composable tools - Python implementation
5
5
  Author: JAF Contributors
6
6
  Maintainer: JAF Contributors
@@ -82,7 +82,7 @@ Dynamic: license-file
82
82
 
83
83
  <!-- ![JAF Banner](docs/cover.png) -->
84
84
 
85
- [![Version](https://img.shields.io/badge/version-2.6.0-blue.svg)](https://github.com/xynehq/jaf-py)
85
+ [![Version](https://img.shields.io/badge/version-2.6.2-blue.svg)](https://github.com/xynehq/jaf-py)
86
86
  [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
87
87
  [![Docs](https://img.shields.io/badge/Docs-Live-brightgreen)](https://xynehq.github.io/jaf-py/)
88
88
 
@@ -1,4 +1,4 @@
1
- jaf/__init__.py,sha256=x18d5MnT8t9HFbWVrUr9gL2c35eugxvx50kGNFGcU_Q,8652
1
+ jaf/__init__.py,sha256=Yc0wSawKKU2cvECCRJeJ_8mL6XwCPkbTHe74WmjVKRY,8652
2
2
  jaf/cli.py,sha256=EDMMA5uX0e3TUIedLdyP3p4Qy-aXADvpht3VgJPJagU,8299
3
3
  jaf/exceptions.py,sha256=FdLIw7bdCNtBYfqRyJBkRT4Z1vWuvkzrMqFiMAzjL8Y,9158
4
4
  jaf/a2a/__init__.py,sha256=r4W-WHZNjoxR8EQ0x41_rY3fl12OH5qcSn0KycXaKKU,7752
@@ -43,7 +43,7 @@ jaf/core/agent_tool.py,sha256=gZje8_gZSaWCecySg2ZBK07RcD8bc2hxHsR4z87oKJE,12075
43
43
  jaf/core/analytics.py,sha256=ypdhllyOThXZB-TY_eR1t1n2qrnAVN7Ljb8PaOtJft0,23267
44
44
  jaf/core/checkpoint.py,sha256=O7mfi7gFOAUgJ3zHzgJsr11uzn-BU-Vj1iKyKjcirMk,8398
45
45
  jaf/core/composition.py,sha256=Tj0-FRTVWygmAfsBLld7pnZK4nrGMMBx2YYJW_KQPoo,25393
46
- jaf/core/engine.py,sha256=tr1qHrBYLLmFLVuNNwqXb0g6EAuuqRlSw_VDb1DCF-k,69833
46
+ jaf/core/engine.py,sha256=1jY8gBeNy00LgUKolAQRfF33C2L_xZ0j5nyI5OTAPyk,70271
47
47
  jaf/core/errors.py,sha256=iDw00o3WH0gHcenRcTj3QEbbloZVpgwnPij6mtaJJk4,5710
48
48
  jaf/core/guardrails.py,sha256=oPB7MpD3xWiCWoyaS-xQQp-glaPON7GNVrIL0h1Jefs,26931
49
49
  jaf/core/handoff.py,sha256=M7TQfd7BXuer1ZeRJ51nLsI55KifbM6faNtmA2Nsj3I,6196
@@ -56,8 +56,8 @@ jaf/core/state.py,sha256=fdWDc2DQ-o_g_8E4ibg2QM0Vad_XUique3a5iYBwGZo,9516
56
56
  jaf/core/streaming.py,sha256=5ntOtJrZVCHuGsygquyCLG2J5yuSxE6DN5OM-BrQiGw,16818
57
57
  jaf/core/tool_results.py,sha256=L9U3JDQAjAH5YR7iMpSxfVky2Nxo6FYQs4WE05RATaQ,11283
58
58
  jaf/core/tools.py,sha256=rHxzAfGVGpYk3YJKmrq3AQLW0oE3ACkiJBOwle2bLdc,15146
59
- jaf/core/tracing.py,sha256=oXnaOjTnOC_zKuptL3t74bD7v0hn9UiKTyFmMXwMTXc,55603
60
- jaf/core/types.py,sha256=1QolTM3IYnQArbYAZkCIfj868j9vyEZTThqw37q7EyU,35395
59
+ jaf/core/tracing.py,sha256=-ZlIsfDRoFktiJgoY5R2d9lVjSASctKGjdUBWEuw-EE,57320
60
+ jaf/core/types.py,sha256=MwHSXSamOz3QDjTEaOQzNqOMU1JxwFbHg8Fd9Xzw33Y,35576
61
61
  jaf/core/workflows.py,sha256=0825AoD1QwEiGAs5IRlWHmaKrjurx6xF7oDJR6POBsg,25651
62
62
  jaf/memory/__init__.py,sha256=YfANOg5vUFSPVG7gpBE4_lYkV5X3_U6Yj9v1_QexfN0,1396
63
63
  jaf/memory/approval_storage.py,sha256=DcwtERcoIMH7B-abK9hqND3Moz4zSETsPlgJNkvqcaM,10573
@@ -89,9 +89,9 @@ jaf/visualization/functional_core.py,sha256=0Xs2R8ELADKNIgokcbjuxmWwxEyCH1yXIEdG
89
89
  jaf/visualization/graphviz.py,sha256=EwWVIRv8Z7gTiO5Spvcm-z_UUQ1oWNPRgdE33ZzFwx8,11569
90
90
  jaf/visualization/imperative_shell.py,sha256=N5lWzOLMIU_iCoy3n5WCg49eec8VxV8f7JIG6_wNtVw,2506
91
91
  jaf/visualization/types.py,sha256=90G8oClsFa_APqTuMrTW6KjD0oG9I4kVur773dXNW0E,1393
92
- jaf_py-2.6.0.dist-info/licenses/LICENSE,sha256=LXUQBJxdyr-7C4bk9cQBwvsF_xwA-UVstDTKabpcjlI,1063
93
- jaf_py-2.6.0.dist-info/METADATA,sha256=jHV0X-xKL9RpV0pU0ujHYsj_a7F6Kj-EkDqqxZrX7ac,27743
94
- jaf_py-2.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
95
- jaf_py-2.6.0.dist-info/entry_points.txt,sha256=OtIJeNJpb24kgGrqRx9szGgDx1vL9ayq8uHErmu7U5w,41
96
- jaf_py-2.6.0.dist-info/top_level.txt,sha256=Xu1RZbGaM4_yQX7bpalo881hg7N_dybaOW282F15ruE,4
97
- jaf_py-2.6.0.dist-info/RECORD,,
92
+ jaf_py-2.6.2.dist-info/licenses/LICENSE,sha256=LXUQBJxdyr-7C4bk9cQBwvsF_xwA-UVstDTKabpcjlI,1063
93
+ jaf_py-2.6.2.dist-info/METADATA,sha256=IHIsXU-V5KVPanz4Obos8xlgylgNlg2Q8vgvKToDg7Y,27743
94
+ jaf_py-2.6.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
95
+ jaf_py-2.6.2.dist-info/entry_points.txt,sha256=OtIJeNJpb24kgGrqRx9szGgDx1vL9ayq8uHErmu7U5w,41
96
+ jaf_py-2.6.2.dist-info/top_level.txt,sha256=Xu1RZbGaM4_yQX7bpalo881hg7N_dybaOW282F15ruE,4
97
+ jaf_py-2.6.2.dist-info/RECORD,,
File without changes