sunholo 0.99.12__tar.gz → 0.99.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. {sunholo-0.99.12 → sunholo-0.99.13}/PKG-INFO +2 -2
  2. {sunholo-0.99.12 → sunholo-0.99.13}/setup.py +1 -1
  3. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/gcs/add_file.py +3 -0
  4. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/invoke/async_class.py +23 -7
  5. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo.egg-info/PKG-INFO +2 -2
  6. {sunholo-0.99.12 → sunholo-0.99.13}/LICENSE.txt +0 -0
  7. {sunholo-0.99.12 → sunholo-0.99.13}/MANIFEST.in +0 -0
  8. {sunholo-0.99.12 → sunholo-0.99.13}/README.md +0 -0
  9. {sunholo-0.99.12 → sunholo-0.99.13}/setup.cfg +0 -0
  10. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/__init__.py +0 -0
  11. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/__init__.py +0 -0
  12. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/chat_history.py +0 -0
  13. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/dispatch_to_qa.py +0 -0
  14. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/fastapi/__init__.py +0 -0
  15. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/fastapi/base.py +0 -0
  16. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/fastapi/qna_routes.py +0 -0
  17. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/flask/__init__.py +0 -0
  18. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/flask/base.py +0 -0
  19. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/flask/qna_routes.py +0 -0
  20. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/flask/vac_routes.py +0 -0
  21. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/langserve.py +0 -0
  22. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/pubsub.py +0 -0
  23. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/route.py +0 -0
  24. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/special_commands.py +0 -0
  25. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/agents/swagger.py +0 -0
  26. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/archive/__init__.py +0 -0
  27. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/archive/archive.py +0 -0
  28. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/auth/__init__.py +0 -0
  29. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/auth/gcloud.py +0 -0
  30. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/auth/refresh.py +0 -0
  31. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/auth/run.py +0 -0
  32. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/azure/__init__.py +0 -0
  33. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/azure/auth.py +0 -0
  34. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/azure/blobs.py +0 -0
  35. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/azure/event_grid.py +0 -0
  36. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/bots/__init__.py +0 -0
  37. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/bots/discord.py +0 -0
  38. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/bots/github_webhook.py +0 -0
  39. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/bots/webapp.py +0 -0
  40. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/__init__.py +0 -0
  41. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/azure.py +0 -0
  42. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/doc_handling.py +0 -0
  43. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/encode_metadata.py +0 -0
  44. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/images.py +0 -0
  45. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/loaders.py +0 -0
  46. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/message_data.py +0 -0
  47. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/pdfs.py +0 -0
  48. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/process_chunker_data.py +0 -0
  49. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/publish.py +0 -0
  50. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/pubsub.py +0 -0
  51. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/chunker/splitter.py +0 -0
  52. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/__init__.py +0 -0
  53. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/chat_vac.py +0 -0
  54. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/cli.py +0 -0
  55. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/cli_init.py +0 -0
  56. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/configs.py +0 -0
  57. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/deploy.py +0 -0
  58. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/embedder.py +0 -0
  59. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/merge_texts.py +0 -0
  60. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/run_proxy.py +0 -0
  61. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/sun_rich.py +0 -0
  62. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/swagger.py +0 -0
  63. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/cli/vertex.py +0 -0
  64. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/components/__init__.py +0 -0
  65. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/components/llm.py +0 -0
  66. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/components/retriever.py +0 -0
  67. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/components/vectorstore.py +0 -0
  68. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/custom_logging.py +0 -0
  69. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/__init__.py +0 -0
  70. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/alloydb.py +0 -0
  71. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/alloydb_client.py +0 -0
  72. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/database.py +0 -0
  73. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/lancedb.py +0 -0
  74. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/sql/sb/create_function.sql +0 -0
  75. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/sql/sb/create_function_time.sql +0 -0
  76. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/sql/sb/create_table.sql +0 -0
  77. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
  78. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/sql/sb/return_sources.sql +0 -0
  79. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/sql/sb/setup.sql +0 -0
  80. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/static_dbs.py +0 -0
  81. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/database/uuid.py +0 -0
  82. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/discovery_engine/__init__.py +0 -0
  83. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/discovery_engine/chunker_handler.py +0 -0
  84. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/discovery_engine/create_new.py +0 -0
  85. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/discovery_engine/discovery_engine_client.py +0 -0
  86. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
  87. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/embedder/__init__.py +0 -0
  88. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/embedder/embed_chunk.py +0 -0
  89. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/excel/__init__.py +0 -0
  90. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/excel/plugin.py +0 -0
  91. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/gcs/__init__.py +0 -0
  92. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/gcs/download_folder.py +0 -0
  93. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/gcs/download_url.py +0 -0
  94. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/gcs/metadata.py +0 -0
  95. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/genai/__init__.py +0 -0
  96. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/genai/init.py +0 -0
  97. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/genai/process_funcs_cls.py +0 -0
  98. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/genai/safety.py +0 -0
  99. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/invoke/__init__.py +0 -0
  100. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/invoke/direct_vac_func.py +0 -0
  101. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/invoke/invoke_vac_utils.py +0 -0
  102. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/langfuse/__init__.py +0 -0
  103. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/langfuse/callback.py +0 -0
  104. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/langfuse/evals.py +0 -0
  105. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/langfuse/prompts.py +0 -0
  106. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/llamaindex/__init__.py +0 -0
  107. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/llamaindex/get_files.py +0 -0
  108. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/llamaindex/import_files.py +0 -0
  109. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/llamaindex/llamaindex_class.py +0 -0
  110. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/llamaindex/user_history.py +0 -0
  111. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/lookup/__init__.py +0 -0
  112. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/lookup/model_lookup.yaml +0 -0
  113. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/patches/__init__.py +0 -0
  114. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/patches/langchain/__init__.py +0 -0
  115. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/patches/langchain/lancedb.py +0 -0
  116. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/patches/langchain/vertexai.py +0 -0
  117. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/pubsub/__init__.py +0 -0
  118. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/pubsub/process_pubsub.py +0 -0
  119. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/pubsub/pubsub_manager.py +0 -0
  120. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/qna/__init__.py +0 -0
  121. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/qna/parsers.py +0 -0
  122. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/qna/retry.py +0 -0
  123. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/streaming/__init__.py +0 -0
  124. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/streaming/content_buffer.py +0 -0
  125. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/streaming/langserve.py +0 -0
  126. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/streaming/stream_lookup.py +0 -0
  127. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/streaming/streaming.py +0 -0
  128. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/summarise/__init__.py +0 -0
  129. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/summarise/summarise.py +0 -0
  130. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/terraform/__init__.py +0 -0
  131. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/terraform/tfvars_editor.py +0 -0
  132. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/tools/__init__.py +0 -0
  133. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/tools/web_browser.py +0 -0
  134. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/__init__.py +0 -0
  135. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/api_key.py +0 -0
  136. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/big_context.py +0 -0
  137. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/config.py +0 -0
  138. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/config_class.py +0 -0
  139. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/config_schema.py +0 -0
  140. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/gcp.py +0 -0
  141. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/gcp_project.py +0 -0
  142. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/parsers.py +0 -0
  143. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/timedelta.py +0 -0
  144. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/user_ids.py +0 -0
  145. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/utils/version.py +0 -0
  146. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/__init__.py +0 -0
  147. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/extensions_call.py +0 -0
  148. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/extensions_class.py +0 -0
  149. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/genai_functions.py +0 -0
  150. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/init.py +0 -0
  151. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/memory_tools.py +0 -0
  152. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/safety.py +0 -0
  153. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo/vertex/type_dict_to_json.py +0 -0
  154. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo.egg-info/SOURCES.txt +0 -0
  155. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo.egg-info/dependency_links.txt +0 -0
  156. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo.egg-info/entry_points.txt +0 -0
  157. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo.egg-info/requires.txt +0 -0
  158. {sunholo-0.99.12 → sunholo-0.99.13}/sunholo.egg-info/top_level.txt +0 -0
  159. {sunholo-0.99.12 → sunholo-0.99.13}/tests/test_chat_history.py +0 -0
  160. {sunholo-0.99.12 → sunholo-0.99.13}/tests/test_config.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.99.12
