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.
Files changed (57) hide show
  1. flo_ai/__init__.py +13 -0
  2. flo_ai/builders/__init__.py +0 -0
  3. flo_ai/builders/yaml_builder.py +65 -0
  4. flo_ai/callbacks/__init__.py +15 -0
  5. flo_ai/callbacks/flo_callbacks.py +223 -0
  6. flo_ai/callbacks/flo_execution_logger.py +277 -0
  7. flo_ai/common/__init__.py +0 -0
  8. flo_ai/common/flo_langchain_logger.py +68 -0
  9. flo_ai/common/flo_logger.py +122 -0
  10. flo_ai/constants/__init__.py +0 -0
  11. flo_ai/constants/common_constants.py +4 -0
  12. flo_ai/constants/flo_node_contants.py +2 -0
  13. flo_ai/constants/prompt_constants.py +1 -0
  14. flo_ai/core.py +155 -0
  15. flo_ai/error/flo_exception.py +16 -0
  16. flo_ai/factory/agent_factory.py +151 -0
  17. flo_ai/helpers/utils.py +13 -0
  18. flo_ai/models/__init__.py +0 -0
  19. flo_ai/models/delegate.py +7 -0
  20. flo_ai/models/exception.py +8 -0
  21. flo_ai/models/flo_agent.py +112 -0
  22. flo_ai/models/flo_delegation_agent.py +103 -0
  23. flo_ai/models/flo_executable.py +56 -0
  24. flo_ai/models/flo_llm_agent.py +88 -0
  25. flo_ai/models/flo_member.py +4 -0
  26. flo_ai/models/flo_node.py +330 -0
  27. flo_ai/models/flo_reflection_agent.py +76 -0
  28. flo_ai/models/flo_routed_team.py +11 -0
  29. flo_ai/models/flo_team.py +31 -0
  30. flo_ai/models/flo_tool_agent.py +56 -0
  31. flo_ai/parsers/__init__.py +5 -0
  32. flo_ai/parsers/flo_json_parser.py +157 -0
  33. flo_ai/parsers/flo_parser.py +7 -0
  34. flo_ai/parsers/flo_pydantic_parser.py +24 -0
  35. flo_ai/retrievers/__init__.py +0 -0
  36. flo_ai/retrievers/flo_compression_pipeline.py +43 -0
  37. flo_ai/retrievers/flo_multi_query.py +55 -0
  38. flo_ai/retrievers/flo_retriever.py +229 -0
  39. flo_ai/router/__init__.py +0 -0
  40. flo_ai/router/flo_linear.py +86 -0
  41. flo_ai/router/flo_llm_router.py +126 -0
  42. flo_ai/router/flo_router.py +174 -0
  43. flo_ai/router/flo_router_factory.py +55 -0
  44. flo_ai/router/flo_supervisor.py +101 -0
  45. flo_ai/state/__init__.py +4 -0
  46. flo_ai/state/flo_json_output_collector.py +97 -0
  47. flo_ai/state/flo_output_collector.py +26 -0
  48. flo_ai/state/flo_session.py +145 -0
  49. flo_ai/state/flo_state.py +27 -0
  50. flo_ai/storage/data_collector.py +40 -0
  51. flo_ai/tools/__init__.py +3 -0
  52. flo_ai/tools/flo_tool.py +38 -0
  53. flo_ai/yaml/config.py +131 -0
  54. flo_ai/yaml/validators.py +11 -0
  55. flo_ai-0.0.6.dev1.dist-info/METADATA +555 -0
  56. flo_ai-0.0.6.dev1.dist-info/RECORD +57 -0
  57. 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)