dm-aioaiagent 0.3.2__tar.gz → 0.3.4__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.
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/PKG-INFO +30 -1
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/README.md +28 -0
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent/__init__.py +1 -0
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent/ai_agent.py +38 -9
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent/async_ai_agent.py +11 -3
- dm_aioaiagent-0.3.4/dm_aioaiagent/image_message_content_builder.py +15 -0
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent/types.py +22 -1
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent.egg-info/PKG-INFO +30 -1
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent.egg-info/SOURCES.txt +1 -0
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent.egg-info/requires.txt +1 -0
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/setup.py +2 -1
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent.egg-info/dependency_links.txt +0 -0
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/dm_aioaiagent.egg-info/top_level.txt +0 -0
- {dm_aioaiagent-0.3.2 → dm_aioaiagent-0.3.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dm-aioaiagent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: This is my custom aioaiagent client
|
|
5
5
|
Home-page: https://pypi.org/project/dm-aioaiagent
|
|
6
6
|
Author: dimka4621
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pydantic<3.0.0,>=2.9.2
|
|
|
18
18
|
Requires-Dist: langchain~=0.3.0
|
|
19
19
|
Requires-Dist: langchain-core~=0.3.5
|
|
20
20
|
Requires-Dist: langgraph~=0.2.23
|
|
21
|
+
Requires-Dist: grandalf>=0.8
|
|
21
22
|
Requires-Dist: langchain-community~=0.3.0
|
|
22
23
|
Requires-Dist: langchain-openai~=0.2.0
|
|
23
24
|
|
|
@@ -137,6 +138,34 @@ if __name__ == "__main__":
|
|
|
137
138
|
asyncio.run(main())
|
|
138
139
|
```
|
|
139
140
|
|
|
141
|
+
### Image vision
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from dm_aioaiagent import DMAIAgent, ImageMessageContentBuilder
|
|
145
|
+
|
|
146
|
+
def main():
|
|
147
|
+
# create an agent
|
|
148
|
+
ai_agent = DMAIAgent(agent_name="image_vision", model="gpt-4o")
|
|
149
|
+
|
|
150
|
+
# create an image message content
|
|
151
|
+
# NOTE: text argument is optional
|
|
152
|
+
img_content = ImageMessageContentBuilder(image_url="https://your.domain/image",
|
|
153
|
+
text="Hello, what is shown in the photo?")
|
|
154
|
+
|
|
155
|
+
# define the conversation message
|
|
156
|
+
messages = [
|
|
157
|
+
{"role": "user", "content": "Hello!"},
|
|
158
|
+
{"role": "user", "content": img_content},
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
# call an agent
|
|
162
|
+
answer = ai_agent.run(messages)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
main()
|
|
167
|
+
```
|
|
168
|
+
|
|
140
169
|
### Set custom logger
|
|
141
170
|
|
|
142
171
|
_If you want set up custom logger_
|
|
@@ -114,6 +114,34 @@ if __name__ == "__main__":
|
|
|
114
114
|
asyncio.run(main())
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
### Image vision
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from dm_aioaiagent import DMAIAgent, ImageMessageContentBuilder
|
|
121
|
+
|
|
122
|
+
def main():
|
|
123
|
+
# create an agent
|
|
124
|
+
ai_agent = DMAIAgent(agent_name="image_vision", model="gpt-4o")
|
|
125
|
+
|
|
126
|
+
# create an image message content
|
|
127
|
+
# NOTE: text argument is optional
|
|
128
|
+
img_content = ImageMessageContentBuilder(image_url="https://your.domain/image",
|
|
129
|
+
text="Hello, what is shown in the photo?")
|
|
130
|
+
|
|
131
|
+
# define the conversation message
|
|
132
|
+
messages = [
|
|
133
|
+
{"role": "user", "content": "Hello!"},
|
|
134
|
+
{"role": "user", "content": img_content},
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
# call an agent
|
|
138
|
+
answer = ai_agent.run(messages)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
main()
|
|
143
|
+
```
|
|
144
|
+
|
|
117
145
|
### Set custom logger
|
|
118
146
|
|
|
119
147
|
_If you want set up custom logger_
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from pydantic import SecretStr
|
|
2
3
|
from itertools import dropwhile
|
|
3
4
|
from threading import Thread
|
|
4
5
|
from langchain_openai import ChatOpenAI
|
|
@@ -16,6 +17,8 @@ __all__ = ["DMAIAgent"]
|
|
|
16
17
|
class DMAIAgent:
|
|
17
18
|
agent_name = "AIAgent"
|
|
18
19
|
_allowed_roles = ("user", "ai")
|
|
20
|
+
_response_if_request_fail = "I can't provide a response right now. Please try again later."
|
|
21
|
+
_response_if_invalid_image = "The image is unavailable or the link is incorrect."
|
|
19
22
|
MAX_MEMORY_MESSAGES = 20 # Only INT greater than 0
|
|
20
23
|
|
|
21
24
|
def __init__(
|
|
@@ -28,20 +31,29 @@ class DMAIAgent:
|
|
|
28
31
|
agent_name: str = None,
|
|
29
32
|
input_output_logging: bool = True,
|
|
30
33
|
is_memory_enabled: bool = True,
|
|
34
|
+
save_tools_responses_in_memory: bool = True,
|
|
31
35
|
max_memory_messages: int = None,
|
|
36
|
+
response_if_request_fail: str = None,
|
|
37
|
+
response_if_invalid_image: str = None,
|
|
38
|
+
openai_api_key: str = None
|
|
32
39
|
):
|
|
33
|
-
if not os.getenv("OPENAI_API_KEY"):
|
|
40
|
+
if openai_api_key is None and not os.getenv("OPENAI_API_KEY"):
|
|
34
41
|
raise EnvironmentError("'OPENAI_API_KEY' environment variable is not set!")
|
|
35
42
|
|
|
36
43
|
self._logger = DMLogger(agent_name or self.agent_name)
|
|
37
44
|
self._is_tools_exists = bool(tools)
|
|
38
45
|
self._input_output_logging = bool(input_output_logging)
|
|
39
46
|
self._is_memory_enabled = bool(is_memory_enabled)
|
|
47
|
+
self._save_tools_responses_in_memory = bool(save_tools_responses_in_memory)
|
|
40
48
|
self._max_memory_messages = self._validate_max_memory_messages(max_memory_messages)
|
|
49
|
+
self._response_if_request_fail = str(response_if_request_fail or self._response_if_request_fail)
|
|
50
|
+
self._response_if_invalid_image = str(response_if_invalid_image or self._response_if_invalid_image)
|
|
41
51
|
|
|
42
52
|
prompt = ChatPromptTemplate.from_messages([SystemMessage(content=system_message),
|
|
43
53
|
MessagesPlaceholder(variable_name="messages")])
|
|
44
|
-
|
|
54
|
+
if openai_api_key:
|
|
55
|
+
openai_api_key = SecretStr(openai_api_key)
|
|
56
|
+
llm = ChatOpenAI(model_name=str(model), temperature=int(temperature), openai_api_key=openai_api_key)
|
|
45
57
|
if self._is_tools_exists:
|
|
46
58
|
self._tool_map = {t.name: t for t in tools}
|
|
47
59
|
llm = llm.bind_tools(tools)
|
|
@@ -85,7 +97,6 @@ class DMAIAgent:
|
|
|
85
97
|
self._memory[self._validate_memory_id(memory_id)] = []
|
|
86
98
|
|
|
87
99
|
def _prepare_messages_node(self, state: State) -> State:
|
|
88
|
-
state.memory_id = self._validate_memory_id(state.memory_id)
|
|
89
100
|
state.input_messages = state.input_messages or [{"role": "user", "content": ""}]
|
|
90
101
|
for item in state.input_messages:
|
|
91
102
|
if isinstance(item, dict):
|
|
@@ -102,14 +113,23 @@ class DMAIAgent:
|
|
|
102
113
|
state.messages.append(item)
|
|
103
114
|
|
|
104
115
|
if self._input_output_logging:
|
|
105
|
-
|
|
116
|
+
log_kwargs = {} if state.memory_id is None else {"memory_id": state.memory_id}
|
|
117
|
+
self._logger.debug(f"Query:\n{state.messages[-1].content}", **log_kwargs)
|
|
106
118
|
if self._is_memory_enabled:
|
|
107
119
|
state.messages = self.get_memory_messages(state.memory_id) + state.messages
|
|
108
120
|
return state
|
|
109
121
|
|
|
110
|
-
def _invoke_llm_node(self, state: State) -> State:
|
|
122
|
+
def _invoke_llm_node(self, state: State, second_attempt: bool = False) -> State:
|
|
111
123
|
self._logger.debug("Run node: Invoke LLM")
|
|
112
|
-
|
|
124
|
+
try:
|
|
125
|
+
ai_response = self._agent.invoke({"messages": state.messages})
|
|
126
|
+
except Exception as e:
|
|
127
|
+
self._logger.error(e)
|
|
128
|
+
if second_attempt:
|
|
129
|
+
response = self._response_if_invalid_image if "invalid_image_url" in str(e) else self._response_if_request_fail
|
|
130
|
+
state.messages.append(AIMessage(content=response))
|
|
131
|
+
return state
|
|
132
|
+
return self._invoke_llm_node(state, second_attempt=True)
|
|
113
133
|
state.messages.append(ai_response)
|
|
114
134
|
return state
|
|
115
135
|
|
|
@@ -148,12 +168,21 @@ class DMAIAgent:
|
|
|
148
168
|
def _exit_node(self, state: State) -> State:
|
|
149
169
|
answer = state.messages[-1].content
|
|
150
170
|
if self._input_output_logging:
|
|
151
|
-
|
|
171
|
+
log_kwargs = {} if state.memory_id is None else {"memory_id": state.memory_id}
|
|
172
|
+
self._logger.debug(f"Answer:\n{answer}", **log_kwargs)
|
|
152
173
|
|
|
153
174
|
if self._is_memory_enabled:
|
|
175
|
+
memory_id = self._validate_memory_id(state.memory_id)
|
|
154
176
|
messages_to_memory = state.messages[-self._max_memory_messages:]
|
|
155
|
-
|
|
156
|
-
|
|
177
|
+
if self._save_tools_responses_in_memory:
|
|
178
|
+
# drop ToolsMessages from start of list
|
|
179
|
+
self._memory[memory_id] = list(dropwhile(lambda x: isinstance(x, ToolMessage), messages_to_memory))
|
|
180
|
+
else:
|
|
181
|
+
self._memory[memory_id] = []
|
|
182
|
+
for mes in messages_to_memory:
|
|
183
|
+
if isinstance(mes, ToolMessage) or (isinstance(mes, AIMessage) and mes.tool_calls):
|
|
184
|
+
continue
|
|
185
|
+
self._memory[memory_id].append(mes)
|
|
157
186
|
state.response = answer
|
|
158
187
|
else:
|
|
159
188
|
state.response = state.messages[len(state.input_messages):]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import asyncio
|
|
3
|
-
from langchain_core.messages import ToolMessage
|
|
3
|
+
from langchain_core.messages import AIMessage, ToolMessage
|
|
4
4
|
|
|
5
5
|
from .ai_agent import DMAIAgent
|
|
6
6
|
from .types import *
|
|
@@ -18,9 +18,17 @@ class DMAioAIAgent(DMAIAgent):
|
|
|
18
18
|
state = await self._graph.ainvoke({"input_messages": input_messages, "memory_id": memory_id})
|
|
19
19
|
return state["response"]
|
|
20
20
|
|
|
21
|
-
async def _invoke_llm_node(self, state: State) -> State:
|
|
21
|
+
async def _invoke_llm_node(self, state: State, second_attempt: bool = False) -> State:
|
|
22
22
|
self._logger.debug("Run node: Invoke LLM")
|
|
23
|
-
|
|
23
|
+
try:
|
|
24
|
+
ai_response = await self._agent.ainvoke({"messages": state.messages})
|
|
25
|
+
except Exception as e:
|
|
26
|
+
self._logger.error(e)
|
|
27
|
+
if second_attempt:
|
|
28
|
+
response = self._response_if_invalid_image if "invalid_image_url" in str(e) else self._response_if_request_fail
|
|
29
|
+
state.messages.append(AIMessage(content=response))
|
|
30
|
+
return state
|
|
31
|
+
return await self._invoke_llm_node(state, second_attempt=True)
|
|
24
32
|
state.messages.append(ai_response)
|
|
25
33
|
return state
|
|
26
34
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class ImageMessageContentBuilder(list):
|
|
2
|
+
def __init__(self, image_url: str, text: str = None):
|
|
3
|
+
content = []
|
|
4
|
+
if isinstance(text, str):
|
|
5
|
+
content.append({
|
|
6
|
+
"type": "text",
|
|
7
|
+
"text": text
|
|
8
|
+
})
|
|
9
|
+
content.append({
|
|
10
|
+
"type": "image_url",
|
|
11
|
+
"image_url": {
|
|
12
|
+
"url": image_url
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
super().__init__(content)
|
|
@@ -4,12 +4,33 @@ from pydantic import BaseModel, Field
|
|
|
4
4
|
from langchain_core.messages import BaseMessage
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class
|
|
7
|
+
class ImageMessageTextMessage(TypedDict):
|
|
8
|
+
type: Literal['text']
|
|
9
|
+
text: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ImageMessageImageItem(TypedDict):
|
|
13
|
+
type: Literal['image_url']
|
|
14
|
+
image_url: dict
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
ImageMessageContent = list[Union[ImageMessageTextMessage, ImageMessageImageItem]]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ImageMessage(TypedDict):
|
|
21
|
+
role: Literal["user"]
|
|
22
|
+
content: ImageMessageContent
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TextMessage(TypedDict):
|
|
8
26
|
role: Literal["user", "ai"]
|
|
9
27
|
content: str
|
|
10
28
|
|
|
11
29
|
|
|
30
|
+
Message = Union[TextMessage, ImageMessage]
|
|
31
|
+
|
|
12
32
|
InputMessagesType = list[Union[Message, BaseMessage]]
|
|
33
|
+
|
|
13
34
|
ResponseType = Union[str, list[BaseMessage]]
|
|
14
35
|
|
|
15
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dm-aioaiagent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: This is my custom aioaiagent client
|
|
5
5
|
Home-page: https://pypi.org/project/dm-aioaiagent
|
|
6
6
|
Author: dimka4621
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pydantic<3.0.0,>=2.9.2
|
|
|
18
18
|
Requires-Dist: langchain~=0.3.0
|
|
19
19
|
Requires-Dist: langchain-core~=0.3.5
|
|
20
20
|
Requires-Dist: langgraph~=0.2.23
|
|
21
|
+
Requires-Dist: grandalf>=0.8
|
|
21
22
|
Requires-Dist: langchain-community~=0.3.0
|
|
22
23
|
Requires-Dist: langchain-openai~=0.2.0
|
|
23
24
|
|
|
@@ -137,6 +138,34 @@ if __name__ == "__main__":
|
|
|
137
138
|
asyncio.run(main())
|
|
138
139
|
```
|
|
139
140
|
|
|
141
|
+
### Image vision
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from dm_aioaiagent import DMAIAgent, ImageMessageContentBuilder
|
|
145
|
+
|
|
146
|
+
def main():
|
|
147
|
+
# create an agent
|
|
148
|
+
ai_agent = DMAIAgent(agent_name="image_vision", model="gpt-4o")
|
|
149
|
+
|
|
150
|
+
# create an image message content
|
|
151
|
+
# NOTE: text argument is optional
|
|
152
|
+
img_content = ImageMessageContentBuilder(image_url="https://your.domain/image",
|
|
153
|
+
text="Hello, what is shown in the photo?")
|
|
154
|
+
|
|
155
|
+
# define the conversation message
|
|
156
|
+
messages = [
|
|
157
|
+
{"role": "user", "content": "Hello!"},
|
|
158
|
+
{"role": "user", "content": img_content},
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
# call an agent
|
|
162
|
+
answer = ai_agent.run(messages)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
main()
|
|
167
|
+
```
|
|
168
|
+
|
|
140
169
|
### Set custom logger
|
|
141
170
|
|
|
142
171
|
_If you want set up custom logger_
|
|
@@ -8,7 +8,7 @@ def readme():
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name='dm-aioaiagent',
|
|
11
|
-
version='v0.3.
|
|
11
|
+
version='v0.3.4',
|
|
12
12
|
author='dimka4621',
|
|
13
13
|
author_email='mismartconfig@gmail.com',
|
|
14
14
|
description='This is my custom aioaiagent client',
|
|
@@ -23,6 +23,7 @@ setup(
|
|
|
23
23
|
'langchain~=0.3.0',
|
|
24
24
|
'langchain-core~=0.3.5',
|
|
25
25
|
'langgraph~=0.2.23',
|
|
26
|
+
'grandalf>=0.8',
|
|
26
27
|
'langchain-community~=0.3.0',
|
|
27
28
|
'langchain-openai~=0.2.0',
|
|
28
29
|
],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|