3
+ Version: 0.99.13
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.99.12.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.99.13.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -1,6 +1,6 @@
1
1
  from setuptools import setup, find_packages
2
2
 
3
- version = '0.99.12'
3
+ version = '0.99.13'
4
4
 
5
5
  setup(
6
6
  name='sunholo',
@@ -40,6 +40,9 @@ def handle_base64_image(base64_data: str, vector_name: str, extension: str):
40
40
  """
41
41
 
42
42
  model = ConfigManager(vector_name).vacConfig("llm")
43
+ if "," not in base64_data:
44
+ raise ValueError(f"Invalid Base64 image data: Missing metadata header {base64_data}")
45
+
43
46
  if model.startswith("openai"): # pass it to gpt directly
44
47
  return base64_data, base64_data.split(",", 1)
45
48
 
@@ -9,12 +9,12 @@ class AsyncTaskRunner:
9
9
  self.tasks = []
10
10
  self.retry_enabled = retry_enabled
11
11
  self.retry_kwargs = retry_kwargs or {}
12
-
12
+
13
13
  def add_task(self, func: Callable[..., Any], *args: Any):
14
14
  """Adds a task to the list of tasks to be executed."""
15
15
  log.info(f"Adding task: {func.__name__} with args: {args}")
16
16
  self.tasks.append((func.__name__, func, args))
17
-
17
+
18
18
  async def run_async_as_completed(self, callback=None) -> AsyncGenerator[Dict[str, Any], None]:
19
19
  """
20
20
  Runs all tasks concurrently and yields results as they complete, while periodically sending heartbeat messages.
@@ -29,7 +29,7 @@ class AsyncTaskRunner:
29
29
  coro = self._task_wrapper(name, func, args, callback)
30
30
  task = asyncio.create_task(coro)
31
31
  tasks[task] = name
32
-
32
+
33
33
  log.info(f"Start async run with {len(self.tasks)} runners")
34
34
  while tasks:
35
35
  done, _ = await asyncio.wait(tasks.keys(), return_when=asyncio.FIRST_COMPLETED)
@@ -78,8 +78,9 @@ class AsyncTaskRunner:
78
78
  # Stop the heartbeat task
79
79
  if heartbeat_task:
80
80
  heartbeat_task.cancel()
81
+ # Let the heartbeat_task finish in the background without awaiting it
81
82
  try:
82
- await heartbeat_task # Ensure the heartbeat task is properly canceled
83
+ await asyncio.shield(heartbeat_task) # Ensure cancellation is handled cleanly
83
84
  except asyncio.CancelledError:
84
85
  pass
85
86
 
@@ -92,7 +93,7 @@ class AsyncTaskRunner:
92
93
 
93
94
  async def _send_heartbeat(self, callback, func_name, interval=2):
94
95
  """
95
- Sends a single spinner at the start and keeps the task alive.
96
+ Sends a periodic heartbeat to keep the task alive and update the spinner with elapsed time.
96
97
 
97
98
  Args:
98
99
  callback: The callback to notify that the task is still working.
@@ -103,11 +104,26 @@ class AsyncTaskRunner:
103
104
  spinner_html = (
104
105
  f'<div id="{func_name}-spinner" class="spinner-container">'
105
106
  f' <div class="spinner"></div>'
106
- f' <span class="completed">✔️ Task {func_name} completed!</span>'
107
+ f' <span class="elapsed-time">Task {func_name} is still running... 0s elapsed</span>'
107
108
  f'</div>'
108
109
  )
109
110
  await callback.async_on_llm_new_token(token=spinner_html)
110
111
 
112
+ # Keep track of elapsed time
113
+ elapsed_time = 0
114
+
111
115
  # Keep sending heartbeats until task completes
112
116
  while True:
113
- await asyncio.sleep(interval) # Sleep for the interval but do not send multiple messages
117
+ try:
118
+ await asyncio.sleep(interval) # Sleep for the interval
119
+ elapsed_time += interval # Increment elapsed time
120
+
121
+ # Update spinner with the elapsed time
122
+ update_html = (
123
+ f'<div style="display: none;" data-update-id="{func_name}-spinner">'
124
+ f'<span class="elapsed-time">Task {func_name} is still running... {elapsed_time}s elapsed</span>'
125
+ f'</div>'
126
+ )
127
+ await callback.async_on_llm_new_token(token=update_html)
128
+ except asyncio.CancelledError:
129
+ break # Exit the loop if the heartbeat task is canceled
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.99.12
3
+ Version: 0.99.13
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.99.12.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.99.13.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes