dm-aioaiagent 0.4.9__tar.gz → 0.5.1__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dm-aioaiagent
3
- Version: 0.4.9
3
+ Version: 0.5.1
4
4
  Summary: This is my custom aioaiagent client
5
5
  Home-page: https://pypi.org/project/dm-aioaiagent
6
6
  Author: dimka4621
@@ -58,6 +58,17 @@ if sys.platform == "win32":
58
58
  asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
59
59
  ```
60
60
 
61
+ ### Api Key Setup
62
+
63
+ You can set your OpenAI API key in the environment variable `OPENAI_API_KEY` or pass it as an argument to the agent.
64
+
65
+ **Use load_dotenv to load the `.env` file.**
66
+
67
+ ```python
68
+ from dotenv import load_dotenv
69
+ load_dotenv()
70
+ ```
71
+
61
72
  ### Use agent *with* inner memory and run *single* message
62
73
 
63
74
  By default, agent use inner memory to store the conversation history.
@@ -179,24 +190,3 @@ def main():
179
190
  if __name__ == "__main__":
180
191
  main()
181
192
  ```
182
-
183
- ### Set custom logger
184
-
185
- ```python
186
- from dm_aioaiagent import DMAIAgent
187
- from dm_logger import FormatterConfig
188
-
189
-
190
- # set up custom logger for all clients
191
- DMAIAgent.set_logger_params(
192
- {
193
- "name": "my_name",
194
- "formatter_config": FormatterConfig(
195
- show_datetime=False,
196
- )
197
- }
198
- )
199
- ```
200
-
201
- See more about DMLogger [here](https://github.com/MykhLibs/dm-logger)
202
-
@@ -21,6 +21,17 @@ if sys.platform == "win32":
21
21
  asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
22
22
  ```
23
23
 
24
+ ### Api Key Setup
25
+
26
+ You can set your OpenAI API key in the environment variable `OPENAI_API_KEY` or pass it as an argument to the agent.
27
+
28
+ **Use load_dotenv to load the `.env` file.**
29
+
30
+ ```python
31
+ from dotenv import load_dotenv
32
+ load_dotenv()
33
+ ```
34
+
24
35
  ### Use agent *with* inner memory and run *single* message
25
36
 
26
37
  By default, agent use inner memory to store the conversation history.
@@ -142,24 +153,3 @@ def main():
142
153
  if __name__ == "__main__":
143
154
  main()
144
155
  ```
145
-
146
- ### Set custom logger
147
-
148
- ```python
149
- from dm_aioaiagent import DMAIAgent
150
- from dm_logger import FormatterConfig
151
-
152
-
153
- # set up custom logger for all clients
154
- DMAIAgent.set_logger_params(
155
- {
156
- "name": "my_name",
157
- "formatter_config": FormatterConfig(
158
- show_datetime=False,
159
- )
160
- }
161
- )
162
- ```
163
-
164
- See more about DMLogger [here](https://github.com/MykhLibs/dm-logger)
165
-
@@ -1,5 +1,3 @@
1
- from dotenv import load_dotenv
2
- load_dotenv()
3
1
  from .ai_agent import DMAIAgent
4
2
  from .async_ai_agent import DMAioAIAgent
5
3
  from .openai_image_message_content import OpenAIImageMessageContent
@@ -12,58 +12,73 @@ from dm_logger import DMLogger
12
12
 
13
13
  from .types import *
14
14
 
15
- __all__ = ["DMAIAgent"]
16
-
17
15
 
18
16
  class DMAIAgent:
19
17
  MAX_MEMORY_MESSAGES = 20 # Only INT greater than 0
20
18
  _ALLOWED_ROLES = ("user", "ai")
21
- _logger_params = None
22
19
 
23
20
  def __init__(
24
21
  self,
22
+ # general
25
23
  system_message: str = "You are a helpful assistant.",
26
- tools: list[BaseTool] = None,
27
- *,
28
24
  model: str = "gpt-4o-mini",
29
25
  temperature: float = None,
30
- parallel_tool_calls: bool = None,
26
+ output_schema: OutputSchemaType = None, # IF you set output_schema, tools and memory will be disabled
31
27
  agent_name: str = "AIAgent",
32
- input_output_logging: bool = True,
28
+ # tools
29
+ tools: list[BaseTool] = None,
30
+ parallel_tool_calls: bool = None,
31
+ # memory
33
32
  is_memory_enabled: bool = True,
34
- max_memory_messages: int = MAX_MEMORY_MESSAGES,
35
33
  save_tools_responses_in_memory: bool = True,
36
- llm_provider_api_key: str = "",
37
- llm_provider_base_url: str = "",
34
+ max_memory_messages: int = MAX_MEMORY_MESSAGES,
35
+ # other
36
+ input_output_logging: bool = True,
38
37
  response_if_request_fail: str = "I can't provide a response right now. Please try again later.",
39
- response_if_invalid_image: str = "The image is unavailable or the link is incorrect."
38
+ response_if_invalid_image: str = "The image is unavailable or the link is incorrect.",
39
+ # llm provider
40
+ llm_provider_api_key: str = "",
41
+ llm_provider_base_url: str = ""
40
42
  ):
41
- self._set_logger(agent_name)
42
- self._input_output_logging = bool(input_output_logging)
43
+ self._logger = DMLogger(agent_name)
43
44
 
45
+ # general
44
46
  self._system_message = str(system_message)
45
- self._tools = tools or []
46
- self._is_tools_exists = bool(tools)
47
47
  self._model = str(model)
48
48
  self._temperature = temperature
49
+ self._output_schema = self._validate_output_schema(output_schema)
50
+ if self._output_schema:
51
+ tools = []
52
+ parallel_tool_calls = None
53
+ is_memory_enabled = False
54
+ # tools
55
+ self._tools = tools or []
56
+ self._is_tools_exists = bool(tools)
49
57
  self._parallel_tool_calls = parallel_tool_calls
50
- self._llm_provider_api_key = str(llm_provider_api_key)
51
- self._llm_provider_base_url = str(llm_provider_base_url)
52
-
58
+ # memory
53
59
  self._memory_messages = []
54
60
  self._is_memory_enabled = bool(is_memory_enabled)
55
61
  self._save_tools_responses_in_memory = bool(save_tools_responses_in_memory)
56
62
  self._max_memory_messages = self._validate_max_memory_messages(max_memory_messages)
63
+ # other
64
+ self._input_output_logging = bool(input_output_logging)
57
65
  self._response_if_request_fail = str(response_if_request_fail)
58
66
  self._response_if_invalid_image = str(response_if_invalid_image)
67
+ # llm provider
68
+ self._llm_provider_api_key = str(llm_provider_api_key)
69
+ self._llm_provider_base_url = str(llm_provider_base_url)
59
70
 
60
71
  self._check_langsmith_envs()
61
72
  self._init_agent()
62
73
  self._init_graph()
63
74
 
64
- def run(self, query: str, **kwargs) -> str:
65
- new_messages = self.run_messages(messages=[{"role": "user", "content": query}], **kwargs)
66
- return new_messages[-1].content
75
+ def run(self, query: str, **kwargs) -> Union[str, TypedDict, BaseModel]:
76
+ new_messages = self.run_messages(messages=[TextMessage(role="user", content=query)], **kwargs)
77
+
78
+ last_message = new_messages[-1]
79
+ if isinstance(last_message, AIMessage):
80
+ return last_message.content
81
+ return last_message
67
82
 
68
83
  def run_messages(
69
84
  self,
@@ -86,8 +101,10 @@ class DMAIAgent:
86
101
  "tags": ls_tags,
87
102
  "run_id": ls_run_id
88
103
  }
89
- state = self._graph.invoke(input={"messages": messages, "new_messages": []},
90
- config={k: v for k, v in config_data.items() if v})
104
+ state = self._graph.invoke(
105
+ input={"messages": messages, "new_messages": []},
106
+ config={k: v for k, v in config_data.items() if v}
107
+ )
91
108
  return state["new_messages"]
92
109
 
93
110
  @property
@@ -127,12 +144,18 @@ class DMAIAgent:
127
144
  except Exception as e:
128
145
  self._logger.error(e)
129
146
  if second_attempt:
130
- response = self._response_if_invalid_image if "invalid_image_url" in str(e) else self._response_if_request_fail
147
+ if "invalid_image_url" in str(e):
148
+ response = self._response_if_invalid_image
149
+ else:
150
+ response = self._response_if_request_fail
151
+
131
152
  ai_response = AIMessage(content=response)
132
153
  state["messages"].append(ai_response)
133
154
  state["new_messages"].append(ai_response)
134
155
  return state
156
+
135
157
  return self._invoke_llm_node(state, second_attempt=True)
158
+
136
159
  state["messages"].append(ai_response)
137
160
  state["new_messages"].append(ai_response)
138
161
  return state
@@ -140,6 +163,7 @@ class DMAIAgent:
140
163
  def _execute_tool_node(self, state: State) -> State:
141
164
  self._logger.debug("Run node: Execute tool")
142
165
  threads = []
166
+
143
167
  for tool_call in state["messages"][-1].tool_calls:
144
168
  tool_id = tool_call["id"]
145
169
  tool_name = tool_call["name"]
@@ -171,7 +195,10 @@ class DMAIAgent:
171
195
 
172
196
  def _exit_node(self, state: State) -> State:
173
197
  if self._input_output_logging:
174
- self._logger.debug(f'Answer:\n{state["messages"][-1].content}')
198
+ answer = state["messages"][-1]
199
+ if isinstance(answer, AIMessage):
200
+ answer = answer.content
201
+ self._logger.debug(f'Answer:\n{answer}')
175
202
 
176
203
  if self._is_memory_enabled:
177
204
  messages_to_memory = state["messages"][-self._max_memory_messages:]
@@ -187,16 +214,18 @@ class DMAIAgent:
187
214
  return state
188
215
 
189
216
  def _messages_router(self, state: State) -> str:
190
- if self._is_tools_exists and state["messages"][-1].tool_calls:
191
- route = "execute_tool"
192
- else:
193
- route = "exit"
194
- return route
217
+ if self._output_schema:
218
+ return "exit"
219
+ elif self._is_tools_exists and state["messages"][-1].tool_calls:
220
+ return "execute_tool"
221
+ return "exit"
195
222
 
196
223
  def _init_agent(self) -> None:
197
224
  base_kwargs = {"model": self._model}
198
225
  if isinstance(self._temperature, float):
199
226
  base_kwargs["temperature"] = self._temperature
227
+ else:
228
+ ValueError("Temperature must be a float value.")
200
229
  if self._llm_provider_api_key:
201
230
  base_kwargs["api_key"] = SecretStr(self._llm_provider_api_key)
202
231
  if self._llm_provider_base_url:
@@ -221,6 +250,9 @@ class DMAIAgent:
221
250
  self._tool_map = {t.name: t for t in self._tools}
222
251
  llm = llm.bind_tools(self._tools, **bind_tool_kwargs)
223
252
 
253
+ if self._output_schema:
254
+ llm = llm.with_structured_output(self._output_schema)
255
+
224
256
  prompt = ChatPromptTemplate.from_messages([SystemMessage(content=self._system_message),
225
257
  MessagesPlaceholder(variable_name="messages")])
226
258
  self._agent = prompt | llm
@@ -255,6 +287,15 @@ class DMAIAgent:
255
287
  return max_messages_in_memory
256
288
  return cls.MAX_MEMORY_MESSAGES
257
289
 
290
+ @staticmethod
291
+ def _validate_output_schema(schema: OutputSchemaType) -> OutputSchemaType:
292
+ if schema is None:
293
+ return None
294
+ if isinstance(schema, type) and \
295
+ (type(schema).__name__ == "_TypedDictMeta" or issubclass(schema, BaseModel)):
296
+ return schema
297
+ raise ValueError("Output schema must be a TypedDict or BaseModel type, or None.")
298
+
258
299
  def print_graph(self) -> None:
259
300
  self._graph.get_graph().print_ascii()
260
301
 
@@ -265,14 +306,3 @@ class DMAIAgent:
265
306
  f.write(image)
266
307
  except Exception as e:
267
308
  self._logger.error(e)
268
-
269
- def _set_logger(self, agent_name: str) -> None:
270
- params = {"name": agent_name}
271
- if isinstance(self._logger_params, dict):
272
- params.update(self._logger_params)
273
- self._logger = DMLogger(**params)
274
-
275
- @classmethod
276
- def set_logger_params(cls, extra_params = None) -> None:
277
- if isinstance(extra_params, dict) or extra_params is None:
278
- cls._logger_params = extra_params
@@ -6,24 +6,18 @@ from langchain_core.messages import AIMessage, ToolMessage
6
6
  from .ai_agent import DMAIAgent
7
7
  from .types import *
8
8
 
9
- __all__ = ["DMAioAIAgent"]
10
-
11
9
 
12
10
  class DMAioAIAgent(DMAIAgent):
13
- _logger_params = None
14
-
15
- def __init__(self, *args, agent_name: str = "AioAIAgent", **kwargs):
11
+ def __init__(self, *args, **kwargs):
16
12
  super().__init__(*args, **kwargs)
17
13
 
18
- if not isinstance(self._logger_params, dict):
19
- self._logger_params = {}
20
- if "name" not in self._logger_params:
21
- self._logger_params["name"] = agent_name
22
- self._set_logger(agent_name)
14
+ async def run(self, query: str, *args, **kwargs) -> Union[str, TypedDict, BaseModel]:
15
+ new_messages = await self.run_messages(messages=[TextMessage(role="user", content=query)], *args, **kwargs)
23
16
 
24
- async def run(self, query: str, *args, **kwargs) -> str:
25
- new_messages = await self.run_messages(messages=[{"role": "user", "content": query}], *args, **kwargs)
26
- return new_messages[-1].content
17
+ last_message = new_messages[-1]
18
+ if isinstance(last_message, AIMessage):
19
+ return last_message.content
20
+ return last_message
27
21
 
28
22
  async def run_messages(
29
23
  self,
@@ -46,8 +40,10 @@ class DMAioAIAgent(DMAIAgent):
46
40
  "tags": ls_tags,
47
41
  "run_id": ls_run_id
48
42
  }
49
- state = await self._graph.ainvoke(input={"messages": messages, "new_messages": []},
50
- config={k: v for k, v in config_data.items() if v})
43
+ state = await self._graph.ainvoke(
44
+ input={"messages": messages, "new_messages": []},
45
+ config={k: v for k, v in config_data.items() if v}
46
+ )
51
47
  return state["new_messages"]
52
48
 
53
49
  async def _invoke_llm_node(self, state: State, second_attempt: bool = False) -> State:
@@ -57,12 +53,18 @@ class DMAioAIAgent(DMAIAgent):
57
53
  except Exception as e:
58
54
  self._logger.error(e)
59
55
  if second_attempt:
60
- response = self._response_if_invalid_image if "invalid_image_url" in str(e) else self._response_if_request_fail
56
+ if "invalid_image_url" in str(e):
57
+ response = self._response_if_invalid_image
58
+ else:
59
+ response = self._response_if_request_fail
60
+
61
61
  ai_response = AIMessage(content=response)
62
62
  state["messages"].append(ai_response)
63
63
  state["new_messages"].append(ai_response)
64
64
  return state
65
+
65
66
  return await self._invoke_llm_node(state, second_attempt=True)
67
+
66
68
  state["messages"].append(ai_response)
67
69
  state["new_messages"].append(ai_response)
68
70
  return state
@@ -70,6 +72,7 @@ class DMAioAIAgent(DMAIAgent):
70
72
  async def _execute_tool_node(self, state: State) -> State:
71
73
  self._logger.debug("Run node: Execute tool")
72
74
  tasks = []
75
+
73
76
  for tool_call in state["messages"][-1].tool_calls:
74
77
  tool_id = tool_call["id"]
75
78
  tool_name = tool_call["name"]
@@ -1,7 +1,9 @@
1
- from typing import Literal, Union
2
- from typing_extensions import TypedDict
1
+ from typing import Literal, Union, Type, TypedDict
2
+ from pydantic import BaseModel
3
3
  from langchain_core.messages import BaseMessage
4
4
 
5
+ OutputSchemaType = Union[Type[TypedDict], Type[BaseModel], None]
6
+
5
7
 
6
8
  class ImageMessageTextItem(TypedDict):
7
9
  type: Literal['text']
@@ -25,6 +27,7 @@ class TextMessage(TypedDict):
25
27
 
26
28
  InputMessage = Union[TextMessage, ImageMessage, BaseMessage]
27
29
 
30
+
28
31
  class State(TypedDict):
29
32
  messages: list[InputMessage]
30
33
  new_messages: list[BaseMessage]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dm-aioaiagent
3
- Version: 0.4.9
3
+ Version: 0.5.1
4
4
  Summary: This is my custom aioaiagent client
5
5
  Home-page: https://pypi.org/project/dm-aioaiagent
6
6
  Author: dimka4621
@@ -58,6 +58,17 @@ if sys.platform == "win32":
58
58
  asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
59
59
  ```
60
60
 
61
+ ### Api Key Setup
62
+
63
+ You can set your OpenAI API key in the environment variable `OPENAI_API_KEY` or pass it as an argument to the agent.
64
+
65
+ **Use load_dotenv to load the `.env` file.**
66
+
67
+ ```python
68
+ from dotenv import load_dotenv
69
+ load_dotenv()
70
+ ```
71
+
61
72
  ### Use agent *with* inner memory and run *single* message
62
73
 
63
74
  By default, agent use inner memory to store the conversation history.
@@ -179,24 +190,3 @@ def main():
179
190
  if __name__ == "__main__":
180
191
  main()
181
192
  ```
182
-
183
- ### Set custom logger
184
-
185
- ```python
186
- from dm_aioaiagent import DMAIAgent
187
- from dm_logger import FormatterConfig
188
-
189
-
190
- # set up custom logger for all clients
191
- DMAIAgent.set_logger_params(
192
- {
193
- "name": "my_name",
194
- "formatter_config": FormatterConfig(
195
- show_datetime=False,
196
- )
197
- }
198
- )
199
- ```
200
-
201
- See more about DMLogger [here](https://github.com/MykhLibs/dm-logger)
202
-
@@ -8,7 +8,7 @@ def readme():
8
8
 
9
9
  setup(
10
10
  name='dm-aioaiagent',
11
- version='v0.4.9',
11
+ version='v0.5.1',
12
12
  author='dimka4621',
13
13
  author_email='mismartconfig@gmail.com',
14
14
  description='This is my custom aioaiagent client',
File without changes