MindsDB 25.7.4.0__py3-none-any.whl → 25.8.3.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 MindsDB might be problematic. Click here for more details.

Files changed (65) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +13 -1
  3. mindsdb/api/a2a/agent.py +6 -16
  4. mindsdb/api/a2a/common/types.py +3 -4
  5. mindsdb/api/a2a/task_manager.py +24 -35
  6. mindsdb/api/a2a/utils.py +63 -0
  7. mindsdb/api/executor/command_executor.py +9 -15
  8. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +21 -24
  9. mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +9 -3
  10. mindsdb/api/executor/sql_query/steps/subselect_step.py +11 -8
  11. mindsdb/api/executor/utilities/mysql_to_duckdb_functions.py +264 -0
  12. mindsdb/api/executor/utilities/sql.py +30 -0
  13. mindsdb/api/http/initialize.py +2 -1
  14. mindsdb/api/http/namespaces/agents.py +6 -7
  15. mindsdb/api/http/namespaces/views.py +56 -72
  16. mindsdb/integrations/handlers/db2_handler/db2_handler.py +19 -23
  17. mindsdb/integrations/handlers/gong_handler/__about__.py +2 -0
  18. mindsdb/integrations/handlers/gong_handler/__init__.py +30 -0
  19. mindsdb/integrations/handlers/gong_handler/connection_args.py +37 -0
  20. mindsdb/integrations/handlers/gong_handler/gong_handler.py +164 -0
  21. mindsdb/integrations/handlers/gong_handler/gong_tables.py +508 -0
  22. mindsdb/integrations/handlers/gong_handler/icon.svg +25 -0
  23. mindsdb/integrations/handlers/gong_handler/test_gong_handler.py +125 -0
  24. mindsdb/integrations/handlers/huggingface_handler/__init__.py +8 -12
  25. mindsdb/integrations/handlers/huggingface_handler/finetune.py +203 -223
  26. mindsdb/integrations/handlers/huggingface_handler/huggingface_handler.py +360 -383
  27. mindsdb/integrations/handlers/huggingface_handler/requirements.txt +7 -7
  28. mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +7 -7
  29. mindsdb/integrations/handlers/huggingface_handler/settings.py +25 -25
  30. mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +1 -2
  31. mindsdb/integrations/handlers/openai_handler/constants.py +11 -30
  32. mindsdb/integrations/handlers/openai_handler/helpers.py +27 -34
  33. mindsdb/integrations/handlers/openai_handler/openai_handler.py +14 -12
  34. mindsdb/integrations/handlers/salesforce_handler/constants.py +9 -2
  35. mindsdb/integrations/libs/llm/config.py +0 -14
  36. mindsdb/integrations/libs/llm/utils.py +0 -15
  37. mindsdb/integrations/utilities/files/file_reader.py +5 -19
  38. mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +1 -1
  39. mindsdb/interfaces/agents/agents_controller.py +83 -45
  40. mindsdb/interfaces/agents/constants.py +16 -3
  41. mindsdb/interfaces/agents/langchain_agent.py +84 -21
  42. mindsdb/interfaces/database/projects.py +111 -7
  43. mindsdb/interfaces/knowledge_base/controller.py +7 -1
  44. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +6 -10
  45. mindsdb/interfaces/knowledge_base/preprocessing/text_splitter.py +73 -0
  46. mindsdb/interfaces/query_context/context_controller.py +14 -15
  47. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +7 -1
  48. mindsdb/interfaces/skills/skill_tool.py +7 -1
  49. mindsdb/interfaces/skills/sql_agent.py +6 -2
  50. mindsdb/utilities/config.py +2 -0
  51. mindsdb/utilities/fs.py +60 -17
  52. {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.3.0.dist-info}/METADATA +277 -262
  53. {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.3.0.dist-info}/RECORD +57 -56
  54. mindsdb/integrations/handlers/anyscale_endpoints_handler/__about__.py +0 -9
  55. mindsdb/integrations/handlers/anyscale_endpoints_handler/__init__.py +0 -20
  56. mindsdb/integrations/handlers/anyscale_endpoints_handler/anyscale_endpoints_handler.py +0 -290
  57. mindsdb/integrations/handlers/anyscale_endpoints_handler/creation_args.py +0 -14
  58. mindsdb/integrations/handlers/anyscale_endpoints_handler/icon.svg +0 -4
  59. mindsdb/integrations/handlers/anyscale_endpoints_handler/requirements.txt +0 -2
  60. mindsdb/integrations/handlers/anyscale_endpoints_handler/settings.py +0 -51
  61. mindsdb/integrations/handlers/anyscale_endpoints_handler/tests/test_anyscale_endpoints_handler.py +0 -212
  62. /mindsdb/integrations/handlers/{anyscale_endpoints_handler/tests/__init__.py → gong_handler/requirements.txt} +0 -0
  63. {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.3.0.dist-info}/WHEEL +0 -0
  64. {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.3.0.dist-info}/licenses/LICENSE +0 -0
  65. {mindsdb-25.7.4.0.dist-info → mindsdb-25.8.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,264 @@
1
+ from mindsdb_sql_parser.ast import Identifier, Function, Constant, BinaryOperation
2
+
3
+
4
+ def adapt_char_fn(node: Function) -> Function | None:
5
+ """Replace MySQL's multy-arg CHAR call to chain of DuckDB's CHR calls
6
+
7
+ Example:
8
+ CHAR(77, 78, 79) => CHR(77) || CHR(78) || CHR(79)
9
+
10
+ Args:
11
+ node (Function): Function node to adapt
12
+
13
+ Returns:
14
+ Function | None: Adapted function node
15
+ """
16
+ if len(node.args) == 1:
17
+ node.op = "chr"
18
+ return node
19
+
20
+ acc = None
21
+ for arg in node.args:
22
+ fn = Function(op="chr", args=[arg])
23
+ if acc is None:
24
+ acc = fn
25
+ continue
26
+ acc = BinaryOperation("||", args=[acc, fn])
27
+
28
+ acc.parentheses = True
29
+ acc.alias = node.alias
30
+ return acc
31
+
32
+
33
+ def adapt_locate_fn(node: Function) -> Function | None:
34
+ """Replace MySQL's LOCATE (or INSTR) call to DuckDB's STRPOS call
35
+
36
+ Example:
37
+ LOCATE('bar', 'foobarbar') => STRPOS('foobarbar', 'bar')
38
+ INSTR('foobarbar', 'bar') => STRPOS('foobarbar', 'bar')
39
+ LOCATE('bar', 'foobarbar', 3) => ValueError (there is no analogue in DuckDB)
40
+
41
+ Args:
42
+ node (Function): Function node to adapt
43
+
44
+ Returns:
45
+ Function | None: Adapted function node
46
+
47
+ Raises:
48
+ ValueError: If the function has 3 arguments
49
+ """
50
+ if len(node.args) == 3:
51
+ raise ValueError("MySQL LOCATE function with 3 arguments is not supported")
52
+ if node.op == "locate":
53
+ node.args = [node.args[1], node.args[0]]
54
+ elif node.op == "insrt":
55
+ node.args = [node.args[0], node.args[1]]
56
+ node.op = "strpos"
57
+
58
+
59
+ def adapt_unhex_fn(node: Function) -> None:
60
+ """Check MySQL's UNHEX function call arguments to ensure they are strings,
61
+ because DuckDB's UNHEX accepts only string arguments, while MySQL's UNHEX can accept integer arguments.
62
+ NOTE: if return dataframe from duckdb then unhex values are array - this may be an issue
63
+
64
+ Args:
65
+ node (Function): Function node to adapt
66
+
67
+ Returns:
68
+ None
69
+
70
+ Raises:
71
+ ValueError: If the function argument is not a string
72
+ """
73
+ for arg in node.args:
74
+ if not isinstance(arg, (str, bytes)):
75
+ raise ValueError("MySQL UNHEX function argument must be a string")
76
+
77
+
78
+ def adapt_format_fn(node: Function) -> None:
79
+ """Adapt MySQL's FORMAT function to DuckDB's FORMAT function
80
+
81
+ Example:
82
+ FORMAT(1234567.89, 0) => FORMAT('{:,.0f}', 1234567.89)
83
+ FORMAT(1234567.89, 2) => FORMAT('{:,.2f}', 1234567.89)
84
+ FORMAT(name, 2) => FORMAT('{:,.2f}', name)
85
+ FORMAT('{:.2f}', 1234567.89) => FORMAT('{:,.2f}', 1234567.89) # no changes for original style
86
+
87
+ Args:
88
+ node (Function): Function node to adapt
89
+
90
+ Returns:
91
+ None
92
+
93
+ Raises:
94
+ ValueError: If MySQL's function has 3rd 'locale' argument, like FORMAT(12332.2, 2, 'de_DE')
95
+ """
96
+ match node.args[0], node.args[1]:
97
+ case Constant(value=(int() | float())), Constant(value=int()):
98
+ ...
99
+ case Identifier(), Constant(value=int()):
100
+ ...
101
+ case _:
102
+ return node
103
+
104
+ if len(node.args) > 2:
105
+ raise ValueError("'locale' argument of 'format' function is not supported")
106
+ decimal_places = node.args[1].value
107
+
108
+ if isinstance(node.args[0], Constant):
109
+ node.args[1].value = node.args[0].value
110
+ node.args[0].value = f"{{:,.{decimal_places}f}}"
111
+ else:
112
+ node.args[1] = node.args[0]
113
+ node.args[0] = Constant(f"{{:,.{decimal_places}f}}")
114
+
115
+
116
+ def adapt_sha2_fn(node: Function) -> None:
117
+ """Adapt MySQL's SHA2 function to DuckDB's SHA256 function
118
+
119
+ Example:
120
+ SHA2('test', 256) => SHA256('test')
121
+
122
+ Args:
123
+ node (Function): Function node to adapt
124
+
125
+ Returns:
126
+ None
127
+
128
+ Raises:
129
+ ValueError: If the function has more than 1 argument or the argument is not 256
130
+ """
131
+ if len(node.args) > 1 and node.args[1].value != 256:
132
+ raise ValueError("Only sha256 is supported")
133
+ node.op = "sha256"
134
+ node.args = [node.args[0]]
135
+
136
+
137
+ def adapt_length_fn(node: Function) -> None:
138
+ """Adapt MySQL's LENGTH function to DuckDB's STRLEN function
139
+ NOTE: duckdb also have LENGTH, therefore it can not be used
140
+
141
+ Example:
142
+ LENGTH('test') => STRLEN('test')
143
+
144
+ Args:
145
+ node (Function): Function node to adapt
146
+
147
+ Returns:
148
+ None
149
+ """
150
+ node.op = "strlen"
151
+
152
+
153
+ def adapt_regexp_substr_fn(node: Function) -> None:
154
+ """Adapt MySQL's REGEXP_SUBSTR function to DuckDB's REGEXP_EXTRACT function
155
+
156
+ Example:
157
+ REGEXP_SUBSTR('foobarbar', 'bar', 1, 1) => REGEXP_EXTRACT('foobarbar', 'bar')
158
+
159
+ Args:
160
+ node (Function): Function node to adapt
161
+
162
+ Returns:
163
+ None
164
+
165
+ Raises:
166
+ ValueError: If the function has more than 2 arguments or 3rd or 4th argument is not 1
167
+ """
168
+ if (
169
+ len(node.args) == 3
170
+ and node.args[2].value != 1
171
+ or len(node.args) == 4
172
+ and (node.args[3].value != 1 or node.args[2].value != 1)
173
+ or len(node.args) > 4
174
+ ):
175
+ raise ValueError("Only 2 arguments are supported for REGEXP_SUBSTR function")
176
+ node.args = node.args[:2]
177
+ node.op = "regexp_extract"
178
+
179
+
180
+ def adapt_substring_index_fn(node: Function) -> BinaryOperation | Function:
181
+ """Adapt MySQL's SUBSTRING_INDEX function to DuckDB's SPLIT_PART function
182
+
183
+ Example:
184
+ SUBSTRING_INDEX('a.b.c.d', '.', 1) => SPLIT_PART('a.b.c.d', '.', 1)
185
+ SUBSTRING_INDEX('a.b.c.d', '.', 2) => CONCAT_WS('.', SPLIT_PART('a.b.c.d', '.', 1), SPLIT_PART('a.b.c.d', '.', 2))
186
+
187
+ Args:
188
+ node (Function): Function node to adapt
189
+
190
+ Returns:
191
+ BinaryOperation | Function: Binary operation node or function node
192
+
193
+ Raises:
194
+ ValueError: If the function has more than 3 arguments or the 3rd argument is not 1
195
+ """
196
+ if len(node.args[1].value) > 1:
197
+ raise ValueError("Only one car in separator")
198
+
199
+ if node.args[2].value == 1:
200
+ node.op = "split_part"
201
+ return node
202
+
203
+ acc = [node.args[1]]
204
+ for i in range(node.args[2].value):
205
+ fn = Function(op="split_part", args=[node.args[0], node.args[1], Constant(i + 1)])
206
+ acc.append(fn)
207
+
208
+ acc = Function(op="concat_ws", args=acc)
209
+ acc.alias = node.alias
210
+ return acc
211
+
212
+
213
+ def adapt_curtime_fn(node: Function) -> BinaryOperation:
214
+ """Adapt MySQL's CURTIME function to DuckDB's GET_CURRENT_TIME function.
215
+ To get the same type as MySQL's CURTIME function, we need to cast the result to time type.
216
+
217
+ Example:
218
+ CURTIME() => GET_CURRENT_TIME()::time
219
+
220
+ Args:
221
+ node (Function): Function node to adapt
222
+
223
+ Returns:
224
+ BinaryOperation: Binary operation node
225
+ """
226
+ return BinaryOperation("::", args=[Function(op="get_current_time", args=[]), Identifier("time")], alias=node.alias)
227
+
228
+
229
+ def adapt_timestampdiff_fn(node: Function) -> None:
230
+ """Adapt MySQL's TIMESTAMPDIFF function to DuckDB's DATE_DIFF function
231
+ NOTE: Looks like cast string args to timestamp works in most cases, but there may be some exceptions.
232
+
233
+ Example:
234
+ TIMESTAMPDIFF(YEAR, '2000-02-01', '2003-05-01') => DATE_DIFF('year', timestamp '2000-02-01', timestamp '2003-05-01')
235
+
236
+ Args:
237
+ node (Function): Function node to adapt
238
+
239
+ Returns:
240
+ None
241
+ """
242
+ node.op = "date_diff"
243
+ node.args[0] = Constant(node.args[0].parts[0])
244
+ node.args[1] = BinaryOperation(" ", args=[Identifier("timestamp"), node.args[1]])
245
+ node.args[2] = BinaryOperation(" ", args=[Identifier("timestamp"), node.args[2]])
246
+
247
+
248
+ def adapt_extract_fn(node: Function) -> None:
249
+ """Adapt MySQL's EXTRACT function to DuckDB's EXTRACT function
250
+ TODO: multi-part args, like YEAR_MONTH, is not supported yet
251
+ NOTE: Looks like adding 'timestamp' works in most cases, but there may be some exceptions.
252
+
253
+ Example:
254
+ EXTRACT(YEAR FROM '2000-02-01') => EXTRACT('year' from timestamp '2000-02-01')
255
+
256
+ Args:
257
+ node (Function): Function node to adapt
258
+
259
+ Returns:
260
+ None
261
+ """
262
+ node.args[0] = Constant(node.args[0].parts[0])
263
+ if not isinstance(node.from_arg, Identifier):
264
+ node.from_arg = BinaryOperation(" ", args=[Identifier("timestamp"), node.from_arg])
@@ -14,6 +14,19 @@ from mindsdb.utilities.exception import format_db_error_message
14
14
  from mindsdb.utilities.functions import resolve_table_identifier, resolve_model_identifier
15
15
  from mindsdb.utilities.json_encoder import CustomJSONEncoder
16
16
  from mindsdb.utilities.render.sqlalchemy_render import SqlalchemyRender
17
+ from mindsdb.api.executor.utilities.mysql_to_duckdb_functions import (
18
+ adapt_char_fn,
19
+ adapt_locate_fn,
20
+ adapt_unhex_fn,
21
+ adapt_format_fn,
22
+ adapt_sha2_fn,
23
+ adapt_length_fn,
24
+ adapt_regexp_substr_fn,
25
+ adapt_substring_index_fn,
26
+ adapt_curtime_fn,
27
+ adapt_timestampdiff_fn,
28
+ adapt_extract_fn,
29
+ )
17
30
 
18
31
  logger = log.getLogger(__name__)
19
32
 
@@ -185,6 +198,23 @@ def query_df(df, query, session=None):
185
198
  if isinstance(node, Function):
186
199
  fnc_name = node.op.lower()
187
200
 
201
+ mysql_to_duck_fn_map = {
202
+ "char": adapt_char_fn,
203
+ "locate": adapt_locate_fn,
204
+ "insrt": adapt_locate_fn,
205
+ "unhex": adapt_unhex_fn,
206
+ "format": adapt_format_fn,
207
+ "sha2": adapt_sha2_fn,
208
+ "length": adapt_length_fn,
209
+ "regexp_substr": adapt_regexp_substr_fn,
210
+ "substring_index": adapt_substring_index_fn,
211
+ "curtime": adapt_curtime_fn,
212
+ "timestampdiff": adapt_timestampdiff_fn,
213
+ "extract": adapt_extract_fn,
214
+ }
215
+ if fnc_name in mysql_to_duck_fn_map:
216
+ return mysql_to_duck_fn_map[fnc_name](node)
217
+
188
218
  if fnc_name == "database" and len(node.args) == 0:
189
219
  if session is not None:
190
220
  cur_db = session.database
@@ -203,7 +203,8 @@ def initialize_static():
203
203
  logger.debug("Updating gui..")
204
204
  success = update_static(last_gui_version_lv)
205
205
 
206
- db.session.close()
206
+ if db.session:
207
+ db.session.close()
207
208
  return success
208
209
 
209
210
 
@@ -323,15 +323,16 @@ class AgentCompletionsStream(Resource):
323
323
  @ns_conf.doc("agent_completions_stream")
324
324
  @api_endpoint_metrics("POST", "/agents/agent/completions/stream")
325
325
  def post(self, project_name, agent_name):
326
- logger.info(f"Received streaming request for agent {agent_name} in project {project_name}")
327
-
328
- # Check for required parameters.
326
+ # Extract messages from request (HTTP format only)
329
327
  if "messages" not in request.json:
330
- logger.error("Missing 'messages' parameter in request body")
331
328
  return http_error(
332
- HTTPStatus.BAD_REQUEST, "Missing parameter", 'Must provide "messages" parameter in POST body'
329
+ HTTPStatus.BAD_REQUEST,
330
+ "Missing parameter",
331
+ 'Must provide "messages" parameter in POST body',
333
332
  )
334
333
 
334
+ messages = request.json["messages"]
335
+
335
336
  session = SessionController()
336
337
  try:
337
338
  existing_agent = session.agents_controller.get_agent(agent_name, project_name=project_name)
@@ -346,8 +347,6 @@ class AgentCompletionsStream(Resource):
346
347
  HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
347
348
  )
