sunholo 0.64.6__tar.gz → 0.64.8__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 (123) hide show
  1. {sunholo-0.64.6 → sunholo-0.64.8}/PKG-INFO +2 -2
  2. {sunholo-0.64.6 → sunholo-0.64.8}/setup.py +1 -1
  3. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/__init__.py +1 -0
  4. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/flask/qna_routes.py +121 -0
  5. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/route.py +10 -5
  6. sunholo-0.64.8/sunholo/agents/swagger.py +208 -0
  7. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/auth/run.py +2 -2
  8. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/chat_vac.py +2 -1
  9. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/cli.py +3 -1
  10. sunholo-0.64.8/sunholo/cli/swagger.py +38 -0
  11. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/alloydb.py +4 -0
  12. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/streaming/stream_lookup.py +2 -1
  13. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo.egg-info/PKG-INFO +2 -2
  14. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo.egg-info/SOURCES.txt +4 -1
  15. sunholo-0.64.8/tests/test_swagger.py +15 -0
  16. {sunholo-0.64.6 → sunholo-0.64.8}/LICENSE.txt +0 -0
  17. {sunholo-0.64.6 → sunholo-0.64.8}/MANIFEST.in +0 -0
  18. {sunholo-0.64.6 → sunholo-0.64.8}/README.md +0 -0
  19. {sunholo-0.64.6 → sunholo-0.64.8}/setup.cfg +0 -0
  20. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/__init__.py +0 -0
  21. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/chat_history.py +0 -0
  22. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/dispatch_to_qa.py +0 -0
  23. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/fastapi/__init__.py +0 -0
  24. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/fastapi/base.py +0 -0
  25. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/fastapi/qna_routes.py +0 -0
  26. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/flask/__init__.py +0 -0
  27. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/flask/base.py +0 -0
  28. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/langserve.py +0 -0
  29. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/pubsub.py +0 -0
  30. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/agents/special_commands.py +0 -0
  31. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/archive/__init__.py +0 -0
  32. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/archive/archive.py +0 -0
  33. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/auth/__init__.py +0 -0
  34. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/bots/__init__.py +0 -0
  35. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/bots/discord.py +0 -0
  36. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/bots/github_webhook.py +0 -0
  37. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/bots/webapp.py +0 -0
  38. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/__init__.py +0 -0
  39. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/data_to_embed_pubsub.py +0 -0
  40. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/doc_handling.py +0 -0
  41. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/images.py +0 -0
  42. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/loaders.py +0 -0
  43. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/message_data.py +0 -0
  44. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/pdfs.py +0 -0
  45. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/publish.py +0 -0
  46. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/chunker/splitter.py +0 -0
  47. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/__init__.py +0 -0
  48. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/cli_init.py +0 -0
  49. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/configs.py +0 -0
  50. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/deploy.py +0 -0
  51. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/embedder.py +0 -0
  52. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/merge_texts.py +0 -0
  53. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/run_proxy.py +0 -0
  54. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/cli/sun_rich.py +0 -0
  55. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/components/__init__.py +0 -0
  56. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/components/llm.py +0 -0
  57. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/components/retriever.py +0 -0
  58. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/components/vectorstore.py +0 -0
  59. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/__init__.py +0 -0
  60. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/database.py +0 -0
  61. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/lancedb.py +0 -0
  62. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/sql/sb/create_function.sql +0 -0
  63. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/sql/sb/create_function_time.sql +0 -0
  64. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/sql/sb/create_table.sql +0 -0
  65. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/sql/sb/delete_source_row.sql +0 -0
  66. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/sql/sb/return_sources.sql +0 -0
  67. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/sql/sb/setup.sql +0 -0
  68. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/static_dbs.py +0 -0
  69. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/database/uuid.py +0 -0
  70. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/embedder/__init__.py +0 -0
  71. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/embedder/embed_chunk.py +0 -0
  72. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/gcs/__init__.py +0 -0
  73. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/gcs/add_file.py +0 -0
  74. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/gcs/download_url.py +0 -0
  75. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/gcs/metadata.py +0 -0
  76. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/langfuse/__init__.py +0 -0
  77. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/langfuse/callback.py +0 -0
  78. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/langfuse/prompts.py +0 -0
  79. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/llamaindex/__init__.py +0 -0
  80. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/llamaindex/generate.py +0 -0
  81. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/llamaindex/get_files.py +0 -0
  82. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/llamaindex/import_files.py +0 -0
  83. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/logging.py +0 -0
  84. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/lookup/__init__.py +0 -0
  85. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/lookup/model_lookup.yaml +0 -0
  86. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/patches/__init__.py +0 -0
  87. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/patches/langchain/__init__.py +0 -0
  88. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/patches/langchain/lancedb.py +0 -0
  89. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/patches/langchain/vertexai.py +0 -0
  90. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/pubsub/__init__.py +0 -0
  91. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/pubsub/process_pubsub.py +0 -0
  92. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/pubsub/pubsub_manager.py +0 -0
  93. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/qna/__init__.py +0 -0
  94. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/qna/parsers.py +0 -0
  95. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/qna/retry.py +0 -0
  96. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/streaming/__init__.py +0 -0
  97. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/streaming/content_buffer.py +0 -0
  98. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/streaming/langserve.py +0 -0
  99. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/streaming/streaming.py +0 -0
  100. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/summarise/__init__.py +0 -0
  101. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/summarise/summarise.py +0 -0
  102. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/__init__.py +0 -0
  103. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/big_context.py +0 -0
  104. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/config.py +0 -0
  105. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/config_schema.py +0 -0
  106. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/gcp.py +0 -0
  107. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/gcp_project.py +0 -0
  108. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/parsers.py +0 -0
  109. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/timedelta.py +0 -0
  110. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/user_ids.py +0 -0
  111. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/utils/version.py +0 -0
  112. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/vertex/__init__.py +0 -0
  113. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/vertex/init.py +0 -0
  114. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/vertex/memory_tools.py +0 -0
  115. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo/vertex/safety.py +0 -0
  116. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo.egg-info/dependency_links.txt +0 -0
  117. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo.egg-info/entry_points.txt +0 -0
  118. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo.egg-info/requires.txt +0 -0
  119. {sunholo-0.64.6 → sunholo-0.64.8}/sunholo.egg-info/top_level.txt +0 -0
  120. {sunholo-0.64.6 → sunholo-0.64.8}/tests/test_chat_history.py +0 -0
  121. {sunholo-0.64.6 → sunholo-0.64.8}/tests/test_chunker.py +0 -0
  122. {sunholo-0.64.6 → sunholo-0.64.8}/tests/test_config.py +0 -0
  123. {sunholo-0.64.6 → sunholo-0.64.8}/tests/test_dispatch_to_qa.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.64.6
