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,322 @@
|
|
|
1
|
+
# Add utility functions for upload CSV that automatically creates the number of row and data types
|
|
2
|
+
import csv
|
|
3
|
+
from typing import Tuple, Union
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
from .file_service_constants import FS_PROTOCOL, INDENT_SIZE
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Types:
|
|
10
|
+
"""
|
|
11
|
+
Type constants definition to be used when representing the schema.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
INT = "int"
|
|
15
|
+
FLOAT = "float"
|
|
16
|
+
BOOL = "bool"
|
|
17
|
+
STR = "str"
|
|
18
|
+
ARRAY = "array"
|
|
19
|
+
OBJECT = "object"
|
|
20
|
+
NULL = "null"
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def PRIMITIVES():
|
|
24
|
+
return [Types.INT, Types.FLOAT, Types.BOOL, Types.STR]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_str_type(value: str) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Get the type of a string value.
|
|
30
|
+
|
|
31
|
+
Parameters:
|
|
32
|
+
- value (str): The string value.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
- str: The type of the value.
|
|
36
|
+
"""
|
|
37
|
+
if value.isdigit():
|
|
38
|
+
return Types.INT
|
|
39
|
+
elif value.replace(".", "", 1).isdigit():
|
|
40
|
+
return Types.FLOAT
|
|
41
|
+
else:
|
|
42
|
+
return Types.STR
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def convert_dict_to_yaml(schema: dict, indent_size=INDENT_SIZE, _level=0) -> str:
|
|
46
|
+
"""
|
|
47
|
+
Convert a dictionary schema to a YAML string.
|
|
48
|
+
|
|
49
|
+
Parameters:
|
|
50
|
+
- schema (dict): The dictionary schema.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
- str: The YAML string.
|
|
54
|
+
"""
|
|
55
|
+
yaml_str = ""
|
|
56
|
+
indent = indent_size * _level
|
|
57
|
+
|
|
58
|
+
for key, value in schema.items():
|
|
59
|
+
if isinstance(value, dict):
|
|
60
|
+
value_str = convert_dict_to_yaml(value, indent_size, _level + 1)
|
|
61
|
+
yaml_str += f"{space(indent)}{key}:\n{value_str}\n"
|
|
62
|
+
else:
|
|
63
|
+
yaml_str += f"{space(indent)}{key}: {value}\n"
|
|
64
|
+
return yaml_str.rstrip()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def space(size=INDENT_SIZE):
|
|
68
|
+
"""
|
|
69
|
+
Get a string of spaces.
|
|
70
|
+
"""
|
|
71
|
+
if size == 0:
|
|
72
|
+
return ""
|
|
73
|
+
return " " * size
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def extract_csv_schema_and_shape(csv_file: Union[str, bytes]) -> Tuple[dict, Tuple[int, int]]:
|
|
77
|
+
"""
|
|
78
|
+
Get the schema and shape of a CSV file.
|
|
79
|
+
|
|
80
|
+
Parameters:
|
|
81
|
+
- csv_file (str): The CSV file content.
|
|
82
|
+
- header_row (str): The header row of the CSV file.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
- str: The schema of the CSV file.
|
|
86
|
+
- st: The shape of the CSV file.
|
|
87
|
+
"""
|
|
88
|
+
if type(csv_file) == bytes:
|
|
89
|
+
csv_file = csv_file.decode("utf-8")
|
|
90
|
+
|
|
91
|
+
# Parse the CSV string
|
|
92
|
+
csv_lines = csv_file.splitlines()
|
|
93
|
+
csv_reader = csv.DictReader(csv_lines)
|
|
94
|
+
columns = csv_reader.fieldnames
|
|
95
|
+
listed_csv = list(csv_reader)
|
|
96
|
+
shape = (len(listed_csv), len(columns))
|
|
97
|
+
schema = {}
|
|
98
|
+
for col in columns:
|
|
99
|
+
# deriving type from the top 10 rows
|
|
100
|
+
col_type = None
|
|
101
|
+
for i, row in enumerate(listed_csv):
|
|
102
|
+
if i >= 10:
|
|
103
|
+
break
|
|
104
|
+
curr_type = get_str_type(row[col])
|
|
105
|
+
if col_type is None:
|
|
106
|
+
col_type = curr_type
|
|
107
|
+
elif col_type != curr_type:
|
|
108
|
+
# multiple types, set as str
|
|
109
|
+
col_type = Types.STR
|
|
110
|
+
break
|
|
111
|
+
schema[col] = {"type": col_type}
|
|
112
|
+
|
|
113
|
+
return schema, shape
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def dict_to_schema(input_dict: dict) -> dict:
|
|
117
|
+
"""
|
|
118
|
+
Convert a dictionary to a JSON schema.
|
|
119
|
+
|
|
120
|
+
Parameters:
|
|
121
|
+
- input_dict (dict): The input dictionary.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
- dict: The JSON schema.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def infer_type(value):
|
|
128
|
+
if isinstance(value, str):
|
|
129
|
+
return Types.STR
|
|
130
|
+
elif isinstance(value, int):
|
|
131
|
+
return Types.INT
|
|
132
|
+
elif isinstance(value, float):
|
|
133
|
+
return Types.FLOAT
|
|
134
|
+
elif isinstance(value, bool):
|
|
135
|
+
return Types.BOOL
|
|
136
|
+
elif isinstance(value, list):
|
|
137
|
+
if len(value) > 0:
|
|
138
|
+
return {"type": Types.ARRAY, "items": infer_type(value[0])}
|
|
139
|
+
else:
|
|
140
|
+
return {"type": Types.ARRAY, "items": {}}
|
|
141
|
+
elif isinstance(value, dict):
|
|
142
|
+
return {"type": Types.OBJECT, "properties": infer_properties(value)}
|
|
143
|
+
else:
|
|
144
|
+
return Types.NULL
|
|
145
|
+
|
|
146
|
+
def infer_properties(input_dict):
|
|
147
|
+
return {
|
|
148
|
+
key: (
|
|
149
|
+
{"type": infer_type(value)}
|
|
150
|
+
if isinstance(infer_type(value), str)
|
|
151
|
+
else infer_type(value)
|
|
152
|
+
)
|
|
153
|
+
for key, value in input_dict.items()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if type(input_dict) is list:
|
|
157
|
+
input_dict = input_dict[0]
|
|
158
|
+
json_schema = {
|
|
159
|
+
"type": Types.ARRAY,
|
|
160
|
+
"items": {"type": Types.OBJECT, "properties": infer_properties(input_dict)},
|
|
161
|
+
}
|
|
162
|
+
else:
|
|
163
|
+
json_schema = {"type": Types.OBJECT, "properties": infer_properties(input_dict)}
|
|
164
|
+
|
|
165
|
+
return json_schema
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_dict_array_shape(
|
|
169
|
+
data: Union[dict, list], max_level=2, _path="", _result=None, _level=0
|
|
170
|
+
):
|
|
171
|
+
"""
|
|
172
|
+
Get the count of each array in a dictionary schema.
|
|
173
|
+
"""
|
|
174
|
+
if _result is None:
|
|
175
|
+
_result = []
|
|
176
|
+
|
|
177
|
+
# If it's a list, record its length and traverse each element
|
|
178
|
+
if isinstance(data, list):
|
|
179
|
+
if _path:
|
|
180
|
+
_result.append([_path, len(data)])
|
|
181
|
+
else:
|
|
182
|
+
_result.append(["top level", len(data)])
|
|
183
|
+
if _level < max_level:
|
|
184
|
+
get_dict_array_shape(data[0], max_level, f"{_path}[*]", _result, _level + 1)
|
|
185
|
+
|
|
186
|
+
# If it's a dictionary, traverse each key-value pair
|
|
187
|
+
elif isinstance(data, dict):
|
|
188
|
+
for key, value in data.items():
|
|
189
|
+
new_path = f"{_path}.{key}" if _path else key
|
|
190
|
+
if _level < max_level:
|
|
191
|
+
get_dict_array_shape(value, max_level, new_path, _result, _level + 1)
|
|
192
|
+
|
|
193
|
+
return _result
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def get_csv_schema_and_shape(schema: dict, shape: Tuple[int, int]) -> Tuple[str, str]:
|
|
197
|
+
"""
|
|
198
|
+
Get the formatted schema and shape of a CSV file.
|
|
199
|
+
|
|
200
|
+
Parameters:
|
|
201
|
+
- schema (dict): The schema of the CSV file.
|
|
202
|
+
- shape (Tuple[int, int]): The shape of the CSV file.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
- str: The formatted schema of the CSV file.
|
|
206
|
+
- Tuple[int, int]: The shape of the CSV file.
|
|
207
|
+
"""
|
|
208
|
+
shape_str = f"{shape[0]} rows x {shape[1]} columns"
|
|
209
|
+
schema_dict = {
|
|
210
|
+
"type": Types.ARRAY,
|
|
211
|
+
"items": {"type": Types.OBJECT, "properties": schema},
|
|
212
|
+
}
|
|
213
|
+
schema_str = convert_dict_to_yaml(schema_dict)
|
|
214
|
+
return schema_str, shape_str
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def get_json_schema_and_shape(file: Union[bytes, str]) -> Tuple[str, str]:
|
|
218
|
+
try:
|
|
219
|
+
if type(file) == bytes:
|
|
220
|
+
file = file.decode("utf-8")
|
|
221
|
+
json_data = json.loads(file)
|
|
222
|
+
schema = dict_to_schema(json_data)
|
|
223
|
+
schema_str = convert_dict_to_yaml(schema)
|
|
224
|
+
shape_str = None
|
|
225
|
+
|
|
226
|
+
def get_row_text(count):
|
|
227
|
+
return f"{count} elements" if count > 1 else f"{count} element"
|
|
228
|
+
|
|
229
|
+
array_lengths = get_dict_array_shape(json_data)
|
|
230
|
+
if array_lengths:
|
|
231
|
+
shape_str = "\n".join(
|
|
232
|
+
[f"{key}: {get_row_text(val)}" for key, val in array_lengths]
|
|
233
|
+
)
|
|
234
|
+
return schema_str, shape_str
|
|
235
|
+
except Exception as e:
|
|
236
|
+
return None, None
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def get_file_schema_and_shape(file: bytes, metadata: dict) -> Tuple[str, str]:
|
|
240
|
+
"""
|
|
241
|
+
Get the schema and shape of a file.
|
|
242
|
+
|
|
243
|
+
Parameters:
|
|
244
|
+
- file (bytes): The file content as bytes.
|
|
245
|
+
- metadata (dict): The file metadata.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
- str: The schema of the file. None if cannot be determined.
|
|
249
|
+
- str: The shape of the file. None if cannot be determined.
|
|
250
|
+
"""
|
|
251
|
+
if metadata.get("mime_type") == "text/csv":
|
|
252
|
+
schema, shape = extract_csv_schema_and_shape(file)
|
|
253
|
+
schema_str, shape_str = get_csv_schema_and_shape(schema, shape)
|
|
254
|
+
return schema_str, shape_str
|
|
255
|
+
elif metadata.get("mime_type") == "application/json":
|
|
256
|
+
schema, shape = get_json_schema_and_shape(file)
|
|
257
|
+
return schema, shape
|
|
258
|
+
else:
|
|
259
|
+
return None, None
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def recursive_file_resolver(
|
|
263
|
+
obj: Union[dict, str, list],
|
|
264
|
+
resolver: callable,
|
|
265
|
+
session_id: str,
|
|
266
|
+
force_resolve: bool = False,
|
|
267
|
+
):
|
|
268
|
+
"""
|
|
269
|
+
Recursively resolve all the file URLs in the input data.
|
|
270
|
+
|
|
271
|
+
Parameters:
|
|
272
|
+
- obj (dict|str|list): The input data.
|
|
273
|
+
- resolver (callable): The resolver function.
|
|
274
|
+
- session_id (str): The session ID.
|
|
275
|
+
- force_resolve (bool): Resolve all the URLs regardless of the 'resolve' query parameter.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
- dict|str|list: The object with resolved URLs.
|
|
279
|
+
"""
|
|
280
|
+
if type(obj) == str:
|
|
281
|
+
return resolver(obj, session_id, force_resolve)
|
|
282
|
+
|
|
283
|
+
keys = []
|
|
284
|
+
if type(obj) == list:
|
|
285
|
+
keys = range(len(obj))
|
|
286
|
+
elif type(obj) == dict:
|
|
287
|
+
keys = obj.keys()
|
|
288
|
+
|
|
289
|
+
for key in keys:
|
|
290
|
+
if isinstance(obj[key], dict):
|
|
291
|
+
obj[key] = recursive_file_resolver(
|
|
292
|
+
obj[key], resolver, session_id, force_resolve
|
|
293
|
+
)
|
|
294
|
+
elif isinstance(obj[key], list):
|
|
295
|
+
obj[key] = [
|
|
296
|
+
(
|
|
297
|
+
recursive_file_resolver(item, resolver, session_id, force_resolve)
|
|
298
|
+
if isinstance(item, dict)
|
|
299
|
+
else item
|
|
300
|
+
)
|
|
301
|
+
for item in obj[key]
|
|
302
|
+
]
|
|
303
|
+
elif isinstance(obj[key], str):
|
|
304
|
+
obj[key] = resolver(obj[key], session_id, force_resolve)
|
|
305
|
+
return obj
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def starts_with_fs_url(url: str) -> bool:
|
|
309
|
+
"""
|
|
310
|
+
Check if the URL starts with the file service protocol.
|
|
311
|
+
|
|
312
|
+
Parameters:
|
|
313
|
+
- url (str): The URL.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
- bool: True if the URL starts with the file service protocol.
|
|
317
|
+
"""
|
|
318
|
+
return (
|
|
319
|
+
url.startswith(f"{FS_PROTOCOL}://")
|
|
320
|
+
or url.startswith(f"<url>{FS_PROTOCOL}://")
|
|
321
|
+
or url.startswith(f"<url> {FS_PROTOCOL}://")
|
|
322
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
class BaseHistoryProvider(ABC):
|
|
5
|
+
|
|
6
|
+
def __init__(self, config=None):
|
|
7
|
+
self.config = config
|
|
8
|
+
self.max_turns = self.config.get("max_turns")
|
|
9
|
+
self.max_characters = self.config.get("max_characters")
|
|
10
|
+
self.enforce_alternate_message_roles = self.config.get("enforce_alternate_message_roles")
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def store_history(self, session_id: str, role: str, content: Union[str, dict]):
|
|
14
|
+
"""
|
|
15
|
+
Store a new entry in the history.
|
|
16
|
+
|
|
17
|
+
:param session_id: The session identifier.
|
|
18
|
+
:param role: The role of the entry to be stored in the history.
|
|
19
|
+
:param content: The content of the entry to be stored in the history.
|
|
20
|
+
"""
|
|
21
|
+
raise NotImplementedError("Method not implemented")
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def get_history(self, session_id: str):
|
|
25
|
+
"""
|
|
26
|
+
Retrieve the entire history.
|
|
27
|
+
|
|
28
|
+
:param session_id: The session identifier.
|
|
29
|
+
:return: The complete history.
|
|
30
|
+
"""
|
|
31
|
+
raise NotImplementedError("Method not implemented")
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def store_file(self, session_id: str, file: dict):
|
|
35
|
+
"""
|
|
36
|
+
Store a file in the history.
|
|
37
|
+
|
|
38
|
+
:param session_id: The session identifier.
|
|
39
|
+
:param file: The file metadata to be stored in the history.
|
|
40
|
+
"""
|
|
41
|
+
raise NotImplementedError("Method not implemented")
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def get_files(self, session_id: str):
|
|
45
|
+
"""
|
|
46
|
+
Retrieve the files for a session.
|
|
47
|
+
|
|
48
|
+
:param session_id: The session identifier.
|
|
49
|
+
:return: The files for the session.
|
|
50
|
+
"""
|
|
51
|
+
raise NotImplementedError("Method not implemented")
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def get_session_meta(self, session_id: str):
|
|
55
|
+
"""
|
|
56
|
+
Retrieve the session metadata.
|
|
57
|
+
|
|
58
|
+
:param session_id: The session identifier.
|
|
59
|
+
:return: The session metadata.
|
|
60
|
+
"""
|
|
61
|
+
raise NotImplementedError("Method not implemented")
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def clear_history(self, session_id: str, keep_levels=0):
|
|
65
|
+
"""
|
|
66
|
+
Clear the history and files, optionally keeping a specified number of recent entries.
|
|
67
|
+
|
|
68
|
+
:param session_id: The session identifier.
|
|
69
|
+
:param keep_levels: Number of most recent history entries to keep. Default is 0 (clear all).
|
|
70
|
+
"""
|
|
71
|
+
raise NotImplementedError("Method not implemented")
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def get_all_sessions(self) -> list[str]:
|
|
75
|
+
"""
|
|
76
|
+
Retrieve all session identifiers.
|
|
77
|
+
"""
|
|
78
|
+
raise NotImplementedError("Method not implemented")
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from .base_history_provider import BaseHistoryProvider
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MemoryHistoryProvider(BaseHistoryProvider):
|
|
6
|
+
def __init__(self, config=None):
|
|
7
|
+
super().__init__(config)
|
|
8
|
+
self.history = {}
|
|
9
|
+
|
|
10
|
+
def store_history(self, session_id: str, role: str, content: str | dict):
|
|
11
|
+
"""
|
|
12
|
+
Store a new entry in the history.
|
|
13
|
+
|
|
14
|
+
:param session_id: The session identifier.
|
|
15
|
+
:param history_entry: The entry to be stored in the history.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
if session_id not in self.history:
|
|
19
|
+
self.history[session_id] = {
|
|
20
|
+
"history": [],
|
|
21
|
+
"files": [],
|
|
22
|
+
"last_active_time": time.time(),
|
|
23
|
+
"num_characters": 0,
|
|
24
|
+
"num_turns": 0,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Check if adding another entry would exceed the max_turns
|
|
28
|
+
if self.history[session_id]["num_turns"] == self.max_turns:
|
|
29
|
+
# Remove the oldest entry
|
|
30
|
+
oldest_entry = self.history[session_id]["history"].pop(0)
|
|
31
|
+
# Subtract the length of the oldest entry from the total length
|
|
32
|
+
self.history[session_id]["num_characters"] -= len(
|
|
33
|
+
str(oldest_entry["content"])
|
|
34
|
+
)
|
|
35
|
+
self.history[session_id]["num_turns"] -= 1
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
self.enforce_alternate_message_roles
|
|
39
|
+
and self.history[session_id]["num_turns"] > 0
|
|
40
|
+
# Check if the last entry was by the same role
|
|
41
|
+
and self.history[session_id]["history"]
|
|
42
|
+
and self.history[session_id]["history"][-1]["role"] == role
|
|
43
|
+
):
|
|
44
|
+
# Append to last entry
|
|
45
|
+
self.history[session_id]["history"][-1]["content"] += content
|
|
46
|
+
else:
|
|
47
|
+
# Add the new entry
|
|
48
|
+
self.history[session_id]["history"].append(
|
|
49
|
+
{"role": role, "content": content}
|
|
50
|
+
)
|
|
51
|
+
# Update the number of turns
|
|
52
|
+
self.history[session_id]["num_turns"] += 1
|
|
53
|
+
|
|
54
|
+
# Update the length
|
|
55
|
+
self.history[session_id]["num_characters"] += len(str(content))
|
|
56
|
+
# Update the last active time
|
|
57
|
+
self.history[session_id]["last_active_time"] = time.time()
|
|
58
|
+
|
|
59
|
+
# Check if we have exceeded max_characters
|
|
60
|
+
if self.max_characters:
|
|
61
|
+
while (
|
|
62
|
+
self.history[session_id]["num_characters"] > self.max_characters
|
|
63
|
+
and self.history[session_id]["num_turns"] > 0
|
|
64
|
+
):
|
|
65
|
+
# Remove the oldest entry
|
|
66
|
+
oldest_entry = self.history[session_id]["history"].pop(0)
|
|
67
|
+
# Subtract the length of the oldest entry from the total length
|
|
68
|
+
self.history[session_id]["num_characters"] -= len(
|
|
69
|
+
str(oldest_entry["content"])
|
|
70
|
+
)
|
|
71
|
+
self.history[session_id]["num_turns"] -= 1
|
|
72
|
+
|
|
73
|
+
def get_history(self, session_id: str):
|
|
74
|
+
"""
|
|
75
|
+
Retrieve the entire history for a session.
|
|
76
|
+
|
|
77
|
+
:param session_id: The session identifier.
|
|
78
|
+
:return: The complete history for the session.
|
|
79
|
+
"""
|
|
80
|
+
if session_id not in self.history:
|
|
81
|
+
return []
|
|
82
|
+
return self.history.get(session_id)["history"]
|
|
83
|
+
|
|
84
|
+
def store_file(self, session_id: str, file: dict):
|
|
85
|
+
"""
|
|
86
|
+
Store a file in the history.
|
|
87
|
+
|
|
88
|
+
:param session_id: The session identifier.
|
|
89
|
+
:param file: The file metadata to be stored in the history.
|
|
90
|
+
"""
|
|
91
|
+
if session_id not in self.history:
|
|
92
|
+
self.history[session_id] = {
|
|
93
|
+
"history": [],
|
|
94
|
+
"files": [],
|
|
95
|
+
"last_active_time": time.time(),
|
|
96
|
+
"num_characters": 0,
|
|
97
|
+
"num_turns": 0,
|
|
98
|
+
}
|
|
99
|
+
self.history[session_id]["last_active_time"] = time.time()
|
|
100
|
+
|
|
101
|
+
# Check duplicate
|
|
102
|
+
for f in self.history[session_id]["files"]:
|
|
103
|
+
if f.get("url") and f.get("url") == file.get("url"):
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
self.history[session_id]["files"].append(file)
|
|
107
|
+
|
|
108
|
+
def get_files(self, session_id: str):
|
|
109
|
+
"""
|
|
110
|
+
Retrieve the files for a session.
|
|
111
|
+
|
|
112
|
+
:param session_id: The session identifier.
|
|
113
|
+
:return: The files for the session.
|
|
114
|
+
"""
|
|
115
|
+
if session_id not in self.history:
|
|
116
|
+
return []
|
|
117
|
+
files = []
|
|
118
|
+
current_time = time.time()
|
|
119
|
+
all_files = self.history.get(session_id)["files"].copy()
|
|
120
|
+
for file in all_files:
|
|
121
|
+
expiration_timestamp = file.get("expiration_timestamp")
|
|
122
|
+
if expiration_timestamp and current_time > expiration_timestamp:
|
|
123
|
+
self.history[session_id]["files"].remove(file)
|
|
124
|
+
continue
|
|
125
|
+
files.append(file)
|
|
126
|
+
return files
|
|
127
|
+
|
|
128
|
+
def clear_history(self, session_id: str, keep_levels=0):
|
|
129
|
+
"""
|
|
130
|
+
Clear the history for a session, optionally keeping a specified number of recent entries.
|
|
131
|
+
|
|
132
|
+
:param session_id: The session identifier.
|
|
133
|
+
:param keep_levels: Number of most recent history entries to keep. Default is 0 (clear all).
|
|
134
|
+
"""
|
|
135
|
+
if session_id in self.history:
|
|
136
|
+
if keep_levels <= 0:
|
|
137
|
+
del self.history[session_id]
|
|
138
|
+
else:
|
|
139
|
+
self.history[session_id]["history"] = self.history[session_id][
|
|
140
|
+
"history"
|
|
141
|
+
][-keep_levels:]
|
|
142
|
+
# Recalculate the length and num_turns
|
|
143
|
+
self.history[session_id]["num_characters"] = sum(
|
|
144
|
+
len(str(entry)) for entry in self.history[session_id]["history"]
|
|
145
|
+
)
|
|
146
|
+
self.history[session_id]["num_turns"] = len(
|
|
147
|
+
self.history[session_id]["history"]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def get_session_meta(self, session_id: str):
|
|
151
|
+
"""
|
|
152
|
+
Retrieve the session metadata.
|
|
153
|
+
|
|
154
|
+
:param session_id: The session identifier.
|
|
155
|
+
:return: The session metadata.
|
|
156
|
+
"""
|
|
157
|
+
if session_id in self.history:
|
|
158
|
+
session = self.history[session_id]
|
|
159
|
+
return {
|
|
160
|
+
"num_characters": session["num_characters"],
|
|
161
|
+
"num_turns": session["num_turns"],
|
|
162
|
+
"last_active_time": session["last_active_time"],
|
|
163
|
+
}
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
def get_all_sessions(self) -> list[str]:
|
|
167
|
+
return list(self.history.keys())
|