348
349
 
349
- messages = request.json["messages"]
350
-
351
350
  try:
352
351
  gen = _completion_event_generator(agent_name, messages, project_name)
353
352
  logger.info(f"Starting streaming response for agent {agent_name}")
@@ -10,143 +10,127 @@ from mindsdb.metrics.metrics import api_endpoint_metrics
10
10
  from mindsdb.utilities.exception import EntityNotExistsError
11
11
 
12
12
 
13
- @ns_conf.route('/<project_name>/views')
13
+ @ns_conf.route("/<project_name>/views")
14
14
  class ViewsList(Resource):
15
- @ns_conf.doc('list_views')
16
- @api_endpoint_metrics('GET', '/views')
15
+ @ns_conf.doc("list_views")
16
+ @api_endpoint_metrics("GET", "/views")
17
17
  def get(self, project_name):
18
- '''List all views'''
18
+ """List all views"""
19
19
  session = SessionController()
20
20
  try:
21
21
  project = session.database_controller.get_project(project_name)
22
22
  except EntityNotExistsError:
23
- return http_error(
24
- HTTPStatus.NOT_FOUND,
25
- 'Project not found',
26
- f'Project name {project_name} does not exist'
27
- )
23
+ return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
28
24
 
29
25
  all_views = project.get_views()