3
+ Version: 0.64.8
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.64.6.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.64.8.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -1,7 +1,7 @@
1
1
  from setuptools import setup, find_packages
2
2
 
3
3
  # Define your base version
4
- version = '0.64.6'
4
+ version = '0.64.8'
5
5
 
6
6
  setup(
7
7
  name='sunholo',
@@ -4,3 +4,4 @@ from .pubsub import process_pubsub
4
4
  from .special_commands import handle_special_commands, app_to_store, handle_files
5
5
  from .flask import register_qna_routes, create_app
6
6
  from .fastapi import register_qna_fastapi_routes, create_fastapi_app
7
+ from .swagger import config_to_swagger
@@ -41,7 +41,32 @@ except ImportError:
41
41
 
42
42
 
43
43
  def register_qna_routes(app, stream_interpreter, vac_interpreter):
44
+ """
45
+ Register Q&A routes for a Flask application.
44
46
 
47
+ This function sets up multiple routes for handling Q&A operations,
48
+ including streaming responses and processing static responses.
49
+
50
+ Args:
51
+ app (Flask): The Flask application instance.
52
+ stream_interpreter (function): Function to handle streaming Q&A responses.
53
+ vac_interpreter (function): Function to handle static Q&A responses.
54
+
55
+ Returns:
56
+ None
57
+
58
+ Example:
59
+ from flask import Flask
60
+ app = Flask(__name__)
61
+
62
+ def dummy_stream_interpreter(...):
63
+ ...
64
+
65
+ def dummy_vac_interpreter(...):
66
+ ...
67
+
68
+ register_qna_routes(app, dummy_stream_interpreter, dummy_vac_interpreter)
69
+ """
45
70
  @app.route("/")
46
71
  def home():
47
72
  return jsonify("OK")
@@ -52,6 +77,21 @@ def register_qna_routes(app, stream_interpreter, vac_interpreter):
52
77
 
53
78
  @app.route('/vac/streaming/<vector_name>', methods=['POST'])
54
79
  def stream_qa(vector_name):
80
+ """
81
+ Handle streaming Q&A responses.
82
+
83
+ This function sets up a route to handle streaming Q&A responses based on
84
+ the provided vector name.
85
+
86
+ Args:
87
+ vector_name (str): The name of the vector for the request.
88
+
89
+ Returns:
90
+ Response: A Flask response object streaming the Q&A response content.
91
+
92
+ Example:
93
+ response = stream_qa("example_vector")
94
+ """
55
95
  observed_stream_interpreter = observe()(stream_interpreter)
56
96
  prep = prep_vac(request, vector_name)
57
97
  log.debug(f"Processing prep: {prep}")
@@ -117,6 +157,21 @@ def register_qna_routes(app, stream_interpreter, vac_interpreter):
117
157
 
118
158
  @app.route('/vac/<vector_name>', methods=['POST'])
119
159
  def process_qna(vector_name):
160
+ """
161
+ Handle static Q&A responses.
162
+
163
+ This function sets up a route to handle static Q&A responses based on
164
+ the provided vector name.
165
+
166
+ Args:
167
+ vector_name (str): The name of the vector for the request.
168
+
169
+ Returns:
170
+ Response: A Flask response object with the Q&A response content.
171
+
172
+ Example:
173
+ response = process_qna("example_vector")
174
+ """
120
175
  observed_vac_interpreter = observe()(vac_interpreter)
121
176
  prep = prep_vac(request, vector_name)
122
177
  log.debug(f"Processing prep: {prep}")
@@ -167,6 +222,21 @@ def register_qna_routes(app, stream_interpreter, vac_interpreter):
167
222
  @app.route('/openai/v1/chat/completions', methods=['POST'])
168
223
  @app.route('/openai/v1/chat/completions/<vector_name>', methods=['POST'])
169
224
  def openai_compatible_endpoint(vector_name=None):
225
+ """
226
+ Handle OpenAI-compatible chat completions.
227
+
228
+ This function sets up routes to handle OpenAI-compatible chat completion requests,
229
+ both with and without a specified vector name.
230
+
231
+ Args:
232
+ vector_name (str, optional): The name of the vector for the request. Defaults to None.
233
+
234
+ Returns:
235
+ Response: A Flask response object with the chat completion content.
236
+
237
+ Example:
238
+ response = openai_compatible_endpoint("example_vector")
239
+ """
170
240
  data = request.get_json()
171
241
  log.info(f'openai_compatible_endpoint got data: {data} for vector: {vector_name}')
172
242
 
@@ -301,6 +371,22 @@ def register_qna_routes(app, stream_interpreter, vac_interpreter):
301
371
 
302
372
 
303
373
  def create_langfuse_trace(request, vector_name):
374
+ """
375
+ Create a Langfuse trace for tracking requests.
376
+
377
+ This function initializes a Langfuse trace object based on the request headers
378
+ and vector name.
379
+
380
+ Args:
381
+ request (Request): The Flask request object.
382
+ vector_name (str): The name of the vector for the request.
383
+
384
+ Returns:
385
+ Langfuse.Trace: The Langfuse trace object.
386
+
387
+ Example:
388
+ trace = create_langfuse_trace(request, "example_vector")
389
+ """
304
390
  try:
305
391
  from langfuse import Langfuse
306
392
  langfuse = Langfuse()
@@ -327,6 +413,22 @@ def create_langfuse_trace(request, vector_name):
327
413
  )
