solace-agent-mesh 0.0.1__py3-none-any.whl → 0.1.0__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.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/__init__.py +0 -3
- solace_agent_mesh/agents/__init__.py +0 -0
- solace_agent_mesh/agents/base_agent_component.py +224 -0
- solace_agent_mesh/agents/global/__init__.py +0 -0
- solace_agent_mesh/agents/global/actions/__init__.py +0 -0
- solace_agent_mesh/agents/global/actions/agent_state_change.py +54 -0
- solace_agent_mesh/agents/global/actions/clear_history.py +32 -0
- solace_agent_mesh/agents/global/actions/convert_file_to_markdown.py +160 -0
- solace_agent_mesh/agents/global/actions/create_file.py +70 -0
- solace_agent_mesh/agents/global/actions/error_action.py +45 -0
- solace_agent_mesh/agents/global/actions/plantuml_diagram.py +93 -0
- solace_agent_mesh/agents/global/actions/plotly_graph.py +117 -0
- solace_agent_mesh/agents/global/actions/retrieve_file.py +51 -0
- solace_agent_mesh/agents/global/global_agent_component.py +38 -0
- solace_agent_mesh/agents/image_processing/__init__.py +0 -0
- solace_agent_mesh/agents/image_processing/actions/__init__.py +0 -0
- solace_agent_mesh/agents/image_processing/actions/create_image.py +75 -0
- solace_agent_mesh/agents/image_processing/actions/describe_image.py +115 -0
- solace_agent_mesh/agents/image_processing/image_processing_agent_component.py +23 -0
- solace_agent_mesh/agents/slack/__init__.py +1 -0
- solace_agent_mesh/agents/slack/actions/__init__.py +1 -0
- solace_agent_mesh/agents/slack/actions/post_message.py +177 -0
- solace_agent_mesh/agents/slack/slack_agent_component.py +59 -0
- solace_agent_mesh/agents/web_request/__init__.py +0 -0
- solace_agent_mesh/agents/web_request/actions/__init__.py +0 -0
- solace_agent_mesh/agents/web_request/actions/do_image_search.py +84 -0
- solace_agent_mesh/agents/web_request/actions/do_news_search.py +47 -0
- solace_agent_mesh/agents/web_request/actions/do_suggestion_search.py +34 -0
- solace_agent_mesh/agents/web_request/actions/do_web_request.py +134 -0
- solace_agent_mesh/agents/web_request/actions/download_file.py +69 -0
- solace_agent_mesh/agents/web_request/web_request_agent_component.py +33 -0
- solace_agent_mesh/cli/__init__.py +1 -0
- solace_agent_mesh/cli/commands/__init__.py +0 -0
- solace_agent_mesh/cli/commands/add/__init__.py +3 -0
- solace_agent_mesh/cli/commands/add/add.py +88 -0
- solace_agent_mesh/cli/commands/add/agent.py +110 -0
- solace_agent_mesh/cli/commands/add/copy_from_plugin.py +90 -0
- solace_agent_mesh/cli/commands/add/gateway.py +221 -0
- solace_agent_mesh/cli/commands/build.py +631 -0
- solace_agent_mesh/cli/commands/chat/__init__.py +3 -0
- solace_agent_mesh/cli/commands/chat/chat.py +361 -0
- solace_agent_mesh/cli/commands/config.py +29 -0
- solace_agent_mesh/cli/commands/init/__init__.py +3 -0
- solace_agent_mesh/cli/commands/init/ai_provider_step.py +76 -0
- solace_agent_mesh/cli/commands/init/broker_step.py +102 -0
- solace_agent_mesh/cli/commands/init/builtin_agent_step.py +88 -0
- solace_agent_mesh/cli/commands/init/check_if_already_done.py +13 -0
- solace_agent_mesh/cli/commands/init/create_config_file_step.py +52 -0
- solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +96 -0
- solace_agent_mesh/cli/commands/init/file_service_step.py +73 -0
- solace_agent_mesh/cli/commands/init/init.py +114 -0
- solace_agent_mesh/cli/commands/init/project_structure_step.py +45 -0
- solace_agent_mesh/cli/commands/init/rest_api_step.py +50 -0
- solace_agent_mesh/cli/commands/init/web_ui_step.py +40 -0
- solace_agent_mesh/cli/commands/plugin/__init__.py +3 -0
- solace_agent_mesh/cli/commands/plugin/add.py +98 -0
- solace_agent_mesh/cli/commands/plugin/build.py +217 -0
- solace_agent_mesh/cli/commands/plugin/create.py +117 -0
- solace_agent_mesh/cli/commands/plugin/plugin.py +109 -0
- solace_agent_mesh/cli/commands/plugin/remove.py +71 -0
- solace_agent_mesh/cli/commands/run.py +68 -0
- solace_agent_mesh/cli/commands/visualizer.py +138 -0
- solace_agent_mesh/cli/config.py +81 -0
- solace_agent_mesh/cli/main.py +306 -0
- solace_agent_mesh/cli/utils.py +246 -0
- solace_agent_mesh/common/__init__.py +0 -0
- solace_agent_mesh/common/action.py +91 -0
- solace_agent_mesh/common/action_list.py +37 -0
- solace_agent_mesh/common/action_response.py +327 -0
- solace_agent_mesh/common/constants.py +3 -0
- solace_agent_mesh/common/mysql_database.py +40 -0
- solace_agent_mesh/common/postgres_database.py +79 -0
- solace_agent_mesh/common/prompt_templates.py +30 -0
- solace_agent_mesh/common/prompt_templates_unused_delete.py +161 -0
- solace_agent_mesh/common/stimulus_utils.py +152 -0
- solace_agent_mesh/common/time.py +24 -0
- solace_agent_mesh/common/utils.py +638 -0
- solace_agent_mesh/configs/agent_global.yaml +74 -0
- solace_agent_mesh/configs/agent_image_processing.yaml +82 -0
- solace_agent_mesh/configs/agent_slack.yaml +64 -0
- solace_agent_mesh/configs/agent_web_request.yaml +75 -0
- solace_agent_mesh/configs/conversation_to_file.yaml +56 -0
- solace_agent_mesh/configs/error_catcher.yaml +56 -0
- solace_agent_mesh/configs/monitor.yaml +0 -0
- solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +106 -0
- solace_agent_mesh/configs/monitor_user_feedback.yaml +58 -0
- solace_agent_mesh/configs/orchestrator.yaml +241 -0
- solace_agent_mesh/configs/service_embedding.yaml +81 -0
- solace_agent_mesh/configs/service_llm.yaml +265 -0
- solace_agent_mesh/configs/visualize_websocket.yaml +55 -0
- solace_agent_mesh/gateway/__init__.py +0 -0
- solace_agent_mesh/gateway/components/__init__.py +0 -0
- solace_agent_mesh/gateway/components/gateway_base.py +41 -0
- solace_agent_mesh/gateway/components/gateway_input.py +265 -0
- solace_agent_mesh/gateway/components/gateway_output.py +289 -0
- solace_agent_mesh/gateway/identity/bamboohr_identity.py +18 -0
- solace_agent_mesh/gateway/identity/identity_base.py +10 -0
- solace_agent_mesh/gateway/identity/identity_provider.py +60 -0
- solace_agent_mesh/gateway/identity/no_identity.py +9 -0
- solace_agent_mesh/gateway/identity/passthru_identity.py +9 -0
- solace_agent_mesh/monitors/base_monitor_component.py +26 -0
- solace_agent_mesh/monitors/feedback/user_feedback_monitor.py +75 -0
- solace_agent_mesh/monitors/stim_and_errors/stim_and_error_monitor.py +560 -0
- solace_agent_mesh/orchestrator/__init__.py +0 -0
- solace_agent_mesh/orchestrator/action_manager.py +225 -0
- solace_agent_mesh/orchestrator/components/__init__.py +0 -0
- solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +54 -0
- solace_agent_mesh/orchestrator/components/orchestrator_action_response_component.py +179 -0
- solace_agent_mesh/orchestrator/components/orchestrator_register_component.py +107 -0
- solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +477 -0
- solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +246 -0
- solace_agent_mesh/orchestrator/orchestrator_main.py +166 -0
- solace_agent_mesh/orchestrator/orchestrator_prompt.py +410 -0
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/authorization/providers/base_authorization_provider.py +56 -0
- solace_agent_mesh/services/bamboo_hr_service/__init__.py +3 -0
- solace_agent_mesh/services/bamboo_hr_service/bamboo_hr.py +182 -0
- solace_agent_mesh/services/common/__init__.py +4 -0
- solace_agent_mesh/services/common/auto_expiry.py +45 -0
- solace_agent_mesh/services/common/singleton.py +18 -0
- solace_agent_mesh/services/file_service/__init__.py +14 -0
- solace_agent_mesh/services/file_service/file_manager/__init__.py +0 -0
- solace_agent_mesh/services/file_service/file_manager/bucket_file_manager.py +149 -0
- solace_agent_mesh/services/file_service/file_manager/file_manager_base.py +162 -0
- solace_agent_mesh/services/file_service/file_manager/memory_file_manager.py +64 -0
- solace_agent_mesh/services/file_service/file_manager/volume_file_manager.py +106 -0
- solace_agent_mesh/services/file_service/file_service.py +432 -0
- solace_agent_mesh/services/file_service/file_service_constants.py +54 -0
- solace_agent_mesh/services/file_service/file_transformations.py +131 -0
- solace_agent_mesh/services/file_service/file_utils.py +322 -0
- solace_agent_mesh/services/file_service/transformers/__init__.py +5 -0
- solace_agent_mesh/services/history_service/__init__.py +3 -0
- solace_agent_mesh/services/history_service/history_providers/__init__.py +0 -0
- solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +78 -0
- solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +167 -0
- solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +163 -0
- solace_agent_mesh/services/history_service/history_service.py +139 -0
- solace_agent_mesh/services/llm_service/components/llm_request_component.py +293 -0
- solace_agent_mesh/services/llm_service/components/llm_service_component_base.py +152 -0
- solace_agent_mesh/services/middleware_service/__init__.py +0 -0
- solace_agent_mesh/services/middleware_service/middleware_service.py +20 -0
- solace_agent_mesh/templates/action.py +38 -0
- solace_agent_mesh/templates/agent.py +29 -0
- solace_agent_mesh/templates/agent.yaml +70 -0
- solace_agent_mesh/templates/gateway-config-template.yaml +6 -0
- solace_agent_mesh/templates/gateway-default-config.yaml +28 -0
- solace_agent_mesh/templates/gateway-flows.yaml +81 -0
- solace_agent_mesh/templates/gateway-header.yaml +16 -0
- solace_agent_mesh/templates/gateway_base.py +15 -0
- solace_agent_mesh/templates/gateway_input.py +98 -0
- solace_agent_mesh/templates/gateway_output.py +71 -0
- solace_agent_mesh/templates/plugin-pyproject.toml +30 -0
- solace_agent_mesh/templates/rest-api-default-config.yaml +23 -0
- solace_agent_mesh/templates/rest-api-flows.yaml +80 -0
- solace_agent_mesh/templates/slack-default-config.yaml +9 -0
- solace_agent_mesh/templates/slack-flows.yaml +90 -0
- solace_agent_mesh/templates/solace-agent-mesh-default.yaml +77 -0
- solace_agent_mesh/templates/solace-agent-mesh-plugin-default.yaml +8 -0
- solace_agent_mesh/templates/web-default-config.yaml +5 -0
- solace_agent_mesh/templates/web-flows.yaml +86 -0
- solace_agent_mesh/tools/__init__.py +0 -0
- solace_agent_mesh/tools/components/__init__.py +0 -0
- solace_agent_mesh/tools/components/conversation_formatter.py +111 -0
- solace_agent_mesh/tools/components/file_resolver_component.py +58 -0
- solace_agent_mesh/tools/config/runtime_config.py +26 -0
- solace_agent_mesh-0.1.0.dist-info/METADATA +179 -0
- solace_agent_mesh-0.1.0.dist-info/RECORD +170 -0
- solace_agent_mesh-0.1.0.dist-info/entry_points.txt +3 -0
- solace_agent_mesh-0.0.1.dist-info/licenses/LICENSE.txt → solace_agent_mesh-0.1.0.dist-info/licenses/LICENSE +1 -2
- solace_agent_mesh-0.0.1.dist-info/METADATA +0 -51
- solace_agent_mesh-0.0.1.dist-info/RECORD +0 -5
- {solace_agent_mesh-0.0.1.dist-info → solace_agent_mesh-0.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""PlantUML diagram"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
from solace_ai_connector.common.log import log
|
|
8
|
+
from ....common.action import Action
|
|
9
|
+
from ....common.action_response import ActionResponse
|
|
10
|
+
from ....services.file_service import FileService
|
|
11
|
+
|
|
12
|
+
PLANTUML_PROMPT = """Generate a PlantUML markup language for the given description by the user. Supports sequence diagrams, class diagrams, use case diagrams, activity diagrams, component diagrams, state diagrams, and timing diagrams. Newlines must be escaped with a backslash and put quotes around participants names. Respond only with PlantUML markup, do not include any other text, symbols or quotes.
|
|
13
|
+
Rules to follow for sequence diagrams:
|
|
14
|
+
- Do not use variables if they are not declared as participants.
|
|
15
|
+
- Only use notes to cover one or two participants per line.
|
|
16
|
+
- Do not use the `activate` or `deactivate` commands directly after a delay notation (`...`), whether directly or with a note in between.
|
|
17
|
+
- Do not use the `activate` or `deactivate` commands twice in a row, whether directly or with a note in between.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PlantUmlDiagram(Action):
|
|
22
|
+
|
|
23
|
+
def __init__(self, **kwargs):
|
|
24
|
+
super().__init__(
|
|
25
|
+
{
|
|
26
|
+
"name": "plantuml",
|
|
27
|
+
"prompt_directive": (
|
|
28
|
+
"Generate PlantUML diagrams using description. Supports sequence diagrams, class diagrams, "
|
|
29
|
+
"use case diagrams, activity diagrams, component diagrams, state diagrams, and timing diagrams."
|
|
30
|
+
),
|
|
31
|
+
"params": [
|
|
32
|
+
{
|
|
33
|
+
"name": "diagram_description",
|
|
34
|
+
"desc": "The detailed description of the diagram to be generated in natural English language.",
|
|
35
|
+
"type": "string",
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"required_scopes": ["global:plantuml:read"],
|
|
39
|
+
},
|
|
40
|
+
**kwargs,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def invoke(self, params, meta={}) -> ActionResponse:
|
|
44
|
+
# Do a local command to run plantuml -tpng
|
|
45
|
+
description = params.get("diagram_description")
|
|
46
|
+
agent = self.get_agent()
|
|
47
|
+
messages = [
|
|
48
|
+
{"role": "system", "content": PLANTUML_PROMPT},
|
|
49
|
+
{"role": "user", "content": description},
|
|
50
|
+
]
|
|
51
|
+
agent = self.get_agent()
|
|
52
|
+
response = agent.do_llm_service_request(messages=messages)
|
|
53
|
+
expression = response.get("content")
|
|
54
|
+
|
|
55
|
+
# Surround expression with @startuml and @enduml if missing
|
|
56
|
+
if not expression.startswith("@startuml"):
|
|
57
|
+
expression = f"@startuml\n{expression}"
|
|
58
|
+
if not expression.endswith("@enduml"):
|
|
59
|
+
expression = f"{expression}\n@enduml"
|
|
60
|
+
log.debug("PlantUML expression: %s", expression)
|
|
61
|
+
img_path = None
|
|
62
|
+
files = []
|
|
63
|
+
with tempfile.NamedTemporaryFile(delete=True) as temp:
|
|
64
|
+
temp.write(expression.encode())
|
|
65
|
+
temp.flush()
|
|
66
|
+
dir_path = os.path.dirname(temp.name)
|
|
67
|
+
img_path = f"{dir_path}/{os.path.basename(temp.name)}.png"
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
subprocess.run(
|
|
71
|
+
["plantuml", "-tpng", temp.name, "-o", dir_path],
|
|
72
|
+
check=True,
|
|
73
|
+
capture_output=True,
|
|
74
|
+
text=True,
|
|
75
|
+
)
|
|
76
|
+
file_service = FileService()
|
|
77
|
+
file_meta = file_service.upload_from_file(
|
|
78
|
+
img_path,
|
|
79
|
+
meta.get("session_id"),
|
|
80
|
+
data_source="Global Agent - PlantUML Action",
|
|
81
|
+
)
|
|
82
|
+
files.append(file_meta)
|
|
83
|
+
|
|
84
|
+
except subprocess.CalledProcessError as e:
|
|
85
|
+
log.error("Subprocess failed with stderr: %s", e.stderr)
|
|
86
|
+
return ActionResponse(
|
|
87
|
+
message=f"Failed to create diagram: {e.stderr}",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return ActionResponse(
|
|
91
|
+
message=f"diagram created with the plantUML: \n```\n{expression}\n```",
|
|
92
|
+
files=files,
|
|
93
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Plotly graph generation"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import random
|
|
5
|
+
import tempfile
|
|
6
|
+
import json
|
|
7
|
+
import yaml
|
|
8
|
+
from io import BytesIO
|
|
9
|
+
from solace_ai_connector.common.log import log
|
|
10
|
+
|
|
11
|
+
import plotly.graph_objects as go
|
|
12
|
+
import plotly.io as pio
|
|
13
|
+
|
|
14
|
+
from ....common.action import Action
|
|
15
|
+
from ....common.action_response import ActionResponse
|
|
16
|
+
from ....services.file_service import FileService
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PlotlyGraph(Action):
|
|
20
|
+
|
|
21
|
+
def __init__(self, **kwargs):
|
|
22
|
+
super().__init__(
|
|
23
|
+
{
|
|
24
|
+
"name": "plotly",
|
|
25
|
+
"prompt_directive": (
|
|
26
|
+
"Create a plotly graph from the given plotly figure configuration. Note that this will be rendered as a static image that can't be interacted with."
|
|
27
|
+
),
|
|
28
|
+
"params": [
|
|
29
|
+
{
|
|
30
|
+
"name": "plotly_figure_config",
|
|
31
|
+
"desc": "The plotly figure configuration as a yaml object (do not give python code, just the yaml object)",
|
|
32
|
+
"type": "object",
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"required_scopes": ["global:plotly:read"],
|
|
36
|
+
"examples": [
|
|
37
|
+
""" <example>
|
|
38
|
+
<example_docstring>
|
|
39
|
+
This is an example of a user asking for a bar graph. The plotly action from the global agent is invoked to generate the graph.
|
|
40
|
+
</example_docstring>
|
|
41
|
+
<example_stimulus>
|
|
42
|
+
<{tp}stimulus starting_id="12">
|
|
43
|
+
Please generate a random bar graph for me
|
|
44
|
+
</{tp}stimulus>
|
|
45
|
+
<{tp}stimulus_metadata>
|
|
46
|
+
local_time: 2024-09-04 15:59:02 EDT-0400 (Wednesday)
|
|
47
|
+
</{tp}stimulus_metadata>
|
|
48
|
+
</example_stimulus>
|
|
49
|
+
<example_response>
|
|
50
|
+
<{tp}reasoning>
|
|
51
|
+
- user has requested a random bar graph
|
|
52
|
+
- invoke the plotly action from the global agent to generate a bar graph with random data
|
|
53
|
+
</{tp}reasoning>
|
|
54
|
+
Certainly! I'd be happy to generate a random bar graph for you.
|
|
55
|
+
<{tp}status_update>I'll use our plotting tool to create this for you right away.</{tp}status_update>
|
|
56
|
+
<{tp}invoke_action agent="global" action="plotly">
|
|
57
|
+
<{tp}parameter name="plotly_figure_config">
|
|
58
|
+
{{
|
|
59
|
+
"data": [
|
|
60
|
+
{{
|
|
61
|
+
"x": ["A", "B", "C", "D", "E"],
|
|
62
|
+
"y": [23, 45, 56, 78, 90],
|
|
63
|
+
"type": "bar"
|
|
64
|
+
}}
|
|
65
|
+
],
|
|
66
|
+
"layout": {{
|
|
67
|
+
"title": "Random Bar Graph",
|
|
68
|
+
"xaxis": {{"title": "Categories"}},
|
|
69
|
+
"yaxis": {{"title": "Values"}}
|
|
70
|
+
}}
|
|
71
|
+
}}
|
|
72
|
+
</{tp}parameter>
|
|
73
|
+
</{tp}invoke_action>
|
|
74
|
+
</example_response>
|
|
75
|
+
</example>
|
|
76
|
+
"""
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
**kwargs,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def invoke(self, params, meta={}) -> ActionResponse:
|
|
83
|
+
obj = params["plotly_figure_config"]
|
|
84
|
+
if isinstance(obj, str):
|
|
85
|
+
# Remove any leading/trailing quote characters
|
|
86
|
+
obj = obj.strip("'\" ")
|
|
87
|
+
try:
|
|
88
|
+
obj = json.loads(obj)
|
|
89
|
+
except: # pylint: disable=bare-except
|
|
90
|
+
try:
|
|
91
|
+
obj = yaml.safe_load(obj)
|
|
92
|
+
except Exception: # pylint: disable=broad-except
|
|
93
|
+
return ActionResponse(
|
|
94
|
+
message=f"Could not parse plotly figure configuration: {obj}\n\nNOTE that this action expects a config object, not code.",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
files = []
|
|
98
|
+
try:
|
|
99
|
+
fig = go.Figure(obj)
|
|
100
|
+
byte_io = BytesIO()
|
|
101
|
+
pio.write_image(fig, byte_io)
|
|
102
|
+
byte_io.seek(0)
|
|
103
|
+
file_service = FileService()
|
|
104
|
+
image_name = "generated_graph_" + str(random.randint(100000, 999999)) + ".png"
|
|
105
|
+
image_meta = file_service.upload_from_buffer(
|
|
106
|
+
byte_io.read(),
|
|
107
|
+
image_name,
|
|
108
|
+
meta.get("session_id"),
|
|
109
|
+
data_source="Global Agent - PlotlyGraph Action",
|
|
110
|
+
)
|
|
111
|
+
files.append(image_meta)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
return ActionResponse(
|
|
114
|
+
message="Could not create plotly graph. Please check the plotly figure configuration. plotly error: " + str(e),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return ActionResponse(files=files)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Retrieve File content action"""
|
|
2
|
+
|
|
3
|
+
from solace_ai_connector.common.log import log
|
|
4
|
+
import re
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from ....common.action import Action
|
|
8
|
+
from ....common.action_response import ActionResponse, InlineFile
|
|
9
|
+
from ....services.file_service import FS_PROTOCOL, FS_URL_REGEX, FileService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RetrieveFile(Action):
|
|
13
|
+
def __init__(self, **kwargs):
|
|
14
|
+
super().__init__(
|
|
15
|
+
{
|
|
16
|
+
"name": "retrieve_file",
|
|
17
|
+
"prompt_directive": f"Retrieve the content of a text based file form a {FS_PROTOCOL} URL",
|
|
18
|
+
"params": [
|
|
19
|
+
{
|
|
20
|
+
"name": "url",
|
|
21
|
+
"desc": f'The "{FS_PROTOCOL}://" url of the file (Optionally with query parameters - Without "resolve" parameter)',
|
|
22
|
+
"type": "string",
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"required_scopes": ["global:retrieve_file:read"],
|
|
26
|
+
},
|
|
27
|
+
**kwargs,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def invoke(self, params, meta={}) -> ActionResponse:
|
|
31
|
+
# Extract the URL from the text
|
|
32
|
+
url = params.get("url")
|
|
33
|
+
# validate the URL
|
|
34
|
+
if not url or not re.match(FS_URL_REGEX, url):
|
|
35
|
+
return ActionResponse(message=f"Invalid {FS_PROTOCOL} URL provided.")
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
# Download the file
|
|
39
|
+
file_service = FileService()
|
|
40
|
+
file, _, file_meta = file_service.resolve_url(url, meta.get("session_id"), return_extra=True)
|
|
41
|
+
file = file.decode("utf-8", errors="ignore") if type(file) == bytes else file
|
|
42
|
+
except Exception as e:
|
|
43
|
+
log.error("Failed to download file from the url %s: %s", url, str(e))
|
|
44
|
+
return ActionResponse(message=f"Failed to download the file from the URL: {url}")
|
|
45
|
+
|
|
46
|
+
max_allowed_size = self.get_config("max_allowed_file_retrieve_size", None)
|
|
47
|
+
if max_allowed_size and len(file) > max_allowed_size:
|
|
48
|
+
return ActionResponse(message=f"Error: File too large. File size exceeds the maximum allowed size of {max_allowed_size} bytes.")
|
|
49
|
+
|
|
50
|
+
inline_file = InlineFile(content=file, name=file_meta.get("name", "unknown.txt"))
|
|
51
|
+
return ActionResponse(message=f"Returning content of the file {url}.", inline_files=[inline_file])
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""The agent component for the global actions"""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
from ..base_agent_component import (
|
|
6
|
+
agent_info,
|
|
7
|
+
BaseAgentComponent,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .actions.agent_state_change import AgentStateChangeAction
|
|
11
|
+
from .actions.clear_history import ClearHistory
|
|
12
|
+
from .actions.error_action import ErrorAction
|
|
13
|
+
from .actions.plantuml_diagram import PlantUmlDiagram
|
|
14
|
+
from .actions.plotly_graph import PlotlyGraph
|
|
15
|
+
from .actions.retrieve_file import RetrieveFile
|
|
16
|
+
from .actions.create_file import CreateFile
|
|
17
|
+
from .actions.convert_file_to_markdown import ConvertFileToMarkdown
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
info = copy.deepcopy(agent_info)
|
|
21
|
+
info["agent_name"] = "global"
|
|
22
|
+
info["class_name"] = "GlobalAgentComponent"
|
|
23
|
+
info["description"] = "Global agent."
|
|
24
|
+
info["always_open"] = True
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GlobalAgentComponent(BaseAgentComponent):
|
|
28
|
+
info = info
|
|
29
|
+
actions = [
|
|
30
|
+
AgentStateChangeAction,
|
|
31
|
+
ClearHistory,
|
|
32
|
+
ErrorAction,
|
|
33
|
+
PlantUmlDiagram,
|
|
34
|
+
PlotlyGraph,
|
|
35
|
+
RetrieveFile,
|
|
36
|
+
CreateFile,
|
|
37
|
+
ConvertFileToMarkdown
|
|
38
|
+
]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Image Creation action"""
|
|
2
|
+
|
|
3
|
+
from solace_ai_connector.common.log import log
|
|
4
|
+
import random
|
|
5
|
+
import requests
|
|
6
|
+
import base64
|
|
7
|
+
|
|
8
|
+
from litellm import image_generation
|
|
9
|
+
|
|
10
|
+
from ....common.action import Action
|
|
11
|
+
from ....common.action_response import ActionResponse
|
|
12
|
+
from ....services.file_service import FileService
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ImageCreation(Action):
|
|
16
|
+
|
|
17
|
+
def __init__(self, **kwargs):
|
|
18
|
+
super().__init__(
|
|
19
|
+
{
|
|
20
|
+
"name": "create_image",
|
|
21
|
+
"prompt_directive": "Create an image",
|
|
22
|
+
"params": [
|
|
23
|
+
{
|
|
24
|
+
"name": "image_description",
|
|
25
|
+
"desc": "The prompt to use to create the image",
|
|
26
|
+
"type": "string",
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"required_scopes": ["image_processing:create_image:create"],
|
|
30
|
+
},
|
|
31
|
+
**kwargs,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _get_image_content(self, image_data):
|
|
35
|
+
"""Helper function to extract image content from response data"""
|
|
36
|
+
if 'url' in image_data and image_data['url'] is not None:
|
|
37
|
+
return requests.get(image_data['url'], timeout=10).content
|
|
38
|
+
elif 'b64_json' in image_data and image_data['b64_json'] is not None:
|
|
39
|
+
return base64.b64decode(image_data['b64_json'])
|
|
40
|
+
else:
|
|
41
|
+
raise ValueError("No valid image data found in response")
|
|
42
|
+
|
|
43
|
+
def invoke(self, params, meta={}) -> ActionResponse:
|
|
44
|
+
prompt = params.get("image_description")
|
|
45
|
+
if not prompt:
|
|
46
|
+
return ActionResponse(message="Image description is required")
|
|
47
|
+
try:
|
|
48
|
+
response = image_generation(
|
|
49
|
+
model=self.get_config("image_gen_model") or None,
|
|
50
|
+
api_base=self.get_config("image_gen_endpoint") or None,
|
|
51
|
+
api_key=self.get_config("image_gen_api_key") or None,
|
|
52
|
+
prompt=prompt,
|
|
53
|
+
n=1,
|
|
54
|
+
**self.get_config("image_gen_litellm_config", {}),
|
|
55
|
+
)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
log.error(f"Error generating image:", e)
|
|
58
|
+
return ActionResponse(message=f"Error generating image, {str(e)}")
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
generated_image = self._get_image_content(response.get("data")[0])
|
|
62
|
+
except Exception as e:
|
|
63
|
+
log.error(f"Error retrieving image content:", e)
|
|
64
|
+
return ActionResponse(message=f"Error retrieving image content: {str(e)}")
|
|
65
|
+
|
|
66
|
+
file_service = FileService()
|
|
67
|
+
image_name = "generated_image_" + str(random.randint(100000, 999999)) + ".png"
|
|
68
|
+
image_meta = file_service.upload_from_buffer(
|
|
69
|
+
generated_image,
|
|
70
|
+
image_name,
|
|
71
|
+
meta.get("session_id"),
|
|
72
|
+
data_source="Image Processing Agent - Image Creation Action",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return ActionResponse(files=[image_meta])
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Image Description action"""
|
|
2
|
+
|
|
3
|
+
from solace_ai_connector.common.log import log
|
|
4
|
+
import re
|
|
5
|
+
import random
|
|
6
|
+
import requests
|
|
7
|
+
from io import BytesIO
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from ....common.action import Action
|
|
11
|
+
from ....common.action_response import ActionResponse
|
|
12
|
+
from ....services.file_service import FS_PROTOCOL, FileService
|
|
13
|
+
from ....services.file_service.file_utils import starts_with_fs_url
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DescribeImage(Action):
|
|
17
|
+
|
|
18
|
+
def __init__(self, **kwargs):
|
|
19
|
+
super().__init__(
|
|
20
|
+
{
|
|
21
|
+
"name": "describe_image",
|
|
22
|
+
"prompt_directive": "Describe an image",
|
|
23
|
+
"params": [
|
|
24
|
+
{
|
|
25
|
+
"name": "image_url",
|
|
26
|
+
"desc": f'The "{FS_PROTOCOL}://" or "https://" url of the image to describe',
|
|
27
|
+
"type": "string",
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"required_scopes": ["image_processing:describe_image:create"],
|
|
31
|
+
},
|
|
32
|
+
**kwargs,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def extract_url_from_text(self, text: str) -> str:
|
|
36
|
+
"""
|
|
37
|
+
Extracts the first URL from a given text.
|
|
38
|
+
"""
|
|
39
|
+
url = None
|
|
40
|
+
# URL Regex pattern
|
|
41
|
+
# Starts with https:// or http:// or FS_PROTOCOL://
|
|
42
|
+
# ends with space or newline or end of string
|
|
43
|
+
url_pattern = r"(https?|{}):\/\/\S+".format(FS_PROTOCOL)
|
|
44
|
+
match = re.search(url_pattern, text)
|
|
45
|
+
if match:
|
|
46
|
+
url = match.group()
|
|
47
|
+
return url
|
|
48
|
+
|
|
49
|
+
def download_image(self, image_url: str, session_id: str) -> str:
|
|
50
|
+
"""
|
|
51
|
+
Download the image using the provided image URL.
|
|
52
|
+
"""
|
|
53
|
+
url = image_url
|
|
54
|
+
if not starts_with_fs_url(url):
|
|
55
|
+
# Download the image and upload to the file service
|
|
56
|
+
file_service = FileService()
|
|
57
|
+
byte_buffer = BytesIO(requests.get(image_url).content)
|
|
58
|
+
metadata = file_service.upload_from_buffer(
|
|
59
|
+
byte_buffer.read(),
|
|
60
|
+
str(random.randint(100000, 999999)) + "_image.png",
|
|
61
|
+
session_id,
|
|
62
|
+
data_source="Image Processing Agent - Describe Image Action",
|
|
63
|
+
)
|
|
64
|
+
url = metadata.get("url")
|
|
65
|
+
|
|
66
|
+
base64_image_url = f"{url}?encoding=datauri&resolve=true"
|
|
67
|
+
return base64_image_url
|
|
68
|
+
|
|
69
|
+
def get_image_description(self, base64_image_url: str) -> str:
|
|
70
|
+
"""
|
|
71
|
+
Get the description of the image using the provided image URL.
|
|
72
|
+
"""
|
|
73
|
+
agent = self.get_agent()
|
|
74
|
+
content = agent.do_llm_service_request(
|
|
75
|
+
[
|
|
76
|
+
{
|
|
77
|
+
"role": "user",
|
|
78
|
+
"content": [
|
|
79
|
+
{
|
|
80
|
+
"type": "text",
|
|
81
|
+
"text": "describe the content of this image in detail",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"type": "image_url",
|
|
85
|
+
"image_url": {"url": base64_image_url},
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
resolve_files=True,
|
|
91
|
+
).get("content")
|
|
92
|
+
|
|
93
|
+
return content
|
|
94
|
+
|
|
95
|
+
def invoke(self, params, meta={}) -> ActionResponse:
|
|
96
|
+
log.debug("Describing image: %s", params.get("image_url"))
|
|
97
|
+
# Extract the URL from the text
|
|
98
|
+
url = self.extract_url_from_text(params.get("image_url", ""))
|
|
99
|
+
if not url:
|
|
100
|
+
return ActionResponse(message="No image URL found in the query.")
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
# Download the image
|
|
104
|
+
image_url = self.download_image(url, meta.get("session_id"))
|
|
105
|
+
except Exception as e:
|
|
106
|
+
log.error("Failed to download image from the url %s: %s", url, str(e))
|
|
107
|
+
return ActionResponse(message=f"Failed to download the image from the URL: {url}")
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
# Get the image description
|
|
111
|
+
description = self.get_image_description(image_url)
|
|
112
|
+
return ActionResponse(message=description)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
log.error("Failed to describe image in %s: %s", url, str(e))
|
|
115
|
+
return ActionResponse(message=f"Failed to describe the image in {url}.")
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Responsible for processing the actions from the Image Processing Agent"""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
from ..base_agent_component import (
|
|
6
|
+
agent_info,
|
|
7
|
+
BaseAgentComponent,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .actions.create_image import ImageCreation
|
|
11
|
+
from .actions.describe_image import DescribeImage
|
|
12
|
+
|
|
13
|
+
info = copy.deepcopy(agent_info)
|
|
14
|
+
info["agent_name"] = "image_processing"
|
|
15
|
+
info["class_name"] = "ImageProcessingAgentComponent"
|
|
16
|
+
info["description"] = "Image Processing agent. This can generate images from text or describe images to text"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ImageProcessingAgentComponent(
|
|
20
|
+
BaseAgentComponent
|
|
21
|
+
):
|
|
22
|
+
info = info
|
|
23
|
+
actions = [ImageCreation, DescribeImage]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Slack agent package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Slack agent actions."""
|