30
26
  all_view_objs = []
31
27
  # Only want to return relevant fields to the user.
32
28
  for view in all_views:
33
- all_view_objs.append({
34
- 'id': view['metadata']['id'],
35
- 'name': view['name'],
36
- 'query': view['query']
37
- })
29
+ all_view_objs.append({"id": view["metadata"]["id"], "name": view["name"], "query": view["query"]})
38
30
  return all_view_objs
39
31
 
40
- @ns_conf.doc('create_view')
41
- @api_endpoint_metrics('POST', '/views')
32
+ @ns_conf.doc("create_view")
33
+ @api_endpoint_metrics("POST", "/views")
42
34
  def post(self, project_name):
43
- '''Create a new view'''
44
- if 'view' not in request.json:
45
- return http_error(HTTPStatus.BAD_REQUEST, 'Wrong argument', 'Must provide "view" parameter in POST body')
35
+ """Create a new view"""
36
+ if "view" not in request.json:
37
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Must provide "view" parameter in POST body')
46
38
  session = SessionController()
47
- view_obj = request.json['view']
48
- if 'name' not in view_obj:
49
- return http_error(HTTPStatus.BAD_REQUEST, 'Wrong argument', 'Missing "name" field for view')
50
- if 'query' not in view_obj:
51
- return http_error(HTTPStatus.BAD_REQUEST, 'Wrong argument', 'Missing "query" field for view')
52
- name = view_obj['name']
53
- query = view_obj['query']
39
+ view_obj = request.json["view"]
40
+ if "name" not in view_obj:
41
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "name" field for view')
42
+ if "query" not in view_obj:
43
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "query" field for view')
44
+ name = view_obj["name"]
45
+ query = view_obj["query"]
54
46
 