328
414
 
329
415
  def prep_vac(request, vector_name):
416
+ """
417
+ Prepare the input data for a VAC request.
418
+
419
+ This function processes the incoming request data, extracts relevant
420
+ information, and prepares the data for VAC processing.
421
+
422
+ Args:
423
+ request (Request): The Flask request object.
424
+ vector_name (str): The name of the vector for the request.
425
+
426
+ Returns:
427
+ dict: A dictionary containing prepared input data and metadata.
428
+
429
+ Example:
430
+ prep_data = prep_vac(request, "example_vector")
431
+ """
330
432
  #trace = create_langfuse_trace(request, vector_name)
331
433
  trace = None
332
434
  span = None
@@ -396,6 +498,25 @@ def prep_vac(request, vector_name):
396
498
 
397
499
 
398
500
  def handle_file_upload(file, vector_name):
501
+ """
502
+ Handle file upload and store the file in Google Cloud Storage.
503
+
504
+ This function saves the uploaded file locally, uploads it to Google Cloud Storage,
505
+ and then removes the local copy.
506
+
507
+ Args:
508
+ file (FileStorage): The uploaded file.
509
+ vector_name (str): The name of the vector for the request.
510
+
511
+ Returns:
512
+ tuple: A tuple containing the URI of the uploaded file and its MIME type.
513
+
514
+ Raises:
515
+ Exception: If the file upload fails.
516
+
517
+ Example:
518
+ uri, mime_type = handle_file_upload(file, "example_vector")
519
+ """
399
520
  try:
