prefactor-langchain 0.2.2__tar.gz → 0.2.3__tar.gz

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.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prefactor-langchain
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: LangChain integration for Prefactor observability
5
5
  Author-email: Prefactor Pty Ltd <josh@prefactor.tech>
6
6
  License: MIT
7
7
  Requires-Python: <4.0.0,>=3.11.0
8
8
  Requires-Dist: langchain-core>=1.0.0
9
- Requires-Dist: prefactor-core>=0.2.1
9
+ Requires-Dist: prefactor-core>=0.2.2
10
10
  Description-Content-Type: text/markdown
11
11
 
12
12
  # prefactor-langchain
@@ -50,7 +50,7 @@ def _safe_eval(node: ast.expr) -> float:
50
50
  if isinstance(node, ast.Constant):
51
51
  return node.n # type: ignore[return-value]
52
52
  if isinstance(node, ast.BinOp):
53
- return _OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right))
53
+ return _OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right)) # type: ignore[invalid-argument-type]
54
54
  if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
55
55
  return -_safe_eval(node.operand)
56
56
  raise ValueError(f"Unsupported expression: {ast.dump(node)}")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "prefactor-langchain"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  description = "LangChain integration for Prefactor observability"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -9,7 +9,7 @@ authors = [
9
9
  ]
10
10
  requires-python = ">=3.11.0, <4.0.0"
11
11
  dependencies = [
12
- "prefactor-core>=0.2.1",
12
+ "prefactor-core>=0.2.2",
13
13
  "langchain-core>=1.0.0",
14
14
  ]
15
15
 
@@ -526,7 +526,15 @@ class PrefactorMiddleware(AgentMiddleware):
526
526
  if loop is None or loop.is_closed():
527
527
  return ""
528
528
 
529
- import asyncio
529
+ try:
530
+ running_loop = asyncio.get_running_loop()
531
+ except RuntimeError:
532
+ running_loop = None
533
+
534
+ if running_loop is not None and running_loop is loop:
535
+ raise RuntimeError(
536
+ "_create_agent_span_sync must be called from a non-event-loop thread"
537
+ )
530
538
 
531
539
  # Use whatever parent was set by set_parent_span() from the async side.
532
540
  # We cannot read the SpanContextStack here because run_in_executor does
@@ -664,6 +672,17 @@ class PrefactorMiddleware(AgentMiddleware):
664
672
  async def _finish() -> None:
665
673
  await instance.finish_span(span_id, result_payload=result)
666
674
 
675
+ try:
676
+ running_loop = asyncio.get_running_loop()
677
+ except RuntimeError:
678
+ running_loop = None
679
+
680
+ if running_loop is not None and running_loop is loop:
681
+ raise RuntimeError(
682
+ "after_agent must be called from a non-event-loop thread; "
683
+ "use aafter_agent for async execution"
684
+ )
685
+
667
686
  future = asyncio.run_coroutine_threadsafe(_finish(), loop)
668
687
  future.result(timeout=5.0)
669
688
 
@@ -718,8 +737,16 @@ class PrefactorMiddleware(AgentMiddleware):
718
737
  # SpanContextStack.peek() here returns the outer workflow span
719
738
  # because we are executing in the async event loop context.
720
739
  self._agent_span_cm = instance.span("langchain:agent")
721
- self._agent_span_context = await self._agent_span_cm.__aenter__()
722
- await self._agent_span_context.start(params)
740
+ try:
741
+ self._agent_span_context = await self._agent_span_cm.__aenter__()
742
+ await self._agent_span_context.start(params)
743
+ except BaseException:
744
+ import sys
745
+
746
+ await self._agent_span_cm.__aexit__(*sys.exc_info())
747
+ self._agent_span_cm = None
748
+ self._agent_span_context = None
749
+ raise
723
750
 
724
751
  self._agent_span_id = self._agent_span_context.id
725
752
  logger.debug("Created agent span %s (async)", self._agent_span_id)