55
47
  try:
56
48
  project = session.database_controller.get_project(project_name)
57
49
  except EntityNotExistsError:
58
- return http_error(HTTPStatus.NOT_FOUND, 'Not found', f'Project name {project_name} does not exist')
50
+ return http_error(HTTPStatus.NOT_FOUND, "Not found", f"Project name {project_name} does not exist")
59
51
 
60
52
  if project.get_view(name) is not None:
61
- return http_error(HTTPStatus.CONFLICT, 'Name conflict', f'View with name {name} already exists.')
53
+ return http_error(HTTPStatus.CONFLICT, "Name conflict", f"View with name {name} already exists.")
62
54
 
63
- project.create_view(name, query)
55
+ project.create_view(name, query, session)
64
56
  created_view = project.get_view(name)
65
57
  # Only want to return relevant fields to the user.
66
58
  return {
67
- 'id': created_view['metadata']['id'],
68
- 'name': created_view['name'],
69
- 'query': created_view['query']
59
+ "id": created_view["metadata"]["id"],
60
+ "name": created_view["name"],
61
+ "query": created_view["query"],
70
62
  }, HTTPStatus.CREATED
71
63
 
72
64
 
73
- @ns_conf.route('/<project_name>/views/<view_name>')
74
- @ns_conf.param('project_name', 'Name of the project')
75
- @ns_conf.param('view_name', 'Name of the view')
65
+ @ns_conf.route("/<project_name>/views/<view_name>")
66
+ @ns_conf.param("project_name", "Name of the project")
67
+ @ns_conf.param("view_name", "Name of the view")
76
68
  class ViewResource(Resource):