400
521
  file.save(file.filename)
401
522
  image_uri = add_file_to_gcs(file.filename, vector_name)
@@ -14,7 +14,7 @@
14
14
  from ..logging import log
15
15
  from ..utils import load_config_key, load_config
16
16
 
17
- def route_qna(vector_name):
17
+ def route_vac(vector_name):
18
18
 
19
19
  agent_url = load_config_key('agent_url', vector_name=vector_name, kind="vacConfig")
20
20
  if agent_url:
@@ -35,17 +35,22 @@ def route_qna(vector_name):
35
35
  log.info(f'agent_url: {agent_url}')
36
36
  return agent_url
37
37
 
38
- def route_endpoint(vector_name, override_endpoint=None):
38
+ def route_endpoint(vector_name, method = 'post', override_endpoint=None):
39
39
 
40
40
  agent_type = load_config_key('agent_type', vector_name, kind="vacConfig")
41
41
  if not agent_type:
42
42
  agent_type = load_config_key('agent', vector_name, kind="vacConfig")
43
43
 
44
- stem = route_qna(vector_name) if not override_endpoint else override_endpoint
44
+ stem = route_vac(vector_name) if not override_endpoint else override_endpoint
45
45
 
46
- endpoints_config = load_config_key(agent_type, vector_name, kind="agentConfig")
46
+ agents_config = load_config_key(agent_type, vector_name, kind="agentConfig")
47
47
 
48
- log.info(f"endpoints_config: {endpoints_config}")
48
+ log.info(f"endpoints_config: {agents_config}")
49
+ if method not in agents_config:
50
+ raise ValueError(f"Invalid method '{method}' for agent configuration.")
51
+
52
+ # 'post' or 'get'
53
+ endpoints_config = agents_config[method]
49
54
 
