sunholo 0.60.8__py3-none-any.whl → 0.61.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.
@@ -41,9 +41,14 @@ def register_qna_routes(app, stream_interpreter, vac_interpreter):
41
41
  def home():
42
42
  return jsonify("OK")
43
43
 
44
+ @app.route("/health")
45
+ def health():
46
+ return jsonify({"status": "healthy"})
47
+
44
48
  @app.route('/vac/streaming/<vector_name>', methods=['POST'])
45
49
  @observe()
46
50
  def stream_qa(vector_name):
51
+ observed_stream_interpreter = observe()(stream_interpreter)
47
52
  prep = prep_vac(request, vector_name)
48
53
  log.debug(f"Processing prep: {prep}")
49
54
  trace = prep["trace"]
@@ -69,7 +74,7 @@ def register_qna_routes(app, stream_interpreter, vac_interpreter):
69
74
 
70
75
  for chunk in start_streaming_chat(question=all_input["user_input"],
71
76
  vector_name=vector_name,
72
- qna_func=stream_interpreter,
77
+ qna_func=observed_stream_interpreter,
73
78
  chat_history=all_input["chat_history"],
74
79
  wait_time=all_input["stream_wait_time"],
75
80
  timeout=all_input["stream_timeout"],
sunholo/cli/chat_vac.py CHANGED
@@ -186,7 +186,7 @@ def vac_command(args):
186
186
 
187
187
  stream_chat_session(service_url, args.vac_name)
188
188
 
189
- stop_proxy(agent_name)
189
+ stop_proxy(agent_name, stop_local=False)
190
190
 
191
191
 
192
192
  def list_cloud_run_services(project, region):
sunholo/cli/cli.py CHANGED
@@ -11,10 +11,17 @@ from ..utils.config import load_config_key
11
11
 
12
12
  from ..logging import log
13
13
 
14
+ from .sun_rich import console
15
+ import sys
14
16
 
15
17
 
16
18
  def load_default_gcp_config():
17
- gcp_config = load_config_key('gcp_config', 'global', kind="vacConfig")
19
+ try:
20
+ gcp_config = load_config_key('gcp_config', 'global', kind="vacConfig")
21
+ except FileNotFoundError as e:
22
+ console.print(f"{e} - move config/ folder to working directory or set the _CONFIG_FOLDER environment variable to its location")
23
+ sys.exit(1)
24
+
18
25
  if gcp_config:
19
26
  return gcp_config.get('project_id', ''), gcp_config.get('location', 'europe-west1')
20
27
  else:
sunholo/cli/run_proxy.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import subprocess
2
2
  import os
3
+ import sys
3
4
  import signal
4
5
  import json
5
6
 
@@ -96,7 +97,7 @@ def save_proxies(proxies):
96
97
 
97
98
 
98
99
 
99
- def start_proxy(service_name, region, project, port=None):
100
+ def start_proxy(service_name, region, project, port=None, local=False, app_type=None, app_folder=None, log_file=None):
100
101
  """
101
102
  Starts the gcloud proxy to the Cloud Run service and stores the PID.
102
103
 
@@ -114,34 +115,48 @@ def start_proxy(service_name, region, project, port=None):
114
115
 
115
116
  if not port:
116
117
  port = get_next_available_port(proxies, DEFAULT_PORT)
117
-
118
- command = [
119
- "gcloud", "run", "services", "proxy", service_name,
120
- "--region", region,
121
- "--project", project,
122
- "--port", str(port)
123
- ]
124
- with open(os.devnull, 'w') as devnull:
125
- process = subprocess.Popen(command, stdout=devnull, stderr=devnull, preexec_fn=os.setpgrp)
126
-
127
- proxies[service_name] = {
128
- "pid": process.pid,
129
- "port": port
130
- }
131
- save_proxies(proxies)
132
118
 
133
- console.print(f"Proxy for [bold orange]'{service_name}'[/bold orange] setup complete on port {port}")
134
- list_proxies()
119
+
120
+
121
+ if local:
122
+ start_local(service_name, port, app_type, app_folder, log_file)
123
+ else:
124
+ command = [
125
+ "gcloud", "run", "services", "proxy", service_name,
126
+ "--region", region,
127
+ "--project", project,
128
+ "--port", str(port)
129
+ ]
130
+ if log_file:
131
+ log_file_path = os.path.join(app_folder, f"{service_name}_log.txt")
132
+ with open(log_file_path, 'a') as logf:
133
+ process = subprocess.Popen(command, stdout=logf, stderr=logf, preexec_fn=os.setpgrp)
134
+ else:
135
+ log_file_path = "No log file specified"
136
+ with open(os.devnull, 'w') as devnull:
137
+ process = subprocess.Popen(command, stdout=devnull, stderr=devnull, preexec_fn=os.setpgrp)
138
+
139
+ proxies[service_name] = {
140
+ "pid": process.pid,
141
+ "port": port,
142
+ "local": "No",
143
+ "logs": log_file_path
144
+ }
145
+ save_proxies(proxies)
146
+
147
+ console.print(f"Proxy for [bold orange]'{service_name}'[/bold orange] setup complete on port {port}")
148
+ list_proxies()
135
149
 
136
150
  return f"http://127.0.0.1:{port}"
137
151
 
138
152
 
139
- def stop_proxy(service_name):
153
+ def stop_proxy(service_name, stop_local=True):
140
154
  """
141
155
  Stops the gcloud proxy to the Cloud Run service using the stored PID.
142
156
 
143
157
  Args:
144
158
  service_name (str): Name of the Cloud Run service.
159
+ stop_local (bool): Whether to stop locally running services or not. Defaults to True.
145
160
  """
146
161
  proxies = clean_proxy_list()
147
162
 
@@ -149,16 +164,21 @@ def stop_proxy(service_name):
149
164
  print(f"No proxy found for service: {service_name}")
150
165
  return
151
166
 
152
- pid = proxies[service_name]["pid"]
153
- try:
154
- os.kill(pid, signal.SIGTERM)
155
- del proxies[service_name]
156
- save_proxies(proxies)
157
- console.print(f"Proxy for [bold orange]'{service_name}'[bold orange] stopped.")
158
- except ProcessLookupError:
159
- console.print(f"No process found with PID: {pid}")
160
- except Exception as e:
161
- console.print(f"[bold red]Error stopping proxy for {service_name}: {e}[/bold red]")
167
+ if not stop_local:
168
+ local = proxies[service_name]["local"]
169
+ if local != "No":
170
+ console.print(f"Not stopping local VAC running on: {local}")
171
+ else:
172
+ pid = proxies[service_name]["pid"]
173
+ try:
174
+ os.kill(pid, signal.SIGTERM)
175
+ del proxies[service_name]
176
+ save_proxies(proxies)
177
+ console.print(f"Proxy for [bold orange]'{service_name}'[bold orange] stopped.")
178
+ except ProcessLookupError:
179
+ console.print(f"No process found with PID: {pid}")
180
+ except Exception as e:
181
+ console.print(f"[bold red]Error stopping proxy for {service_name}: {e}[/bold red]")
162
182
 
163
183
  list_proxies()
164
184
 
@@ -192,18 +212,71 @@ def list_proxies():
192
212
  if not proxies:
193
213
  print("No proxies currently running.")
194
214
  else:
195
- table = Table(title="VAC Proxies")
215
+ table = Table(title="VAC Proxies - `sunholo proxy list`")
196
216
  table.add_column("VAC")
197
217
  table.add_column("Port")
198
218
  table.add_column("PID")
199
219
  table.add_column("URL")
220
+ table.add_column("Local")
221
+ table.add_column("Logs")
200
222
 
201
223
  for service_name, info in proxies.items():
202
224
  url = f"http://127.0.0.1:{info['port']}"
203
- table.add_row(service_name, str(info['port']), str(info['pid']), url)
225
+ table.add_row(service_name,
226
+ str(info['port']),
227
+ str(info['pid']),
228
+ url,
229
+ str(info['local']),
230
+ str(info['logs']) )
204
231
 
205
232
  console.print(table)
206
233
 
234
+ def start_local(service_name, port, app_type, app_folder, log_file):
235
+ """
236
+ Starts a local Flask or FastAPI VAC app.
237
+
238
+ Args:
239
+ service_name (str): Name of the service.
240
+ port (int): Port to run the local app on.
241
+ app_type (str): Type of the app ('flask' or 'fastapi').
242
+ app_folder (str): Folder containing the app.
243
+ """
244
+ proxies = clean_proxy_list()
245
+
246
+ if service_name in proxies:
247
+ console.print(f"Local VAC app [bold orange]'{service_name}'[/bold orange] is already running on port {proxies[service_name]['port']}.")
248
+ return
249
+
250
+ if app_type == 'flask':
251
+ command = [sys.executable, 'app.py']
252
+ elif app_type == 'fastapi':
253
+ command = ["uvicorn", "app:app", f"--port={port}"]
254
+ else:
255
+ print(f"[bold red]Unknown app type: {app_type}[/bold red]")
256
+ return
257
+
258
+ if log_file:
259
+ log_file_path = os.path.join(app_folder, f"{service_name}_log.txt")
260
+ with open(log_file_path, 'w') as logf:
261
+ process = subprocess.Popen(command, cwd=app_folder, stdout=logf, stderr=logf, preexec_fn=os.setpgrp)
262
+ else:
263
+ log_file_path = "No log file specified"
264
+ with open(os.devnull, 'a') as devnull:
265
+ process = subprocess.Popen(command, cwd=app_folder, stdout=devnull, stderr=devnull, preexec_fn=os.setpgrp)
266
+
267
+ proxies[service_name] = {
268
+ "pid": process.pid,
269
+ "port": port,
270
+ "local": f"{app_folder}/app.py - {app_type}",
271
+ "logs": log_file_path
272
+ }
273
+ save_proxies(proxies)
274
+
275
+ console.print(f"Local app [bold orange]'{service_name}'[/bold orange] started on port {port}")
276
+ list_proxies()
277
+
278
+ return f"http://127.0.0.1:{port}"
279
+
207
280
  def setup_proxy_subparser(subparsers):
208
281
  """
209
282
  Sets up an argparse subparser for the 'proxy' command.
@@ -218,8 +291,19 @@ def setup_proxy_subparser(subparsers):
218
291
  start_parser = proxy_subparsers.add_parser('start', help='Start the proxy to the VAC Cloud Run service')
219
292
  start_parser.add_argument('service_name', help='Name of the Cloud Run service.')
220
293
  start_parser.add_argument('--port', type=int, help='Port to run the proxy on. Auto-assigns if not provided.')
221
- start_parser.set_defaults(func=lambda args: start_proxy(args.service_name, args.region, args.project, args.port))
222
-
294
+ start_parser.add_argument('--local', action='store_true', help='Run the service locally instead of proxying to Cloud Run.')
295
+ start_parser.add_argument('--app-type', choices=['flask', 'fastapi'], help='If local, type of the local app (flask or fastapi).')
296
+ start_parser.add_argument('--app-folder', help='If local, folder containing the local app.py')
297
+ start_parser.add_argument('--log-file', action='store_true', help='Whether to create a file containing proxy logs.')
298
+ start_parser.set_defaults(func=lambda args: start_proxy(args.service_name,
299
+ args.region,
300
+ args.project,
301
+ args.port,
302
+ args.local,
303
+ args.app_type,
304
+ args.app_folder,
305
+ args.log_file))
306
+
223
307
  stop_parser = proxy_subparsers.add_parser('stop', help='Stop the proxy to the Cloud Run service.')
224
308
  stop_parser.add_argument('service_name', help='Name of the Cloud Run service.')
225
309
  stop_parser.set_defaults(func=lambda args: stop_proxy(args.service_name))
@@ -5,7 +5,7 @@ def create_lancedb_index(bucket, vector_name, num_partitions=256, num_sub_vector
5
5
  import lancedb
6
6
  #import tantivy
7
7
  except ImportError:
8
- raise ValueError("Could not import lancedb module, install via `pip intall sunholo[database]`")
8
+ raise ValueError("Could not import lancedb module, install via `pip install sunholo[database]`")
9
9
 
10
10
  try:
11
11
  db = lancedb.connect(bucket)
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.60.8
3
+ Version: 0.61.0
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Home-page: https://github.com/sunholo-data/sunholo-py
6
- Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.60.8.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.61.0.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -7,13 +7,12 @@ sunholo/agents/langserve.py,sha256=FdhQjorAY2bMn2rpuabNT6bU3uqSKWrl8DjpH3L_V7k,4
7
7
  sunholo/agents/pubsub.py,sha256=5hbbhbBGyVWRpt2sAGC5FEheYH1mCCwVUhZEB1S7vGg,1337
8
8
  sunholo/agents/route.py,sha256=0klBifx-QtMGsjq8HB04s9Bytm0nFXPYaWKeyt-S9S4,2356
9
9
  sunholo/agents/special_commands.py,sha256=PI4ADgFQvPDCeCpOeWIrD4bD432NYFeVcBBnkqTBWi8,6457
10
- sunholo/agents/test_chat_history.py,sha256=vPbPu0xREEs4J4X_zJKBY1f19Vy5yV05_CKfUUQqfFg,3923
11
10
  sunholo/agents/fastapi/__init__.py,sha256=S_pj4_bTUmDGoq_exaREHlOKThi0zTuGT0VZY0YfODQ,88
12
11
  sunholo/agents/fastapi/base.py,sha256=clk76cHbUAvU0OYJrRfCWX_5f0ACbhDsIzYBhI3wyoE,2514
13
12
  sunholo/agents/fastapi/qna_routes.py,sha256=DgK4Btu5XriOC1JaRQ4G_nWEjJfnQ0J5pyLanF6eF1g,3857
14
13
  sunholo/agents/flask/__init__.py,sha256=uqfHNw2Ru3EJ4dJEcbp86h_lkquBQPMxZbjhV_xe3rs,72
15
14
  sunholo/agents/flask/base.py,sha256=RUGWBYWeV60FatYF5sMRrxD-INU97Vodsi6JaB6i93s,763
16
- sunholo/agents/flask/qna_routes.py,sha256=Pr2dxM7GKhXNKv6t7_J578BN6oMETAYFe6ydEm4D90g,8620
15
+ sunholo/agents/flask/qna_routes.py,sha256=9xQUIadDtQIgTRa-nXIYJbzwQokqtVjeLp-tcSxGy9Y,8788
17
16
  sunholo/archive/__init__.py,sha256=qNHWm5rGPVOlxZBZCpA1wTYPbalizRT7f8X4rs2t290,31
18
17
  sunholo/archive/archive.py,sha256=C-UhG5x-XtZ8VheQp92IYJqgD0V3NFQjniqlit94t18,1197
19
18
  sunholo/auth/__init__.py,sha256=4owDjSaWYkbTlPK47UHTOC0gCWbZsqn4ZIEw5NWZTlg,28
@@ -32,13 +31,13 @@ sunholo/chunker/pdfs.py,sha256=daCZ1xjn1YvxlifIyxskWNpLJLe-Q9D_Jq12MWx3tZo,2473
32
31
  sunholo/chunker/publish.py,sha256=PoT8q3XJeFCg10WrLkYhuaaXIrGVkvUD3-R9IfoWoH4,2703
33
32
  sunholo/chunker/splitter.py,sha256=FLkDhkePkg_zGQpFBK13Cznw575D-Rf9pcaCpc1HUxY,6726
34
33
  sunholo/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- sunholo/cli/chat_vac.py,sha256=FvQTgFA4h0vfPSXvAx0_KCe8DUr2u_Wv1Md8-p6BkHk,11361
36
- sunholo/cli/cli.py,sha256=LWA5OveHx3ocy9KD1XwAwosBFrTUwifArIbltYgpul4,2420
34
+ sunholo/cli/chat_vac.py,sha256=_NUjwATKvzwfnBJRedP2GqPFNeaCF5F8OvdygXRH1LY,11379
35
+ sunholo/cli/cli.py,sha256=cogY1F5rcIGFYpZVFtbDNlAIElpfyPSCvSLC1ZIpHXg,2666
37
36
  sunholo/cli/cli_init.py,sha256=JMZ9AX2cPDZ-_mv3adiv2ToFVNyRPtjk9Biszl1kiR0,2358
38
37
  sunholo/cli/configs.py,sha256=QUM9DvKOdZmEQRM5uI3Nh887T0YDiSMr7O240zTLqws,4546
39
38
  sunholo/cli/deploy.py,sha256=zxdwUsRTRMC8U5vyRv0JiKBLFn84Ug_Tc88-_h9hJSs,1609
40
39
  sunholo/cli/merge_texts.py,sha256=U9vdMwKmcPoc6iPOWX5MKSxn49dNGbNzVLw8ui5PhEU,1823
41
- sunholo/cli/run_proxy.py,sha256=53bRzm6IVf5SB07fiwqKpJrGQBTt3xAWi-669w6VYSU,7591
40
+ sunholo/cli/run_proxy.py,sha256=9ILCxSVHPzS-cSBvjdHhfZFlwsJ4Ttmu0vLtNoPCRgo,11469
42
41
  sunholo/cli/sun_rich.py,sha256=UpMqeJ0C8i0pkue1AHnnyyX0bFJ9zZeJ7HBR6yhuA8A,54
43
42
  sunholo/components/__init__.py,sha256=RJGNEihwvRIiDScKis04RHJv4yZGI1UpXlOmuCptNZI,208
44
43
  sunholo/components/llm.py,sha256=T4we3tGmqUj4tPwxQr9M6AXv_BALqZV_dRSvINan-oU,10374
@@ -48,7 +47,7 @@ sunholo/components/vectorstore.py,sha256=dzspqOBtuxSjCFxem5_50sqwUUjbZ4oBYERtCwx
48
47
  sunholo/database/__init__.py,sha256=Zz0Shcq-CtStf9rJGIYB_Ybzb8rY_Q9mfSj-nviM490,241
49
48
  sunholo/database/alloydb.py,sha256=0zRLyeC9nACzj3v36ET9NqLeuzdwBJ2bE09CzgVTTFM,17098
50
49
  sunholo/database/database.py,sha256=doY05kG8BZBLL-arh4hq5ef1ouWOtGHqdsDc6M2YHgk,7345
51
- sunholo/database/lancedb.py,sha256=WSrbY5mgyeXx6i7UBiz4YQ_i5UIYVYFo-vPGO72bQKY,707
50
+ sunholo/database/lancedb.py,sha256=2rAbJVusMrm5TPtVTsUtmwn0z1iZ_wvbKhc6eyT6ClE,708
52
51
  sunholo/database/static_dbs.py,sha256=aOyU3AJ-Dzz3qSNjbuN2293cfYw5PhkcQuQxdwPMJ4w,435
53
52
  sunholo/database/uuid.py,sha256=GtUL_uq80u2xkozPF9kwNpvhBf03hbZR3xUhO3NomBM,237
54
53
  sunholo/database/sql/sb/create_function.sql,sha256=HuDyp3fxS5Etop3gGDy28_AuFuhgEgVcq9-q3oeecPU,1033
@@ -96,9 +95,9 @@ sunholo/utils/parsers.py,sha256=OrHmASqIbI45atVOhiGodgLvnfrzkvVzyHnSvAXD89I,3841
96
95
  sunholo/utils/user_ids.py,sha256=SQd5_H7FE7vcTZp9AQuQDWBXd4FEEd7TeVMQe1H4Ny8,292
97
96
  sunholo/vertex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
97
  sunholo/vertex/init_vertex.py,sha256=JDMUaBRdednzbKF-5p33qqLit2LMsvgvWW-NRz0AqO0,1801
99
- sunholo-0.60.8.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
100
- sunholo-0.60.8.dist-info/METADATA,sha256=TN1eHOBtuzd5fypSsbUpXwexrvfQ12ZPPE8IkRYq_A8,8057
101
- sunholo-0.60.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
102
- sunholo-0.60.8.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
103
- sunholo-0.60.8.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
104
- sunholo-0.60.8.dist-info/RECORD,,
98
+ sunholo-0.61.0.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
99
+ sunholo-0.61.0.dist-info/METADATA,sha256=bgegQWs1_EJTqy_Xqwebux6L8ACVcJn-wtuVPxpw_yw,8057
100
+ sunholo-0.61.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
101
+ sunholo-0.61.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
102
+ sunholo-0.61.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
103
+ sunholo-0.61.0.dist-info/RECORD,,
@@ -1,119 +0,0 @@
1
- import pytest
2
- from .chat_history import *
3
-
4
-
5
- def test_extract_chat_history_null_input():
6
- assert extract_chat_history(None) == [], 'Expected empty list for null input'
7
-
8
-
9
- def test_extract_chat_history_no_chat():
10
- assert extract_chat_history([]) == [], 'Expected empty list for no chat history'
11
-
12
-
13
- def test_extract_chat_history_with_chat():
14
- chat_history = [('User', 'Hello'), ('Bot', 'Hi'), ('User', 'How are you?'), ('Bot', 'I am fine.')]
15
- expected_output = [('User', 'Hello'), ('Bot', 'Hi'), ('User', 'How are you?'), ('Bot', 'I am fine.')]
16
- assert extract_chat_history(chat_history) == expected_output, 'Expected list of paired messages for chat history'
17
-
18
-
19
- # Test cases for embeds_to_json function
20
-
21
- def test_embeds_to_json_no_embeds():
22
- message = 'Hello, world!'
23
- assert embeds_to_json(message) == '', 'Expected empty string for message with no embeds'
24
-
25
-
26
- def test_embeds_to_json_one_embed():
27
- message = 'Hello, world! [embed]'
28
- expected_output = '{"embeds": ["embed"]}'
29
- assert embeds_to_json(message) == expected_output, 'Expected JSON string with one embed for message with one embed'
30
-
31
-
32
- def test_embeds_to_json_multiple_embeds():
33
- message = 'Hello, world! [embed1] [embed2]'
34
- expected_output = '{"embeds": ["embed1", "embed2"]}'
35
- assert embeds_to_json(message) == expected_output, 'Expected JSON string with multiple embeds for message with multiple embeds'
36
-
37
-
38
- # Test cases for create_message_element function
39
-
40
- def test_create_message_element_text():
41
- message = {'text': 'Hello, world!'}
42
- assert create_message_element(message) == 'Hello, world!', 'Expected text element for message with text'
43
-
44
-
45
- def test_create_message_element_content():
46
- message = {'content': 'Hello, world!'}
47
- assert create_message_element(message) == 'Hello, world!', 'Expected content element for message with content'
48
-
49
-
50
- def test_create_message_element_no_text_or_content():
51
- message = {}
52
- with pytest.raises(KeyError):
53
- create_message_element(message)
54
-
55
-
56
- # Test cases for is_human function
57
-
58
- def test_is_human_name_human():
59
- message = {'name': 'Human'}
60
- assert is_human(message) == True, 'Expected True for message with name Human'
61
-
62
-
63
- def test_is_human_sender_type_human():
64
- message = {'sender': {'type': 'HUMAN'}}
65
- assert is_human(message) == True, 'Expected True for message with sender type HUMAN'
66
-
67
-
68
- def test_is_human_user_no_bot_id():
69
- message = {'user': 'User1', 'bot_id': None}
70
- assert is_human(message) == True, 'Expected True for message with user field and no bot_id field'
71
-
72
-
73
- def test_is_human_not_human():
74
- message = {'name': 'Bot'}
75
- assert is_human(message) == False, 'Expected False for message not from a human'
76
-
77
-
78
- # Test cases for is_bot function
79
-
80
- def test_is_bot_name_bot():
81
- message = {'name': 'Bot'}
82
- assert is_bot(message) == True, 'Expected True for message with name Bot'
83
-
84
-
85
- def test_is_bot_sender_type_bot():
86
- message = {'sender': {'type': 'BOT'}}
87
- assert is_bot(message) == True, 'Expected True for message with sender type BOT'
88
-
89
-
90
- def test_is_bot_with_bot_id():
91
- message = {'bot_id': 'bot1'}
92
- assert is_bot(message) == True, 'Expected True for message with bot_id field'
93
-
94
-
95
- def test_is_bot_not_bot():
96
- message = {'name': 'Human'}
97
- assert is_bot(message) == False, 'Expected False for message not from a bot'
98
-
99
-
100
- # Test cases for is_ai function
101
-
102
- def test_is_ai_name_ai():
103
- message = {'name': 'AI'}
104
- assert is_ai(message) == True, 'Expected True for message with name AI'
105
-
106
-
107
- def test_is_ai_sender_type_bot():
108
- message = {'sender': {'type': 'BOT'}}
109
- assert is_ai(message) == True, 'Expected True for message with sender type BOT'
110
-
111
-
112
- def test_is_ai_with_bot_id():
113
- message = {'bot_id': 'bot1'}
114
- assert is_ai(message) == True, 'Expected True for message with bot_id field'
115
-
116
-
117
- def test_is_ai_not_ai():
118
- message = {'name': 'Human'}
119
- assert is_ai(message) == False, 'Expected False for message not from an AI'