77
- @ns_conf.doc('get_view')
78
- @api_endpoint_metrics('GET', '/views/view')
69
+ @ns_conf.doc("get_view")
70
+ @api_endpoint_metrics("GET", "/views/view")
79
71
  def get(self, project_name, view_name):
80
- '''Get a view by name'''
72
+ """Get a view by name"""
81
73
  session = SessionController()
82
74
  try:
83
75
  project = session.database_controller.get_project(project_name)
84
76
  except EntityNotExistsError:
85
- return http_error(HTTPStatus.NOT_FOUND, 'Project not found', f'Project name {project_name} does not exist')
77
+ return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
86
78
 
87
79
  view = project.get_view(view_name)
88
80
  if view is None:
89
- return http_error(HTTPStatus.NOT_FOUND, 'View not found', f'View with name {view_name} does not exist')
81
+ return http_error(HTTPStatus.NOT_FOUND, "View not found", f"View with name {view_name} does not exist")
90
82
 
91
83
  # Only want to return relevant fields to the user.
92
- return {
93
- 'id': view['metadata']['id'],
94
- 'name': view['name'],
95
- 'query': view['query']
96
- }
84
+ return {"id": view["metadata"]["id"], "name": view["name"], "query": view["query"]}
97
85
 
98
- @ns_conf.doc('update_view')
99
- @api_endpoint_metrics('PUT', '/views/view')
86
+ @ns_conf.doc("update_view")
87
+ @api_endpoint_metrics("PUT", "/views/view")
100
88
  def put(self, project_name, view_name):