50
55
  # Replace placeholders in the config
51
56
  endpoints = {}
@@ -0,0 +1,208 @@
1
+ import yaml
2
+ import copy
3
+
4
+ from ..utils.config import load_all_configs
5
+ from .route import route_vac
6
+ from ..logging import log
7
+
8
+ def config_to_swagger():
9
+ """
10
+ Load configuration files and generate a Swagger specification.
11
+
12
+ This function loads the 'vacConfig' and 'agentConfig' configuration files,
13
+ validates their presence, and then generates a Swagger specification
14
+ based on these configurations.
15
+
16
+ Returns:
17
+ str: The generated Swagger specification in YAML format.
18
+
19
+ Raises:
20
+ ValueError: If 'vacConfig' or 'agentConfig' is not loaded.
21
+
22
+ Example:
23
+ ```python
24
+ swagger_yaml = config_to_swagger()
25
+ print(swagger_yaml)
26
+ ```
27
+ """
28
+ configs = load_all_configs()
29
+
30
+ vac_config = configs.get('vacConfig')
31
+ agent_config = configs.get('agentConfig')
32
+
33
+ if not vac_config:
34
+ raise ValueError("Need valid 'vacConfig' loaded")
35
+
36
+ if not agent_config:
37
+ raise ValueError("Need valid 'agentConfig' loaded")
38
+
39
+ swag = generate_swagger(vac_config, agent_config)
40
+
41
+ return swag
42
+
43
+ def generate_swagger(vac_config, agent_config):
44
+ """
45
+ Generate a Swagger specification based on the provided configurations.
46
+
47
+ This function creates a Swagger specification using the provided 'vacConfig'
48
+ and 'agentConfig'. It dynamically builds paths and responses based on the
49
+ configurations.
50
+
51
+ Args:
52
+ vac_config (dict): The VAC configuration.
53
+ agent_config (dict): The agent configuration.
54
+
55
+ Returns:
56
+ str: The generated Swagger specification in YAML format.
57
+
58
+ Example:
59
+ ```python
60
+ vac_config = {
61
+ 'vac': {
62
+ 'service1': {
63
+ 'llm': 'vertex',
64
+ 'model': 'gemini-1.5-flash-001',
65
+ 'agent': 'langserve'
66
+ },
67
+ 'service2': {
68
+ 'llm': 'openai',
69
+ 'agent': 'crewai',
70
+ 'secrets': ['OPENAI_API_KEY']
71
+ }
72
+ }
73
+ }
74
+
75
+ agent_config = {
76
+ 'agents': {
77
+ 'default': {
78
+ 'stream': "{stem}/vac/streaming/{vector_name}",
79
+ 'invoke': "{stem}/vac/{vector_name}",
80
+ 'post': {
81
+ 'stream': "{stem}/vac/streaming/{vector_name}",
82
+ 'invoke': "{stem}/vac/{vector_name}",
83
+ 'openai': "{stem}/openai/v1/chat/completions",
84
+ 'openai-vac': "{stem}/openai/v1/chat/completions/{vector_name}"
85
+ },
86
+ 'get': {
87
+ 'home': "{stem}/",
88
+ 'health': "{stem}/health"
89
+ },
90
+ 'response': {
91
+ 'invoke': {
92
+ '200': {
93
+ 'description': 'Successful invocation response',
94
+ 'schema': {
95
+ 'type': 'object',
96
+ 'properties': {
97
+ 'answer': {'type': 'string'},
98
+ 'source_documents': {
99
+ 'type': 'array',
100
+ 'items': {
101
+ 'type': 'object',
102
+ 'properties': {
103
+ 'page_content': {'type': 'string'},
104
+ 'metadata': {'type': 'string'}
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ swagger_yaml = generate_swagger(vac_config, agent_config)
118
+ print(swagger_yaml)
119
+ ```
120
+ """
121
+ swagger_template = {
122
+ 'swagger': '2.0',
123
+ 'info': {
124
+ 'title': 'Multivac - Cloud Endpoints + Cloud Run',
125
+ 'description': 'Multivac - Cloud Endpoints with a Cloud Run backend',
126
+ 'version': '0.1.0'
127
+ },
128
+ 'host': '${_ENDPOINTS_HOST}',
129
+ 'schemes': ['https'],
130
+ 'produces': ['application/json'],
131
+ 'paths': {}
132
+ }
133
+
134
+ vac_services = vac_config['vac']
135
+
136
+ for vector_name, config in vac_services.items():
137
+ agent_type = config['agent']
138
+ agent_config_paths = agent_config['agents'].get(agent_type, {})
139
+ log.info(f'Configuring swagger for agent_type: {agent_type} for vector_name: {vector_name}')
140
+ try:
141
+ stem = route_vac(vector_name)
142
+ except ValueError:
143
+ stem = f"${{{agent_type.upper()}_BACKEND_URL}}"
144
+ log.warning(f"Failed to find URL stem for {vector_name}/{agent_type} - using {stem} instead")
145
+
146
+ for method, endpoints in agent_config_paths.items():
147
+ if method not in ['get', 'post']:
148
+ log.warning(f"Skipping {endpoints}")
149
+ continue
150
+ for endpoint_key, endpoint_template in endpoints.items():
151
+ endpoint_path = endpoint_template.replace("{stem}", f"/{agent_type}").replace("{vector_name}", vector_name)
152
+ if endpoint_path not in swagger_template['paths']:
153
+ swagger_template['paths'][endpoint_path] = {}
154
+ swagger_template['paths'][endpoint_path][method] = {
155
+ 'summary': f"{method.capitalize()} {vector_name}",
156
+ 'operationId': f"{method}_{agent_type}_{endpoint_key}",
157
+ 'x-google-backend': {
158
+ 'address': endpoint_template.replace("{stem}", stem).replace("{vector_name}", vector_name),
159
+ 'protocol': 'h2'
160
+ },
161
+ 'responses': copy.deepcopy(agent_config_paths.get('response', {}).get(endpoint_key, {
162
+ '200': {
163
+ 'description': 'Default - A successful response',
164
+ 'schema': {
165
+ 'type': 'string'
166
+ }
167
+ }
168
+ }))
169
+ }
170
+ # Handle default agent configuration for agent types without specific entries
171
+ default_agent_config = agent_config['agents'].get('default', {})
172
+
173
+ for vector_name, config in vac_services.items():
174
+ agent_type = config['agent']
175
+ if agent_type in agent_config['agents']:
176
+ continue
177
+ log.info(f'Applying default configuration for agent_type: {agent_type} for vector_name: {vector_name}')
178
+ try:
179
+ stem = route_vac(vector_name)
180
+ except ValueError:
181
+ stem = f"${{{agent_type.upper()}_BACKEND_URL}}"
182
+ log.warning(f"Failed to find URL stem for {vector_name}/{agent_type} - using {stem} instead")
183
+
184
+ for method, endpoints in default_agent_config.items():
185
+ if method not in ['get', 'post']:
186
+ continue
187
+ for endpoint_key, endpoint_template in endpoints.items():
188
+ endpoint_path = endpoint_template.replace("{stem}", f"/{agent_type}").replace("{vector_name}", vector_name)
189
+ if endpoint_path not in swagger_template['paths']:
190
+ swagger_template['paths'][endpoint_path] = {}
191
+ swagger_template['paths'][endpoint_path][method] = {
192
+ 'summary': f"{method.capitalize()} {agent_type}",
193
+ 'operationId': f"{method}_{agent_type}_{endpoint_key}",
194
+ 'x-google-backend': {
195
+ 'address': endpoint_template.replace("{stem}", stem).replace("{vector_name}", vector_name),
196
+ 'protocol': 'h2'
197
+ },
198
+ 'responses': copy.deepcopy(default_agent_config.get('response', {}).get(endpoint_key, {
199
+ '200': {
200
+ 'description': 'Default - A successful response',
201
+ 'schema': {
202
+ 'type': 'string'
203
+ }
204
+ }
205
+ }))
206
+ }
207
+
208
+ return yaml.dump(swagger_template, default_flow_style=False)
@@ -5,14 +5,14 @@ from typing import Dict, Optional
5
5
  from ..utils.config import load_config_key, load_config
