flo_ai 0.0.6.dev1__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.
- flo_ai/__init__.py +13 -0
- flo_ai/builders/__init__.py +0 -0
- flo_ai/builders/yaml_builder.py +65 -0
- flo_ai/callbacks/__init__.py +15 -0
- flo_ai/callbacks/flo_callbacks.py +223 -0
- flo_ai/callbacks/flo_execution_logger.py +277 -0
- flo_ai/common/__init__.py +0 -0
- flo_ai/common/flo_langchain_logger.py +68 -0
- flo_ai/common/flo_logger.py +122 -0
- flo_ai/constants/__init__.py +0 -0
- flo_ai/constants/common_constants.py +4 -0
- flo_ai/constants/flo_node_contants.py +2 -0
- flo_ai/constants/prompt_constants.py +1 -0
- flo_ai/core.py +155 -0
- flo_ai/error/flo_exception.py +16 -0
- flo_ai/factory/agent_factory.py +151 -0
- flo_ai/helpers/utils.py +13 -0
- flo_ai/models/__init__.py +0 -0
- flo_ai/models/delegate.py +7 -0
- flo_ai/models/exception.py +8 -0
- flo_ai/models/flo_agent.py +112 -0
- flo_ai/models/flo_delegation_agent.py +103 -0
- flo_ai/models/flo_executable.py +56 -0
- flo_ai/models/flo_llm_agent.py +88 -0
- flo_ai/models/flo_member.py +4 -0
- flo_ai/models/flo_node.py +330 -0
- flo_ai/models/flo_reflection_agent.py +76 -0
- flo_ai/models/flo_routed_team.py +11 -0
- flo_ai/models/flo_team.py +31 -0
- flo_ai/models/flo_tool_agent.py +56 -0
- flo_ai/parsers/__init__.py +5 -0
- flo_ai/parsers/flo_json_parser.py +157 -0
- flo_ai/parsers/flo_parser.py +7 -0
- flo_ai/parsers/flo_pydantic_parser.py +24 -0
- flo_ai/retrievers/__init__.py +0 -0
- flo_ai/retrievers/flo_compression_pipeline.py +43 -0
- flo_ai/retrievers/flo_multi_query.py +55 -0
- flo_ai/retrievers/flo_retriever.py +229 -0
- flo_ai/router/__init__.py +0 -0
- flo_ai/router/flo_linear.py +86 -0
- flo_ai/router/flo_llm_router.py +126 -0
- flo_ai/router/flo_router.py +174 -0
- flo_ai/router/flo_router_factory.py +55 -0
- flo_ai/router/flo_supervisor.py +101 -0
- flo_ai/state/__init__.py +4 -0
- flo_ai/state/flo_json_output_collector.py +97 -0
- flo_ai/state/flo_output_collector.py +26 -0
- flo_ai/state/flo_session.py +145 -0
- flo_ai/state/flo_state.py +27 -0
- flo_ai/storage/data_collector.py +40 -0
- flo_ai/tools/__init__.py +3 -0
- flo_ai/tools/flo_tool.py +38 -0
- flo_ai/yaml/config.py +131 -0
- flo_ai/yaml/validators.py +11 -0
- flo_ai-0.0.6.dev1.dist-info/METADATA +555 -0
- flo_ai-0.0.6.dev1.dist-info/RECORD +57 -0
- flo_ai-0.0.6.dev1.dist-info/WHEEL +4 -0
flo_ai/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from flo_ai.core import Flo as Flo
|
|
2
|
+
from flo_ai.models.flo_team import FloTeam as FloTeam
|
|
3
|
+
from flo_ai.models.flo_agent import FloAgent as FloAgent
|
|
4
|
+
from flo_ai.router.flo_linear import FloLinear as FloLinear
|
|
5
|
+
from flo_ai.router.flo_router import FloRouter as FloRouter
|
|
6
|
+
from flo_ai.state.flo_session import FloSession as FloSession
|
|
7
|
+
from flo_ai.models.flo_llm_agent import FloLLMAgent as FloLLMAgent
|
|
8
|
+
from flo_ai.models.flo_tool_agent import FloToolAgent as FloToolAgent
|
|
9
|
+
from flo_ai.router.flo_llm_router import FloLLMRouter as FloLLMRouter
|
|
10
|
+
from flo_ai.router.flo_supervisor import FloSupervisor as FloSupervisor
|
|
11
|
+
from flo_ai.retrievers.flo_retriever import FloRagBuilder as FloRagBuilder
|
|
12
|
+
from flo_ai.models.flo_delegation_agent import FloDelegatorAgent as FloDelegatorAgent
|
|
13
|
+
from flo_ai.models.flo_reflection_agent import FloReflectionAgent as FloReflectionAgent
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
from flo_ai.models.flo_team import FloTeam
|
|
3
|
+
from flo_ai.yaml.config import (
|
|
4
|
+
FloRoutedTeamConfig,
|
|
5
|
+
TeamConfig,
|
|
6
|
+
AgentConfig,
|
|
7
|
+
FloAgentConfig,
|
|
8
|
+
)
|
|
9
|
+
from flo_ai.models.flo_executable import ExecutableFlo
|
|
10
|
+
from flo_ai.state.flo_session import FloSession
|
|
11
|
+
from flo_ai.router.flo_router_factory import FloRouterFactory
|
|
12
|
+
from flo_ai.factory.agent_factory import AgentFactory
|
|
13
|
+
from flo_ai.error.flo_exception import FloException
|
|
14
|
+
from flo_ai.yaml.validators import raise_for_name_error
|
|
15
|
+
from flo_ai.common.flo_logger import get_logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_supervised_team(
|
|
19
|
+
session: FloSession, flo_config: Union[FloRoutedTeamConfig, FloAgentConfig]
|
|
20
|
+
) -> ExecutableFlo:
|
|
21
|
+
name_set = set()
|
|
22
|
+
if isinstance(flo_config, FloRoutedTeamConfig):
|
|
23
|
+
team_config: TeamConfig = flo_config.team
|
|
24
|
+
team = parse_and_build_subteams(session, team_config, name_set)
|
|
25
|
+
return team
|
|
26
|
+
elif isinstance(flo_config, FloAgentConfig):
|
|
27
|
+
agent_config: AgentConfig = flo_config.agent
|
|
28
|
+
validate_names(name_set, agent_config.name, session)
|
|
29
|
+
agent = AgentFactory.create(session, agent_config)
|
|
30
|
+
return agent
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def validate_team(name_set: set, team_config: TeamConfig, session: FloSession):
|
|
34
|
+
validate_names(name_set, team_config.name, session)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parse_and_build_subteams(
|
|
38
|
+
session: FloSession, team_config: TeamConfig, name_set=set()
|
|
39
|
+
) -> ExecutableFlo:
|
|
40
|
+
flo_team = None
|
|
41
|
+
validate_team(name_set, team_config, session)
|
|
42
|
+
if team_config.agents:
|
|
43
|
+
members = [AgentFactory.create(session, agent) for agent in team_config.agents]
|
|
44
|
+
flo_team = FloTeam.Builder(session, team_config.name, members=members).build()
|
|
45
|
+
router = FloRouterFactory.create(session, team_config, flo_team)
|
|
46
|
+
flo_routed_team = router.build_routed_team()
|
|
47
|
+
else:
|
|
48
|
+
flo_teams = []
|
|
49
|
+
for subteam in team_config.subteams:
|
|
50
|
+
flo_subteam = parse_and_build_subteams(session, subteam, name_set)
|
|
51
|
+
flo_teams.append(flo_subteam)
|
|
52
|
+
flo_team = FloTeam.Builder(session, team_config.name, members=flo_teams).build()
|
|
53
|
+
router = FloRouterFactory.create(session, team_config, flo_team)
|
|
54
|
+
flo_routed_team = router.build_routed_team()
|
|
55
|
+
return flo_routed_team
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def validate_names(name_set: set, name, session: FloSession):
|
|
59
|
+
raise_for_name_error(name)
|
|
60
|
+
if name in name_set:
|
|
61
|
+
get_logger().error(f"Duplicate name found: '{name}'", session)
|
|
62
|
+
raise FloException(
|
|
63
|
+
f"The name '{name}' is duplicate in the config. Make sure all teams and agents have unique names"
|
|
64
|
+
)
|
|
65
|
+
name_set.add(name)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from flo_ai.callbacks.flo_callbacks import (
|
|
2
|
+
flo_agent_callback,
|
|
3
|
+
flo_router_callback,
|
|
4
|
+
flo_tool_callback,
|
|
5
|
+
flo_call_back,
|
|
6
|
+
)
|
|
7
|
+
from flo_ai.callbacks.flo_execution_logger import FloExecutionLogger
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
'flo_agent_callback',
|
|
11
|
+
'flo_router_callback',
|
|
12
|
+
'flo_tool_callback',
|
|
13
|
+
'flo_call_back',
|
|
14
|
+
'FloExecutionLogger',
|
|
15
|
+
]
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
from typing import Any, Union, Callable, Optional, Dict
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from flo_ai.common.flo_logger import get_logger
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class FloCallbackResponse:
|
|
8
|
+
type: str
|
|
9
|
+
name: Optional[str] = None
|
|
10
|
+
model_name: Optional[str] = None
|
|
11
|
+
input: Optional[str] = None
|
|
12
|
+
output: Optional[str] = None
|
|
13
|
+
error: Union[Exception, KeyboardInterrupt, None] = None
|
|
14
|
+
args: Dict = field(default_factory=dict)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FloToolCallback:
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def on_tool_start(
|
|
22
|
+
self, name: str, input: Any, **kwargs: Any
|
|
23
|
+
) -> Optional[FloCallbackResponse]:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def on_tool_end(
|
|
27
|
+
self, name: str, output: Any, **kwargs: Any
|
|
28
|
+
) -> Optional[FloCallbackResponse]:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def on_tool_error(
|
|
32
|
+
self, name: str, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
|
33
|
+
) -> Optional[FloCallbackResponse]:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FloAgentCallback:
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def on_agent_start(
|
|
42
|
+
self, name: str, model_name: str, input: Any, **kwargs: Any
|
|
43
|
+
) -> Optional[FloCallbackResponse]:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def on_agent_end(
|
|
47
|
+
self, name: str, model_name: str, output: Any, **kwargs: Any
|
|
48
|
+
) -> Optional[FloCallbackResponse]:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
def on_agent_error(
|
|
52
|
+
self,
|
|
53
|
+
name: str,
|
|
54
|
+
model_name: str,
|
|
55
|
+
error: Union[Exception, KeyboardInterrupt],
|
|
56
|
+
**kwargs: Any,
|
|
57
|
+
) -> Optional[FloCallbackResponse]:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class FloRouterCallback:
|
|
62
|
+
def __init__(self) -> None:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
def on_router_start(
|
|
66
|
+
self, name: str, model_name: str, input: Any, **kwargs: Any
|
|
67
|
+
) -> Optional[FloCallbackResponse]:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def on_router_end(
|
|
71
|
+
self, name: str, model_name: str, output: Any, **kwargs: Any
|
|
72
|
+
) -> Optional[FloCallbackResponse]:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def on_router_error(
|
|
76
|
+
self,
|
|
77
|
+
name: str,
|
|
78
|
+
model_name: str,
|
|
79
|
+
error: Union[Exception, KeyboardInterrupt],
|
|
80
|
+
**kwargs: Any,
|
|
81
|
+
) -> None:
|
|
82
|
+
Optional[FloCallbackResponse]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def safe_call_cb(func, cb_response: FloCallbackResponse, ignore_error=True):
|
|
86
|
+
try:
|
|
87
|
+
func(cb_response)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
if ignore_error:
|
|
90
|
+
get_logger().warning(e)
|
|
91
|
+
else:
|
|
92
|
+
raise e
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class FunctionalFloToolCallbackImpl(FloToolCallback):
|
|
96
|
+
def __init__(self, func: Callable, ignore_error: bool = True) -> None:
|
|
97
|
+
super().__init__()
|
|
98
|
+
self.func = func
|
|
99
|
+
self.ignore_error = ignore_error
|
|
100
|
+
|
|
101
|
+
def on_tool_start(
|
|
102
|
+
self, name: str, input: Any, **kwargs: Any
|
|
103
|
+
) -> Optional[FloCallbackResponse]:
|
|
104
|
+
cb_response = FloRouterCallback('on_tool_start', name, input=input, args=kwargs)
|
|
105
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
106
|
+
return cb_response
|
|
107
|
+
|
|
108
|
+
def on_tool_end(
|
|
109
|
+
self, name: str, output: Any, **kwargs: Any
|
|
110
|
+
) -> Optional[FloCallbackResponse]:
|
|
111
|
+
cb_response = FloRouterCallback('on_tool_end', name, output=output, args=kwargs)
|
|
112
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
113
|
+
return cb_response
|
|
114
|
+
|
|
115
|
+
def on_tool_error(
|
|
116
|
+
self, name: str, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
|
117
|
+
) -> Optional[FloCallbackResponse]:
|
|
118
|
+
cb_response = FloRouterCallback('on_tool_error', name, error=error, args=kwargs)
|
|
119
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
120
|
+
return cb_response
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class FunctionalFloAgentCallbackImpl(FloAgentCallback):
|
|
124
|
+
def __init__(self, func: Callable, ignore_error: bool = True) -> None:
|
|
125
|
+
super().__init__()
|
|
126
|
+
self.func = func
|
|
127
|
+
self.ignore_error = ignore_error
|
|
128
|
+
|
|
129
|
+
def on_agent_start(
|
|
130
|
+
self, name: str, model_name: str, input: Any, **kwargs: Any
|
|
131
|
+
) -> Any:
|
|
132
|
+
cb_response = FloCallbackResponse(
|
|
133
|
+
'on_agent_start', name, input=input, args=kwargs, model_name=model_name
|
|
134
|
+
)
|
|
135
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
136
|
+
return cb_response
|
|
137
|
+
|
|
138
|
+
def on_agent_end(
|
|
139
|
+
self, name: str, model_name: str, output: Any, **kwargs: Any
|
|
140
|
+
) -> None:
|
|
141
|
+
cb_response = FloCallbackResponse(
|
|
142
|
+
'on_agent_end', name, output=output, args=kwargs, model_name=model_name
|
|
143
|
+
)
|
|
144
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
145
|
+
return cb_response
|
|
146
|
+
|
|
147
|
+
def on_agent_error(
|
|
148
|
+
self,
|
|
149
|
+
name: str,
|
|
150
|
+
model_name: str,
|
|
151
|
+
error: Union[Exception, KeyboardInterrupt],
|
|
152
|
+
**kwargs: Any,
|
|
153
|
+
) -> None:
|
|
154
|
+
cb_response = FloCallbackResponse(
|
|
155
|
+
'on_agent_error', name, error=error, args=kwargs, model_name=model_name
|
|
156
|
+
)
|
|
157
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
158
|
+
return cb_response
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class FunctionalFloRouterCallbackImpl(FloRouterCallback):
|
|
162
|
+
def __init__(self, func: Callable, ignore_error: bool = True) -> None:
|
|
163
|
+
super().__init__()
|
|
164
|
+
self.func = func
|
|
165
|
+
self.ignore_error = ignore_error
|
|
166
|
+
|
|
167
|
+
def on_router_start(
|
|
168
|
+
self, name: str, model_name: str, input: Any, **kwargs: Any
|
|
169
|
+
) -> Any:
|
|
170
|
+
cb_response = FloCallbackResponse(
|
|
171
|
+
'on_router_start', name, input=input, args=kwargs, model_name=model_name
|
|
172
|
+
)
|
|
173
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
174
|
+
return cb_response
|
|
175
|
+
|
|
176
|
+
def on_router_end(
|
|
177
|
+
self, name: str, model_name: str, output: Any, **kwargs: Any
|
|
178
|
+
) -> None:
|
|
179
|
+
cb_response = FloCallbackResponse(
|
|
180
|
+
'on_router_end', name, output=output, args=kwargs, model_name=model_name
|
|
181
|
+
)
|
|
182
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
183
|
+
return cb_response
|
|
184
|
+
|
|
185
|
+
def on_router_error(
|
|
186
|
+
self,
|
|
187
|
+
name: str,
|
|
188
|
+
model_name: str,
|
|
189
|
+
error: Union[Exception, KeyboardInterrupt],
|
|
190
|
+
**kwargs: Any,
|
|
191
|
+
) -> None:
|
|
192
|
+
cb_response = FloCallbackResponse(
|
|
193
|
+
'on_router_error', name, error=error, args=kwargs, model_name=model_name
|
|
194
|
+
)
|
|
195
|
+
safe_call_cb(self.func, cb_response, self.ignore_error)
|
|
196
|
+
return cb_response
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class FloCallback(
|
|
200
|
+
FunctionalFloToolCallbackImpl,
|
|
201
|
+
FunctionalFloAgentCallbackImpl,
|
|
202
|
+
FunctionalFloRouterCallbackImpl,
|
|
203
|
+
):
|
|
204
|
+
def __init__(self, func: Callable, ignore_error: bool = True) -> None:
|
|
205
|
+
FunctionalFloToolCallbackImpl.__init__(self, func, ignore_error)
|
|
206
|
+
FunctionalFloAgentCallbackImpl.__init__(self, func, ignore_error)
|
|
207
|
+
FunctionalFloRouterCallbackImpl.__init__(self, func, ignore_error)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def flo_tool_callback(func: Callable, ignore_error=True) -> FloToolCallback:
|
|
211
|
+
return FunctionalFloToolCallbackImpl(func, ignore_error)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def flo_agent_callback(func: Callable, ignore_error=True) -> FloToolCallback:
|
|
215
|
+
return FunctionalFloAgentCallbackImpl(func, ignore_error)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def flo_router_callback(func: Callable, ignore_error=True) -> FloRouterCallback:
|
|
219
|
+
return FunctionalFloRouterCallbackImpl(func, ignore_error)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def flo_call_back(func: Callable, ignore_error=True) -> FloRouterCallback:
|
|
223
|
+
return FloCallback(func, ignore_error)
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
from langchain_core.callbacks import BaseCallbackHandler
|
|
6
|
+
from langchain.schema.agent import AgentAction, AgentFinish
|
|
7
|
+
from langchain.schema import HumanMessage, AIMessage, BaseMessage
|
|
8
|
+
from langchain_core.prompts.chat import ChatPromptValue
|
|
9
|
+
from flo_ai.storage.data_collector import DataCollector
|
|
10
|
+
from flo_ai.common.flo_logger import get_logger
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToolLogger(ABC):
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def log_all_tools(session_tools):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EnhancedJSONEncoder(json.JSONEncoder):
|
|
21
|
+
def default(self, obj):
|
|
22
|
+
if isinstance(obj, (HumanMessage, AIMessage, BaseMessage)):
|
|
23
|
+
return {
|
|
24
|
+
'type': obj.__class__.__name__,
|
|
25
|
+
'content': obj.content,
|
|
26
|
+
'additional_kwargs': obj.additional_kwargs,
|
|
27
|
+
}
|
|
28
|
+
elif isinstance(obj, AgentAction):
|
|
29
|
+
return {
|
|
30
|
+
'type': 'AgentAction',
|
|
31
|
+
'tool': obj.tool,
|
|
32
|
+
'tool_input': obj.tool_input,
|
|
33
|
+
'log': obj.log,
|
|
34
|
+
}
|
|
35
|
+
elif isinstance(obj, AgentFinish):
|
|
36
|
+
return {
|
|
37
|
+
'type': 'AgentFinish',
|
|
38
|
+
'return_values': obj.return_values,
|
|
39
|
+
'log': obj.log,
|
|
40
|
+
}
|
|
41
|
+
elif isinstance(obj, ChatPromptValue):
|
|
42
|
+
return {
|
|
43
|
+
'type': 'ChatPromptValue',
|
|
44
|
+
'messages': [self.default(msg) for msg in obj.messages],
|
|
45
|
+
}
|
|
46
|
+
elif isinstance(obj, datetime):
|
|
47
|
+
return obj.isoformat()
|
|
48
|
+
elif isinstance(obj, UUID):
|
|
49
|
+
return str(obj)
|
|
50
|
+
elif hasattr(obj, 'to_dict'):
|
|
51
|
+
return obj.to_dict()
|
|
52
|
+
return super().default(obj)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class FloExecutionLogger(BaseCallbackHandler, ToolLogger):
|
|
56
|
+
def __init__(self, data_collector: DataCollector):
|
|
57
|
+
self.data_collector = data_collector
|
|
58
|
+
self.runs = {}
|
|
59
|
+
self.encoder = EnhancedJSONEncoder()
|
|
60
|
+
self.query = None
|
|
61
|
+
self.added_tools = set()
|
|
62
|
+
self.prompt = {}
|
|
63
|
+
|
|
64
|
+
def _encode_entry(self, entry: Dict[str, Any]) -> Dict[str, Any]:
|
|
65
|
+
return json.loads(self.encoder.encode(entry))
|
|
66
|
+
|
|
67
|
+
def _store_entry(self, entry: Dict[str, Any]) -> None:
|
|
68
|
+
try:
|
|
69
|
+
encoded_entry = self._encode_entry(entry)
|
|
70
|
+
self.data_collector.store_log(encoded_entry)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
get_logger().error(f'Error storing entry in FloExecutionLogger: {e}')
|
|
73
|
+
|
|
74
|
+
def on_llm_start(
|
|
75
|
+
self,
|
|
76
|
+
serialized: dict[str, Any],
|
|
77
|
+
prompts: list[str],
|
|
78
|
+
*,
|
|
79
|
+
run_id: UUID,
|
|
80
|
+
parent_run_id: Optional[UUID] = None,
|
|
81
|
+
tags: Optional[list[str]] = None,
|
|
82
|
+
metadata: Optional[dict[str, Any]] = None,
|
|
83
|
+
**kwargs: Any,
|
|
84
|
+
) -> None:
|
|
85
|
+
self.prompt[str(run_id)] = prompts
|
|
86
|
+
|
|
87
|
+
def on_chain_start(
|
|
88
|
+
self,
|
|
89
|
+
serialized: Dict[str, Any],
|
|
90
|
+
inputs: Dict[str, Any],
|
|
91
|
+
*,
|
|
92
|
+
run_id: UUID,
|
|
93
|
+
parent_run_id: Optional[UUID] = None,
|
|
94
|
+
**kwargs: Any,
|
|
95
|
+
) -> None:
|
|
96
|
+
chain_name = (
|
|
97
|
+
serialized.get('name', 'unnamed_chain') if serialized else 'unnamed_chain'
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if parent_run_id and chain_name != 'agent_chain':
|
|
101
|
+
return
|
|
102
|
+
if isinstance(inputs, dict):
|
|
103
|
+
user_input = inputs.get('messages', {})
|
|
104
|
+
else:
|
|
105
|
+
user_input = {}
|
|
106
|
+
if (
|
|
107
|
+
user_input
|
|
108
|
+
and len(user_input) > 0
|
|
109
|
+
and isinstance(user_input[0], HumanMessage)
|
|
110
|
+
):
|
|
111
|
+
if isinstance(user_input[0], HumanMessage):
|
|
112
|
+
self.query = user_input[0].content
|
|
113
|
+
|
|
114
|
+
self.runs[str(run_id)] = {
|
|
115
|
+
'type': 'chain',
|
|
116
|
+
'start_time': datetime.utcnow(),
|
|
117
|
+
'inputs': inputs,
|
|
118
|
+
'name': chain_name,
|
|
119
|
+
'run_id': str(run_id),
|
|
120
|
+
'parent_run_id': str(parent_run_id) if parent_run_id else None,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
def on_chain_end(
|
|
124
|
+
self,
|
|
125
|
+
outputs: Dict[str, Any],
|
|
126
|
+
*,
|
|
127
|
+
run_id: UUID,
|
|
128
|
+
parent_run_id: Optional[UUID] = None,
|
|
129
|
+
**kwargs: Any,
|
|
130
|
+
) -> None:
|
|
131
|
+
if str(run_id) in self.runs:
|
|
132
|
+
run_info = self.runs[str(run_id)]
|
|
133
|
+
if run_info['type'] != 'chain' and run_info['type'] != 'llm':
|
|
134
|
+
return
|
|
135
|
+
run_info['end_time'] = datetime.utcnow()
|
|
136
|
+
run_info['outputs'] = outputs
|
|
137
|
+
run_info['status'] = 'completed'
|
|
138
|
+
run_info['parent_run_id'] = str(parent_run_id) if parent_run_id else None
|
|
139
|
+
run_info['prompt'] = (
|
|
140
|
+
self.prompt[str(run_id)] if str(run_id) in self.prompt else []
|
|
141
|
+
)
|
|
142
|
+
self._store_entry(run_info)
|
|
143
|
+
del self.runs[str(run_id)]
|
|
144
|
+
else:
|
|
145
|
+
if isinstance(outputs, ChatPromptValue) or isinstance(outputs, AgentFinish):
|
|
146
|
+
run_info = {}
|
|
147
|
+
run_info['type'] = 'llm'
|
|
148
|
+
run_info['end_time'] = datetime.utcnow()
|
|
149
|
+
run_info['inputs'] = outputs
|
|
150
|
+
run_info['status'] = 'completed'
|
|
151
|
+
run_info['run_id'] = str(run_id)
|
|
152
|
+
run_info['parent_run_id'] = (
|
|
153
|
+
str(parent_run_id) if parent_run_id else None
|
|
154
|
+
)
|
|
155
|
+
self.runs[str(parent_run_id)] = run_info
|
|
156
|
+
|
|
157
|
+
def on_chain_error(
|
|
158
|
+
self,
|
|
159
|
+
error: Exception,
|
|
160
|
+
*,
|
|
161
|
+
run_id: UUID,
|
|
162
|
+
parent_run_id: Optional[UUID] = None,
|
|
163
|
+
**kwargs: Any,
|
|
164
|
+
) -> None:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
def on_tool_start(
|
|
168
|
+
self,
|
|
169
|
+
serialized: Dict[str, Any],
|
|
170
|
+
input_str: str,
|
|
171
|
+
*,
|
|
172
|
+
run_id: UUID,
|
|
173
|
+
parent_run_id: Optional[UUID] = None,
|
|
174
|
+
tags: Optional[list[str]] = None,
|
|
175
|
+
metadata: Optional[dict[str, Any]] = None,
|
|
176
|
+
inputs: Optional[dict[str, Any]] = None,
|
|
177
|
+
**kwargs: Any,
|
|
178
|
+
) -> None:
|
|
179
|
+
self.runs[str(run_id)] = {
|
|
180
|
+
'type': 'tool',
|
|
181
|
+
'query': self.query,
|
|
182
|
+
'start_time': datetime.utcnow(),
|
|
183
|
+
'tool_name': serialized.get('name', 'unnamed_tool'),
|
|
184
|
+
'input': input_str,
|
|
185
|
+
'parent_run_id': str(parent_run_id) if parent_run_id else None,
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
def on_tool_end(
|
|
189
|
+
self,
|
|
190
|
+
output: str,
|
|
191
|
+
*,
|
|
192
|
+
run_id: UUID,
|
|
193
|
+
parent_run_id: Optional[UUID] = None,
|
|
194
|
+
tags: Optional[list[str]] = None,
|
|
195
|
+
**kwargs: Any,
|
|
196
|
+
) -> None:
|
|
197
|
+
if str(run_id) in self.runs:
|
|
198
|
+
run_info = self.runs[str(run_id)]
|
|
199
|
+
run_info['end_time'] = datetime.utcnow()
|
|
200
|
+
run_info['output'] = output
|
|
201
|
+
run_info['status'] = 'completed'
|
|
202
|
+
self._store_entry(run_info)
|
|
203
|
+
del self.runs[str(run_id)]
|
|
204
|
+
|
|
205
|
+
def on_tool_error(
|
|
206
|
+
self,
|
|
207
|
+
error: Exception,
|
|
208
|
+
*,
|
|
209
|
+
run_id: UUID,
|
|
210
|
+
parent_run_id: Optional[UUID] = None,
|
|
211
|
+
**kwargs: Any,
|
|
212
|
+
) -> None:
|
|
213
|
+
if str(run_id) in self.runs:
|
|
214
|
+
run_info = self.runs[str(run_id)]
|
|
215
|
+
run_info['end_time'] = datetime.utcnow()
|
|
216
|
+
run_info['error'] = str(error)
|
|
217
|
+
run_info['status'] = 'error'
|
|
218
|
+
self._store_entry(run_info)
|
|
219
|
+
del self.runs[str(run_id)]
|
|
220
|
+
|
|
221
|
+
def on_agent_action(
|
|
222
|
+
self,
|
|
223
|
+
action: AgentAction,
|
|
224
|
+
*,
|
|
225
|
+
run_id: UUID,
|
|
226
|
+
parent_run_id: Optional[UUID] = None,
|
|
227
|
+
**kwargs: Any,
|
|
228
|
+
) -> None:
|
|
229
|
+
agent_info = {
|
|
230
|
+
'type': 'agent_action',
|
|
231
|
+
'start_time': datetime.utcnow(),
|
|
232
|
+
'tool': action.tool,
|
|
233
|
+
'tool_input': action.tool_input,
|
|
234
|
+
'log': action.log,
|
|
235
|
+
'parent_run_id': str(parent_run_id) if parent_run_id else None,
|
|
236
|
+
}
|
|
237
|
+
self.runs[str(run_id)] = agent_info
|
|
238
|
+
self._store_entry(agent_info)
|
|
239
|
+
|
|
240
|
+
def on_agent_finish(
|
|
241
|
+
self,
|
|
242
|
+
finish: AgentFinish,
|
|
243
|
+
*,
|
|
244
|
+
run_id: UUID,
|
|
245
|
+
parent_run_id: Optional[UUID] = None,
|
|
246
|
+
**kwargs: Any,
|
|
247
|
+
) -> None:
|
|
248
|
+
log_entry = {
|
|
249
|
+
'type': 'agent_finish',
|
|
250
|
+
'time': datetime.utcnow(),
|
|
251
|
+
'output': finish.return_values,
|
|
252
|
+
'log': finish.log,
|
|
253
|
+
'parent_run_id': str(parent_run_id) if parent_run_id else None,
|
|
254
|
+
}
|
|
255
|
+
self._store_entry(log_entry)
|
|
256
|
+
|
|
257
|
+
def log_all_tools(self, session_tools):
|
|
258
|
+
try:
|
|
259
|
+
tools = []
|
|
260
|
+
|
|
261
|
+
for val in session_tools:
|
|
262
|
+
tool_name = session_tools[val].name
|
|
263
|
+
if tool_name not in self.added_tools:
|
|
264
|
+
tools.append(
|
|
265
|
+
{
|
|
266
|
+
'tool_name': tool_name,
|
|
267
|
+
'description': session_tools.get(val).description,
|
|
268
|
+
'args': session_tools.get(val).args,
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
self.added_tools.add(tool_name)
|
|
272
|
+
|
|
273
|
+
encoded_entry = self._encode_entry(tools)
|
|
274
|
+
if encoded_entry:
|
|
275
|
+
self.data_collector.store_tool_log(encoded_entry)
|
|
276
|
+
except Exception as e:
|
|
277
|
+
get_logger().error(f'Error storing tool in FloExecutionLogger: {e}')
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Union
|
|
2
|
+
from langchain.callbacks.base import BaseCallbackHandler
|
|
3
|
+
from langchain.schema import AgentAction, AgentFinish, LLMResult
|
|
4
|
+
from flo_ai.common.flo_logger import get_logger
|
|
5
|
+
from flo_ai.callbacks.flo_callbacks import FloToolCallback
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FloLangchainLogger(BaseCallbackHandler):
|
|
9
|
+
def __init__(self, session_id: str, tool_callbacks: List[FloToolCallback] = []):
|
|
10
|
+
self.session_id = session_id
|
|
11
|
+
self.tool_callbacks = tool_callbacks
|
|
12
|
+
|
|
13
|
+
def on_llm_start(
|
|
14
|
+
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
|
|
15
|
+
) -> None:
|
|
16
|
+
get_logger().debug(f'onLLMStart: {prompts}', self)
|
|
17
|
+
|
|
18
|
+
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
|
19
|
+
get_logger().debug(f'onNewToken: {token}', self)
|
|
20
|
+
|
|
21
|
+
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
|
22
|
+
get_logger().debug(f'onLLMEnd: {response.generations}', self)
|
|
23
|
+
|
|
24
|
+
def on_llm_error(
|
|
25
|
+
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
|
26
|
+
) -> None:
|
|
27
|
+
get_logger().debug(f'onLLMEnd: {error}', self)
|
|
28
|
+
|
|
29
|
+
def on_chain_start(
|
|
30
|
+
self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
|
|
31
|
+
) -> None:
|
|
32
|
+
get_logger().debug(f'onChainStart: {inputs}', self)
|
|
33
|
+
|
|
34
|
+
def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> None:
|
|
35
|
+
get_logger().debug(f'onChainEnd: {outputs}', self)
|
|
36
|
+
|
|
37
|
+
def on_chain_error(
|
|
38
|
+
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
|
39
|
+
) -> None:
|
|
40
|
+
get_logger().debug(f'onChainError: {error}', self)
|
|
41
|
+
|
|
42
|
+
def on_tool_start(
|
|
43
|
+
self, serialized: Dict[str, Any], input_str: str, **kwargs: Any
|
|
44
|
+
) -> None:
|
|
45
|
+
get_logger().debug(f'onToolStart: {input_str}', self)
|
|
46
|
+
[
|
|
47
|
+
x.on_tool_start(serialized['name'], kwargs['inputs'], kwargs)
|
|
48
|
+
for x in self.tool_callbacks
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
def on_tool_end(self, output: str, **kwargs: Any) -> None:
|
|
52
|
+
get_logger().debug(f'onToolEnd: {output}', self)
|
|
53
|
+
[x.on_tool_end(kwargs['name'], output, kwargs) for x in self.tool_callbacks]
|
|
54
|
+
|
|
55
|
+
def on_tool_error(
|
|
56
|
+
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
|
57
|
+
) -> None:
|
|
58
|
+
get_logger().debug(f'onToolError: {error}', self)
|
|
59
|
+
[x.on_tool_error(kwargs['name'], error, kwargs) for x in self.tool_callbacks]
|
|
60
|
+
|
|
61
|
+
def on_text(self, text: str, **kwargs: Any) -> None:
|
|
62
|
+
get_logger().debug(f'onText: {text}', self)
|
|
63
|
+
|
|
64
|
+
def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any:
|
|
65
|
+
get_logger().debug(f'onAgentAction: {action.tool} - {action.tool_input}', self)
|
|
66
|
+
|
|
67
|
+
def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> None:
|
|
68
|
+
get_logger().debug(f'onAgentFinish: {finish.return_values}', self)
|