101
- '''Updates or creates a view'''
102
- if 'view' not in request.json:
103
- return http_error(HTTPStatus.BAD_REQUEST, 'Wrong argument', 'Must provide "view" parameter in PUT body')
104
- request_view = request.json['view']
89
+ """Updates or creates a view"""
90
+ if "view" not in request.json:
91
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Must provide "view" parameter in PUT body')
92
+ request_view = request.json["view"]
105
93
  session = SessionController()
106
94
  try:
107
95
  project = session.database_controller.get_project(project_name)
108
96
  except EntityNotExistsError:
109
- return http_error(HTTPStatus.NOT_FOUND, 'Project not found', f'Project name {project_name} does not exist')
97
+ return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
110
98
 
111
99
  existing_view = project.get_view(view_name)
112
100
  if existing_view is None:
113
101
  # Create
114
- if 'query' not in request_view:
115
- return http_error(HTTPStatus.BAD_REQUEST, 'Wrong argument', 'Missing "query" field for new view')
116
- project.create_view(view_name, request_view['query'])
102
+ if "query" not in request_view:
103
+ return http_error(HTTPStatus.BAD_REQUEST, "Wrong argument", 'Missing "query" field for new view')
104
+ project.create_view(view_name, request_view["query"], session)
117
105
  created_view = project.get_view(view_name)