6
6
  from ..utils.gcp import is_running_on_cloudrun
7
7
  from ..logging import log
8
- from ..agents.route import route_qna
8
+ from ..agents.route import route_vac
9
9
 
10
10
  def get_run_url(vector_name=None):
11
11
 
12
12
  if not vector_name:
13
13
  raise ValueError('Vector name was not specified')
14
14
 
15
- cloud_urls = route_qna(vector_name)
15
+ cloud_urls = route_vac(vector_name)
16
16
 
17
17
  cloud_urls, _ = load_config('config/cloud_run_urls.json')
18
18
  agent = load_config_key("agent", vector_name=vector_name, kind="vacConfig")
@@ -272,8 +272,9 @@ def vac_command(args):
272
272
  display_name = load_config_key("display_name", vector_name=args.vac_name, kind="vacConfig")
273
273
  description = load_config_key("description", vector_name=args.vac_name, kind="vacConfig")
274
274
  endpoints_config = load_config_key(agent_name, "dummy_value", kind="agentConfig")
275
+ post_endpoints = endpoints_config['post']
275
276
 
276
- display_endpoints = ' '.join(f"{key}: {value}" for key, value in endpoints_config.items())
277
+ display_endpoints = ' '.join(f"{key}: {value}" for key, value in post_endpoints.items())
277
278
  display_endpoints = display_endpoints.replace("{stem}", service_url).replace("{vector_name}", args.vac_name)
