crewplus 0.2.43__tar.gz → 0.2.46__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.
Potentially problematic release.
This version of crewplus might be problematic. Click here for more details.
- {crewplus-0.2.43 → crewplus-0.2.46}/PKG-INFO +1 -1
- crewplus-0.2.46/crewplus/callbacks/async_langfuse_handler.py +162 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/pyproject.toml +1 -1
- crewplus-0.2.43/crewplus/callbacks/async_langfuse_handler.py +0 -107
- {crewplus-0.2.43 → crewplus-0.2.46}/LICENSE +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/README.md +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/__init__.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/callbacks/__init__.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/services/__init__.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/services/azure_chat_model.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/services/gemini_chat_model.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/services/init_services.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/services/model_load_balancer.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/services/tracing_manager.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/utils/__init__.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/utils/schema_action.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/utils/schema_document_updater.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/vectorstores/milvus/__init__.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/vectorstores/milvus/milvus_schema_manager.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/vectorstores/milvus/schema_milvus.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/crewplus/vectorstores/milvus/vdb_service.py +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/docs/GeminiChatModel.md +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/docs/ModelLoadBalancer.md +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/docs/VDBService.md +0 -0
- {crewplus-0.2.43 → crewplus-0.2.46}/docs/index.md +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# File: crewplus/callbacks/async_langfuse_handler.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import contextvars
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Any, Dict, List, Union, Optional, Sequence
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from langfuse.langchain import CallbackHandler as LangfuseCallbackHandler
|
|
10
|
+
from langchain_core.callbacks import AsyncCallbackHandler
|
|
11
|
+
from langchain_core.outputs import LLMResult, ChatGeneration
|
|
12
|
+
from langchain_core.messages import BaseMessage
|
|
13
|
+
from langchain.schema.agent import AgentAction, AgentFinish
|
|
14
|
+
from langchain.schema.document import Document
|
|
15
|
+
LANGFUSE_AVAILABLE = True
|
|
16
|
+
except ImportError:
|
|
17
|
+
LANGFUSE_AVAILABLE = False
|
|
18
|
+
LangfuseCallbackHandler = None
|
|
19
|
+
AsyncCallbackHandler = object
|
|
20
|
+
# Define dummy types if langchain is not available
|
|
21
|
+
LLMResult = object
|
|
22
|
+
BaseMessage = object
|
|
23
|
+
AgentAction = object
|
|
24
|
+
AgentFinish = object
|
|
25
|
+
Document = object
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_ASYNC_CONTEXT_TOKEN = "in_async_context"
|
|
29
|
+
in_async_context = contextvars.ContextVar(_ASYNC_CONTEXT_TOKEN, default=False)
|
|
30
|
+
|
|
31
|
+
@contextmanager
|
|
32
|
+
def async_context():
|
|
33
|
+
"""A context manager to signal that we are in an async execution context."""
|
|
34
|
+
token = in_async_context.set(True)
|
|
35
|
+
try:
|
|
36
|
+
yield
|
|
37
|
+
finally:
|
|
38
|
+
in_async_context.reset(token)
|
|
39
|
+
|
|
40
|
+
class AsyncLangfuseCallbackHandler(AsyncCallbackHandler):
|
|
41
|
+
"""
|
|
42
|
+
Wraps the synchronous LangfuseCallbackHandler to make it fully compatible with
|
|
43
|
+
LangChain's async methods by handling all relevant events.
|
|
44
|
+
"""
|
|
45
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
|
46
|
+
if not LANGFUSE_AVAILABLE:
|
|
47
|
+
raise ImportError("Langfuse is not available. Please install it with 'pip install langfuse'")
|
|
48
|
+
self.sync_handler = LangfuseCallbackHandler(*args, **kwargs)
|
|
49
|
+
|
|
50
|
+
def __getattr__(self, name: str) -> Any:
|
|
51
|
+
return getattr(self.sync_handler, name)
|
|
52
|
+
|
|
53
|
+
# LLM Events
|
|
54
|
+
async def on_llm_start(
|
|
55
|
+
self, serialized: Dict[str, Any], prompts: List[str], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
56
|
+
) -> None:
|
|
57
|
+
corrected_prompts = prompts if isinstance(prompts, list) else [prompts]
|
|
58
|
+
await asyncio.to_thread(
|
|
59
|
+
self.sync_handler.on_llm_start, serialized, corrected_prompts, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
async def on_chat_model_start(
|
|
63
|
+
self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
64
|
+
) -> Any:
|
|
65
|
+
await asyncio.to_thread(
|
|
66
|
+
self.sync_handler.on_chat_model_start, serialized, messages, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
async def on_llm_end(
|
|
70
|
+
self, response: LLMResult, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
71
|
+
) -> None:
|
|
72
|
+
await asyncio.to_thread(
|
|
73
|
+
self.sync_handler.on_llm_end, response, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async def on_llm_error(
|
|
77
|
+
self, error: Union[Exception, KeyboardInterrupt], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
78
|
+
) -> None:
|
|
79
|
+
await asyncio.to_thread(
|
|
80
|
+
self.sync_handler.on_llm_error, error, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Chain Events
|
|
84
|
+
async def on_chain_start(
|
|
85
|
+
self, serialized: Dict[str, Any], inputs: Dict[str, Any], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
86
|
+
) -> Any:
|
|
87
|
+
await asyncio.to_thread(
|
|
88
|
+
self.sync_handler.on_chain_start, serialized, inputs, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
async def on_chain_end(
|
|
92
|
+
self, outputs: Dict[str, Any], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
93
|
+
) -> Any:
|
|
94
|
+
await asyncio.to_thread(
|
|
95
|
+
self.sync_handler.on_chain_end, outputs, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
async def on_chain_error(
|
|
99
|
+
self, error: Union[Exception, KeyboardInterrupt], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
100
|
+
) -> Any:
|
|
101
|
+
await asyncio.to_thread(
|
|
102
|
+
self.sync_handler.on_chain_error, error, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Tool Events
|
|
106
|
+
async def on_tool_start(
|
|
107
|
+
self, serialized: Dict[str, Any], input_str: str, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
108
|
+
) -> Any:
|
|
109
|
+
await asyncio.to_thread(
|
|
110
|
+
self.sync_handler.on_tool_start, serialized, input_str, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
async def on_tool_end(
|
|
114
|
+
self, output: str, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
115
|
+
) -> Any:
|
|
116
|
+
await asyncio.to_thread(
|
|
117
|
+
self.sync_handler.on_tool_end, output, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
async def on_tool_error(
|
|
121
|
+
self, error: Union[Exception, KeyboardInterrupt], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
122
|
+
) -> Any:
|
|
123
|
+
await asyncio.to_thread(
|
|
124
|
+
self.sync_handler.on_tool_error, error, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Retriever Events
|
|
128
|
+
async def on_retriever_start(
|
|
129
|
+
self, serialized: Dict[str, Any], query: str, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
130
|
+
) -> Any:
|
|
131
|
+
await asyncio.to_thread(
|
|
132
|
+
self.sync_handler.on_retriever_start, serialized, query, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
async def on_retriever_end(
|
|
136
|
+
self, documents: Sequence[Document], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
137
|
+
) -> Any:
|
|
138
|
+
await asyncio.to_thread(
|
|
139
|
+
self.sync_handler.on_retriever_end, documents, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
async def on_retriever_error(
|
|
143
|
+
self, error: Union[Exception, KeyboardInterrupt], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
144
|
+
) -> Any:
|
|
145
|
+
await asyncio.to_thread(
|
|
146
|
+
self.sync_handler.on_retriever_error, error, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Agent Events
|
|
150
|
+
async def on_agent_action(
|
|
151
|
+
self, action: AgentAction, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
152
|
+
) -> Any:
|
|
153
|
+
await asyncio.to_thread(
|
|
154
|
+
self.sync_handler.on_agent_action, action, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
async def on_agent_finish(
|
|
158
|
+
self, finish: AgentFinish, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any
|
|
159
|
+
) -> Any:
|
|
160
|
+
await asyncio.to_thread(
|
|
161
|
+
self.sync_handler.on_agent_finish, finish, run_id=run_id, parent_run_id=parent_run_id, **kwargs
|
|
162
|
+
)
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# File: crewplus/callbacks/async_langfuse_handler.py
|
|
2
|
-
import asyncio
|
|
3
|
-
import contextvars
|
|
4
|
-
from contextlib import contextmanager
|
|
5
|
-
from typing import Any, Dict, List, Union
|
|
6
|
-
|
|
7
|
-
try:
|
|
8
|
-
from langfuse.langchain import CallbackHandler as LangfuseCallbackHandler
|
|
9
|
-
from langchain_core.callbacks import AsyncCallbackHandler
|
|
10
|
-
from langchain_core.outputs import LLMResult
|
|
11
|
-
LANGFUSE_AVAILABLE = True
|
|
12
|
-
except ImportError:
|
|
13
|
-
LANGFUSE_AVAILABLE = False
|
|
14
|
-
LangfuseCallbackHandler = None
|
|
15
|
-
AsyncCallbackHandler = object
|
|
16
|
-
|
|
17
|
-
# This token is a simple flag to indicate that we are in an async context.
|
|
18
|
-
# We use a context variable to make it available only within the async task.
|
|
19
|
-
_ASYNC_CONTEXT_TOKEN = "in_async_context"
|
|
20
|
-
in_async_context = contextvars.ContextVar(_ASYNC_CONTEXT_TOKEN, default=False)
|
|
21
|
-
|
|
22
|
-
@contextmanager
|
|
23
|
-
def async_context():
|
|
24
|
-
"""A context manager to signal that we are in an async execution context."""
|
|
25
|
-
token = in_async_context.set(True)
|
|
26
|
-
try:
|
|
27
|
-
yield
|
|
28
|
-
finally:
|
|
29
|
-
in_async_context.reset(token)
|
|
30
|
-
|
|
31
|
-
class AsyncLangfuseCallbackHandler(AsyncCallbackHandler):
|
|
32
|
-
"""
|
|
33
|
-
Wraps the synchronous LangfuseCallbackHandler to make it compatible with
|
|
34
|
-
LangChain's async methods.
|
|
35
|
-
|
|
36
|
-
This works by running the synchronous handler's methods in a separate thread
|
|
37
|
-
using `asyncio.to_thread`. This is crucial because `asyncio`'s default
|
|
38
|
-
executor can correctly propagate `contextvars`, which solves the
|
|
39
|
-
`ValueError: <Token ...> was created in a different Context` from OpenTelemetry.
|
|
40
|
-
"""
|
|
41
|
-
def __init__(self, *args: Any, **kwargs: Any):
|
|
42
|
-
if not LANGFUSE_AVAILABLE:
|
|
43
|
-
raise ImportError("Langfuse is not available. Please install it with 'pip install langfuse'")
|
|
44
|
-
self.sync_handler = LangfuseCallbackHandler(*args, **kwargs)
|
|
45
|
-
|
|
46
|
-
def __getattr__(self, name: str) -> Any:
|
|
47
|
-
# Delegate any other attribute access to the sync handler
|
|
48
|
-
return getattr(self.sync_handler, name)
|
|
49
|
-
|
|
50
|
-
async def on_llm_start(
|
|
51
|
-
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
|
|
52
|
-
) -> None:
|
|
53
|
-
# --- DEBUGGING: Inspect the arguments from LangChain ---
|
|
54
|
-
print("--- [DEBUG] AsyncLangfuseCallbackHandler.on_llm_start ---")
|
|
55
|
-
print(f"Received prompts type: {type(prompts)}")
|
|
56
|
-
print(f"Received prompts value: {prompts!r}") # Using !r to see quotes if it's a string
|
|
57
|
-
print("----------------------------------------------------------")
|
|
58
|
-
# --- END DEBUGGING ---
|
|
59
|
-
|
|
60
|
-
# WORKAROUND: LangChain's async implementation can sometimes pass a raw
|
|
61
|
-
# string for prompts instead of a list. We wrap it in a list to ensure
|
|
62
|
-
# compatibility with the synchronous handler.
|
|
63
|
-
corrected_prompts = prompts if isinstance(prompts, list) else [prompts]
|
|
64
|
-
|
|
65
|
-
await asyncio.to_thread(
|
|
66
|
-
self.sync_handler.on_llm_start, serialized, corrected_prompts, **kwargs
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
|
70
|
-
await asyncio.to_thread(
|
|
71
|
-
self.sync_handler.on_llm_end, response, **kwargs
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
async def on_llm_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> None:
|
|
75
|
-
await asyncio.to_thread(
|
|
76
|
-
self.sync_handler.on_llm_error, error, **kwargs
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
async def on_tool_start(self, serialized: Dict[str, Any], input_str: str, **kwargs: Any) -> Any:
|
|
80
|
-
await asyncio.to_thread(
|
|
81
|
-
self.sync_handler.on_tool_start, serialized, input_str, **kwargs
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
async def on_tool_end(self, output: str, **kwargs: Any) -> Any:
|
|
85
|
-
await asyncio.to_thread(
|
|
86
|
-
self.sync_handler.on_tool_end, output, **kwargs
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
async def on_tool_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any:
|
|
90
|
-
await asyncio.to_thread(
|
|
91
|
-
self.sync_handler.on_tool_error, error, **kwargs
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
async def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any) -> Any:
|
|
95
|
-
await asyncio.to_thread(
|
|
96
|
-
self.sync_handler.on_chain_start, serialized, inputs, **kwargs
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
async def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any:
|
|
100
|
-
await asyncio.to_thread(
|
|
101
|
-
self.sync_handler.on_chain_end, outputs, **kwargs
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
async def on_chain_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any:
|
|
105
|
-
await asyncio.to_thread(
|
|
106
|
-
self.sync_handler.on_chain_error, error, **kwargs
|
|
107
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|