118
106
  # Only want to return relevant fields to the user.
119
107
  return {
120
- 'id': created_view['metadata']['id'],
121
- 'name': created_view['name'],
122
- 'query': created_view['query']
108
+ "id": created_view["metadata"]["id"],
109
+ "name": created_view["name"],
110
+ "query": created_view["query"],
123
111
  }, HTTPStatus.CREATED
124
112
 
125
- new_query = existing_view['query']
126
- if 'query' in request_view:
127
- new_query = request_view['query']
113
+ new_query = existing_view["query"]
114
+ if "query" in request_view:
115
+ new_query = request_view["query"]
128
116
  project.update_view(view_name, new_query)
129
117
 
130
118
  existing_view = project.get_view(view_name)
131
119
  # Only want to return relevant fields to the user.
132
- return {
133
- 'id': existing_view['metadata']['id'],
134
- 'name': existing_view['name'],
135
- 'query': existing_view['query']
136
- }
120
+ return {"id": existing_view["metadata"]["id"], "name": existing_view["name"], "query": existing_view["query"]}
137
121
 
138
- @ns_conf.doc('delete_view')
139
- @api_endpoint_metrics('DELETE', '/views/view')
122
+ @ns_conf.doc("delete_view")
123
+ @api_endpoint_metrics("DELETE", "/views/view")
140
124
  def delete(self, project_name, view_name):
141
- '''Deletes a view by name'''
125
+ """Deletes a view by name"""
142
126
  session = SessionController()
143
127
  try:
144
128
  project = session.database_controller.get_project(project_name)
145
129
  except EntityNotExistsError:
146
- return http_error(HTTPStatus.NOT_FOUND, 'Project not found', f'Project name {project_name} does not exist')
130
+ return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
147
131
 
148
132
  if project.get_view(view_name) is None:
149
- return http_error(HTTPStatus.NOT_FOUND, 'View not found', f'View with name {view_name} does not exist')
133
+ return http_error(HTTPStatus.NOT_FOUND, "View not found", f"View with name {view_name} does not exist")
150
134
 
151
135
  project.delete_view(view_name)
152
- return '', HTTPStatus.NO_CONTENT
136
+ return "", HTTPStatus.NO_CONTENT
@@ -59,16 +59,20 @@ class DB2Handler(DatabaseHandler):
59
59
  return self.connection
60
60
 
61
61
  # Mandatory connection parameters.
62
- if not all(key in self.connection_data for key in ['host', 'user', 'password', 'database']):
63
- raise ValueError('Required parameters (host, user, password, database) must be provided.')
64
-
65
- connection_string = f"DRIVER={'IBM DB2 ODBC DRIVER'};DATABASE={self.connection_data['database']};HOST={self.connection_data['host']};PROTOCOL=TCPIP;UID={self.connection_data['user']};PWD={self.connection_data['password']};"
66
-
67
- # Optional connection parameters.
68
- if 'port' in self.connection_data:
69
- connection_string += f"PORT={self.connection_data['port']};"
70
-
71
- if 'schema' in self.connection_data:
62
+ if not all(key in self.connection_data for key in ["host", "user", "password", "database"]):
63
+ raise ValueError("Required parameters (host, user, password, database) must be provided.")
64
+ cloud = "databases.appdomain.cloud" in self.connection_data["host"]
65
+ if cloud:
66
+ connection_string = f"DATABASE={self.connection_data['database']};HOSTNAME={self.connection_data['host']};PORT={self.connection_data['port']};PROTOCOL=TCPIP;UID={self.connection_data['user']};PWD={self.connection_data['password']};SECURITY=SSL;"
67
+ connection_string += "SSLSERVERCERTIFICATE=;"
68
+ else:
69
+ connection_string = f"DRIVER={'IBM DB2 ODBC DRIVER'};DATABASE={self.connection_data['database']};HOST={self.connection_data['host']};PROTOCOL=TCPIP;UID={self.connection_data['user']};PWD={self.connection_data['password']};"
70
+
71
+ # Optional connection parameters.
72
+ if "port" in self.connection_data:
73
+ connection_string += f"PORT={self.connection_data['port']};"
74
+
75
+ if "schema" in self.connection_data:
72
76
  connection_string += f"CURRENTSCHEMA={self.connection_data['schema']};"
73
77
 
74
78
  try:
@@ -106,10 +110,10 @@ class DB2Handler(DatabaseHandler):
106
110
  self.connect()
107
111
  response.success = True
108
112
  except (OperationalError, ValueError) as known_error:
109
- logger.error(f'Connection check to IBM Db2 failed, {known_error}!')
113
+ logger.error(f"Connection check to IBM Db2 failed, {known_error}!")
110
114
  response.error_message = str(known_error)
111
115
  except Exception as unknown_error:
112
- logger.error(f'Connection check to IBM Db2 failed due to an unknown error, {unknown_error}!')
116
+ logger.error(f"Connection check to IBM Db2 failed due to an unknown error, {unknown_error}!")
113
117
  response.error_message = str(unknown_error)
114
118
 
115
119
  if response.success and need_to_close:
@@ -141,9 +145,7 @@ class DB2Handler(DatabaseHandler):
141
145
  result = cur.fetchall()
142
146
  response = Response(
143
147
  RESPONSE_TYPE.TABLE,
144
- data_frame=pd.DataFrame(
145
- result, columns=[x[0] for x in cur.description]
146
- ),
148
+ data_frame=pd.DataFrame(result, columns=[x[0] for x in cur.description]),
147
149
  )
148
150
  else:
149
151
  response = Response(RESPONSE_TYPE.OK)
@@ -198,10 +200,7 @@ class DB2Handler(DatabaseHandler):
198
200
  }
199
201
  )
200
202
 
201
- response = Response(
202
- RESPONSE_TYPE.TABLE,
203
- data_frame=pd.DataFrame(tables)
204
- )
203
+ response = Response(RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame(tables))
205
204
 
206
205
  return response
207
206
 
@@ -227,9 +226,6 @@ class DB2Handler(DatabaseHandler):
227
226
 
228
227
  columns = [column["COLUMN_NAME"] for column in result]
229
228
 
230
- response = Response(
231
- RESPONSE_TYPE.TABLE,
232
- data_frame=pd.DataFrame(columns, columns=["COLUMN_NAME"])
233
- )
229
+ response = Response(RESPONSE_TYPE.TABLE, data_frame=pd.DataFrame(columns, columns=["COLUMN_NAME"]))
234
230
 
235
231
  return response
@@ -0,0 +1,2 @@
1
+ __version__ = "0.0.1"
2
+ __description__ = "Gong conversation intelligence platform handler for MindsDB"
@@ -0,0 +1,30 @@
1
+ from mindsdb.integrations.libs.const import HANDLER_TYPE
2
+
3
+ from .__about__ import __version__ as version, __description__ as description
4
+ from .connection_args import connection_args, connection_args_example
5
+
6
+ try:
7
+ from .gong_handler import GongHandler as Handler
8
+
9
+ import_error = None
10
+ except Exception as e:
11
+ Handler = None
12
+ import_error = e
13
+
14
+ title = "Gong"
15
+ name = "gong"
16
+ type = HANDLER_TYPE.DATA
17
+ icon_path = "icon.svg"
18
+
19
+ __all__ = [
20
+ "Handler",
21
+ "version",
22
+ "name",
23
+ "type",
24
+ "title",
25
+ "description",
26
+ "import_error",
27
+ "icon_path",
28
+ "connection_args_example",
29
+ "connection_args",
30
+ ]