278
279
 
279
280
  if agent_name == "langserve":
@@ -8,6 +8,7 @@ from .merge_texts import setup_merge_text_subparser
8
8
  from .run_proxy import setup_proxy_subparser
9
9
  from .chat_vac import setup_vac_subparser
10
10
  from .embedder import setup_embedder_subparser
11
+ from .swagger import setup_swagger_subparser
11
12
 
12
13
  from ..utils.config import load_config_key
13
14
 
@@ -17,7 +18,6 @@ from .sun_rich import console
17
18
  import sys
18
19
  from rich.panel import Panel
19
20
 
20
-
21
21
  def load_default_gcp_config():
22
22
  try:
23
23
  gcp_config = load_config_key('gcp_config', 'global', kind="vacConfig")
@@ -82,6 +82,8 @@ def main(args=None):
82
82
  setup_vac_subparser(subparsers)
83
83
  # embed command
84
84
  setup_embedder_subparser(subparsers)
85
+ # swagger generation
86
+ setup_swagger_subparser(subparsers)
85
87
 
86
88
  #TODO: add database setup commands: alloydb and supabase
87
89
 
@@ -0,0 +1,38 @@
1
+ from ..agents.swagger import generate_swagger
2
+ from ..utils.config import load_all_configs
3
+ from .sun_rich import console
4
+
5
+ def cli_swagger(args):
6
+
7
+ configs = load_all_configs()
8
+
9
+ vac_config = args.vac_config_path or configs.get('vacConfig')
10
+ agent_config = args.agent_config_path or configs.get('agentConfig')
11
+ if not agent_config:
12
+ raise ValueError('Need an agentConfig path')
13
+
14
+ if not vac_config:
15
+ raise ValueError('Need a vacConfig path')
16
+
17
+ swag = generate_swagger(vac_config, agent_config)
18
+
19
+ console.print(swag)
20
+
21
+ return swag
22
+
23
+ def setup_swagger_subparser(subparsers):
24
+ """
25
+ Sets up an argparse subparser for the 'swagger' command.
26
+
27
+ By default will use the 'vacConfig' configuration within the folder specified by '_CONFIG_FOLDER'
28
+
29
+ Example command:
30
+ ```bash
31
+ sunholo swagger --config .
32
+ ```
33
+ """
34
+ deploy_parser = subparsers.add_parser('swagger', help='Create a swagger specification based off a "vacConfig" configuration')
35
+ deploy_parser.add_argument('--vac_config_path', help='Path to the vacConfig file. Set _CONFIG_FOLDER env var and place file in there to change default config location.')
36
+ deploy_parser.add_argument('--agent_config_path', help='Path to agentConfig file. Set _CONFIG_FOLDER env var and place file in there to change default config location.')
37
+ deploy_parser.set_defaults(func=cli_swagger)
38
+
@@ -234,6 +234,10 @@ def create_alloydb_table(vector_name, engine, type = "vectorstore", alloydb_conf
234
234
  log.info(f"AlloyDB Table '{table_name}' exists in cache, skipping creation.")
235
235
 
236
236
  return table_name
237
+
238
+ alloydb_table_cache[table_name] = True
239
+ return table_name
240
+
237
241
  log.info(f"# Creating AlloyDB table {table_name}")
238
242
  try:
239
243
  engine.init_vectorstore_table(
@@ -5,8 +5,9 @@ def can_agent_stream(agent_name: str):
5
5
 
6
6
  log.debug(f"agent_type: {agent_name} checking streaming...")
7
7
  endpoints_config = load_config_key(agent_name, "dummy_value", kind="agentConfig")
8
+ post_endpoints = endpoints_config['post']
8
9
 
9
- return 'stream' in endpoints_config
10
+ return 'stream' in post_endpoints
10
11
 
11
12
 
12
13
 
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.64.6
3
+ Version: 0.64.8
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.64.6.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.64.8.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -18,6 +18,7 @@ sunholo/agents/langserve.py
18
18
  sunholo/agents/pubsub.py
19
19
  sunholo/agents/route.py
20
20
  sunholo/agents/special_commands.py
21
+ sunholo/agents/swagger.py
21
22
  sunholo/agents/fastapi/__init__.py
22
23
  sunholo/agents/fastapi/base.py
23
24
  sunholo/agents/fastapi/qna_routes.py
@@ -51,6 +52,7 @@ sunholo/cli/embedder.py
51
52
  sunholo/cli/merge_texts.py
52
53
  sunholo/cli/run_proxy.py
53
54
  sunholo/cli/sun_rich.py
55
+ sunholo/cli/swagger.py
54
56
  sunholo/components/__init__.py
55
57
  sunholo/components/llm.py
56
58
  sunholo/components/retriever.py
@@ -116,4 +118,5 @@ sunholo/vertex/safety.py
116
118
  tests/test_chat_history.py
117
119
  tests/test_chunker.py
118
120
  tests/test_config.py
119
- tests/test_dispatch_to_qa.py
121
+ tests/test_dispatch_to_qa.py
122
+ tests/test_swagger.py
@@ -0,0 +1,15 @@
1
+ from sunholo.agents import config_to_swagger
2
+ import yaml
3
+
4
+
5
+ # Test cases for config_to_swagger and generate_swagger functions
6
+ def test_config_to_swagger():
7
+ swagger_yaml = config_to_swagger()
8
+ swagger_dict = yaml.safe_load(swagger_yaml)
9
+
10
+ assert 'swagger' in swagger_dict
11
+ assert swagger_dict['swagger'] == '2.0'
12
+ assert 'info' in swagger_dict
13
+ assert swagger_dict['info']['title'] == 'Multivac - Cloud Endpoints + Cloud Run'
14
+ assert 'paths' in swagger_dict
15
+ #TODO: more tests for specific agents
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes