sunholo 0.140.6__tar.gz → 0.140.7__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.
- {sunholo-0.140.6/src/sunholo.egg-info → sunholo-0.140.7}/PKG-INFO +1 -1
- {sunholo-0.140.6 → sunholo-0.140.7}/pyproject.toml +1 -1
- sunholo-0.140.7/src/sunholo/agents/flask/base.py +48 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/flask/vac_routes.py +191 -73
- {sunholo-0.140.6 → sunholo-0.140.7/src/sunholo.egg-info}/PKG-INFO +1 -1
- sunholo-0.140.6/src/sunholo/agents/flask/base.py +0 -22
- {sunholo-0.140.6 → sunholo-0.140.7}/LICENSE.txt +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/MANIFEST.in +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/README.md +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/setup.cfg +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/langserve.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/route.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/agents/swagger.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/archive/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/archive/archive.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/auth/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/auth/gcloud.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/auth/refresh.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/auth/run.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/azure/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/azure/auth.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/azure/blobs.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/azure/event_grid.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/bots/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/bots/discord.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/bots/webapp.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/azure.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/encode_metadata.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/images.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/process_chunker_data.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/publish.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/pubsub.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/cli.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/configs.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/deploy.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/embedder.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/swagger.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/cli/vertex.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/components/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/components/llm.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/components/retriever.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/custom_logging.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/alloydb.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/alloydb_client.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/database.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/lancedb.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/database/uuid.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/discovery_engine/cli.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/discovery_engine/discovery_engine_client.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/embedder/embed_metadata.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/excel/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/excel/plugin.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/gcs/download_folder.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/gcs/download_gcs_text.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/gcs/extract_and_sign.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/genai/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/genai/file_handling.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/genai/genaiv2.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/genai/images.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/genai/init.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/genai/process_funcs_cls.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/genai/safety.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/invoke/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/invoke/async_class.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/invoke/direct_vac_func.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/langchain_types.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/langfuse/evals.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/llamaindex/user_history.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/mcp/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/mcp/cli.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/ollama/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/ollama/ollama_images.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/qna/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/qna/parsers.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/qna/retry.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/senses/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/senses/stream_voice.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/agent/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/agent/agent_service.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/agent/app.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/agent/my_log.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/agent/tools/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/agent/vac_service.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/project/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/project/app.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/project/my_log.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/project/vac_service.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/system_services/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/system_services/app.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/templates/system_services/my_log.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/terraform/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/terraform/tfvars_editor.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/tools/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/tools/web_browser.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/api_key.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/big_context.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/config.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/config_class.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/gcp.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/mime.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/parsers.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/utils/version.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/extensions_call.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/extensions_class.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/genai_functions.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/init.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/memory_tools.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/safety.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo/vertex/type_dict_to_json.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo.egg-info/SOURCES.txt +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/src/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/tests/test_async.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/tests/test_async_genai2.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/tests/test_chat_history.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/tests/test_config.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/tests/test_genai2.py +0 -0
- {sunholo-0.140.6 → sunholo-0.140.7}/tests/test_unstructured.py +0 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright [2024] [Holosun ApS]
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from ...custom_logging import log
|
16
|
+
import time
|
17
|
+
|
18
|
+
def create_app(name):
|
19
|
+
from flask import Flask, request
|
20
|
+
|
21
|
+
app = Flask(name)
|
22
|
+
|
23
|
+
app.config['TRAP_HTTP_EXCEPTIONS'] = True
|
24
|
+
app.config['PROPAGATE_EXCEPTIONS'] = True
|
25
|
+
|
26
|
+
@app.before_request
|
27
|
+
def start_timer():
|
28
|
+
request.start_time = time.time()
|
29
|
+
|
30
|
+
@app.after_request
|
31
|
+
def log_timing(response):
|
32
|
+
if hasattr(request, 'start_time'):
|
33
|
+
duration = time.time() - request.start_time
|
34
|
+
|
35
|
+
# Log all VAC requests with different detail levels
|
36
|
+
if request.path.startswith('/vac/streaming/'):
|
37
|
+
log.info(f"🚀 STREAMING: {duration:.3f}s - {request.path}")
|
38
|
+
elif request.path.startswith('/vac/'):
|
39
|
+
log.info(f"⚡ VAC: {duration:.3f}s - {request.path}")
|
40
|
+
elif duration > 1.0: # Log any slow requests
|
41
|
+
log.warning(f"🐌 SLOW REQUEST: {duration:.3f}s - {request.path}")
|
42
|
+
|
43
|
+
# Add response headers with timing info for debugging
|
44
|
+
response.headers['X-Response-Time'] = f"{duration:.3f}s"
|
45
|
+
|
46
|
+
return response
|
47
|
+
|
48
|
+
return app
|
@@ -6,6 +6,10 @@ import random
|
|
6
6
|
from functools import partial
|
7
7
|
import inspect
|
8
8
|
import asyncio
|
9
|
+
import time
|
10
|
+
import threading
|
11
|
+
from functools import lru_cache
|
12
|
+
from concurrent.futures import ThreadPoolExecutor
|
9
13
|
|
10
14
|
from ..chat_history import extract_chat_history_with_cache, extract_chat_history_async_cached
|
11
15
|
from ...qna.parsers import parse_output
|
@@ -32,6 +36,11 @@ except ImportError:
|
|
32
36
|
# Cache dictionary to store validated API keys
|
33
37
|
api_key_cache = {}
|
34
38
|
cache_duration = timedelta(minutes=5) # Cache duration
|
39
|
+
# Global caches and thread pool (add these after your existing globals)
|
40
|
+
_config_cache = {}
|
41
|
+
_config_lock = threading.Lock()
|
42
|
+
_thread_pool = ThreadPoolExecutor(max_workers=4)
|
43
|
+
|
35
44
|
|
36
45
|
class VACRoutes:
|
37
46
|
"""
|
@@ -69,8 +78,37 @@ if __name__ == "__main__":
|
|
69
78
|
self.additional_routes = additional_routes if additional_routes is not None else []
|
70
79
|
self.async_stream = async_stream
|
71
80
|
self.add_langfuse_eval = add_langfuse_eval
|
81
|
+
|
82
|
+
# Pre-warm common configs
|
83
|
+
self._preload_common_configs()
|
84
|
+
|
72
85
|
self.register_routes()
|
73
|
-
|
86
|
+
|
87
|
+
def _preload_common_configs(self):
|
88
|
+
"""Pre-load commonly used configurations to cache"""
|
89
|
+
common_vector_names = ["aitana3"] # Add your common vector names
|
90
|
+
for vector_name in common_vector_names:
|
91
|
+
try:
|
92
|
+
self._get_cached_config(vector_name)
|
93
|
+
log.info(f"Pre-loaded config for {vector_name}")
|
94
|
+
except Exception as e:
|
95
|
+
log.warning(f"Failed to pre-load config for {vector_name}: {e}")
|
96
|
+
|
97
|
+
|
98
|
+
@lru_cache(maxsize=100)
|
99
|
+
def _get_cached_config(self, vector_name: str):
|
100
|
+
"""Cached config loader with thread safety"""
|
101
|
+
with _config_lock:
|
102
|
+
if vector_name in _config_cache:
|
103
|
+
return _config_cache[vector_name]
|
104
|
+
|
105
|
+
try:
|
106
|
+
config = ConfigManager(vector_name)
|
107
|
+
_config_cache[vector_name] = config
|
108
|
+
return config
|
109
|
+
except Exception as e:
|
110
|
+
log.error(f"Error loading config for {vector_name}: {e}")
|
111
|
+
raise
|
74
112
|
|
75
113
|
def vac_interpreter_default(self, question: str, vector_name: str, chat_history=[], **kwargs):
|
76
114
|
# Create a callback that does nothing for streaming if you don't want intermediate outputs
|
@@ -228,22 +266,43 @@ if __name__ == "__main__":
|
|
228
266
|
|
229
267
|
log.info(f"OpenAI response: {openai_response}")
|
230
268
|
return jsonify(openai_response)
|
231
|
-
|
269
|
+
|
270
|
+
def _finalize_trace_background(self, trace, span, response, all_input):
|
271
|
+
"""Finalize trace operations in background"""
|
272
|
+
try:
|
273
|
+
if span:
|
274
|
+
span.end(output=str(response))
|
275
|
+
if trace:
|
276
|
+
trace.update(output=str(response))
|
277
|
+
self.langfuse_eval_response(trace_id=trace.id, eval_percent=all_input.get('eval_percent'))
|
278
|
+
except Exception as e:
|
279
|
+
log.warning(f"Background trace finalization failed: {e}")
|
280
|
+
|
232
281
|
def handle_stream_vac(self, vector_name):
|
282
|
+
request_start = time.time()
|
233
283
|
observed_stream_interpreter = self.stream_interpreter
|
234
284
|
is_async = inspect.iscoroutinefunction(self.stream_interpreter)
|
235
285
|
|
236
286
|
if is_async:
|
237
287
|
log.info(f"Stream interpreter is async: {observed_stream_interpreter}")
|
238
288
|
|
289
|
+
# Fast prep
|
239
290
|
prep = self.prep_vac(request, vector_name)
|
240
|
-
|
241
|
-
|
242
|
-
|
291
|
+
|
292
|
+
# Check for prep errors
|
293
|
+
if isinstance(prep, tuple) and len(prep) == 2:
|
294
|
+
error_response, status_code = prep
|
295
|
+
return jsonify(error_response), status_code
|
296
|
+
|
297
|
+
log.info(f"Processing prep completed in {time.time() - request_start:.3f}s")
|
298
|
+
|
299
|
+
trace = prep.get("trace")
|
300
|
+
span = prep.get("span")
|
243
301
|
vac_config = prep["vac_config"]
|
244
302
|
all_input = prep["all_input"]
|
245
303
|
|
246
|
-
log.info(f'
|
304
|
+
log.info(f'Starting stream with: {all_input["user_input"][:100]}...')
|
305
|
+
|
247
306
|
if span:
|
248
307
|
span.update(
|
249
308
|
name="start_streaming_chat",
|
@@ -254,7 +313,7 @@ if __name__ == "__main__":
|
|
254
313
|
def generate_response_content():
|
255
314
|
try:
|
256
315
|
if is_async:
|
257
|
-
from queue import Queue
|
316
|
+
from queue import Queue
|
258
317
|
result_queue = Queue()
|
259
318
|
import threading
|
260
319
|
|
@@ -271,7 +330,7 @@ if __name__ == "__main__":
|
|
271
330
|
trace_id=trace.id if trace else None,
|
272
331
|
**all_input["kwargs"]
|
273
332
|
)
|
274
|
-
|
333
|
+
|
275
334
|
async for chunk in async_gen:
|
276
335
|
if isinstance(chunk, dict) and 'answer' in chunk:
|
277
336
|
if trace:
|
@@ -287,6 +346,7 @@ if __name__ == "__main__":
|
|
287
346
|
result_queue.put(f"Streaming Error: {str(e)} {traceback.format_exc()}")
|
288
347
|
finally:
|
289
348
|
result_queue.put(None) # Sentinel
|
349
|
+
|
290
350
|
asyncio.run(process_async())
|
291
351
|
|
292
352
|
thread = threading.Thread(target=run_async)
|
@@ -301,7 +361,7 @@ if __name__ == "__main__":
|
|
301
361
|
|
302
362
|
thread.join()
|
303
363
|
else:
|
304
|
-
log.info("sync streaming response")
|
364
|
+
log.info("Starting sync streaming response")
|
305
365
|
for chunk in start_streaming_chat(
|
306
366
|
question=all_input["user_input"],
|
307
367
|
vector_name=vector_name,
|
@@ -327,15 +387,15 @@ if __name__ == "__main__":
|
|
327
387
|
except Exception as e:
|
328
388
|
yield f"Streaming Error: {str(e)} {traceback.format_exc()}"
|
329
389
|
|
330
|
-
#
|
390
|
+
# Create streaming response
|
331
391
|
response = Response(generate_response_content(), content_type='text/plain; charset=utf-8')
|
332
392
|
response.headers['Transfer-Encoding'] = 'chunked'
|
333
393
|
|
334
|
-
log.debug(f"
|
394
|
+
log.debug(f"Streaming response created in {time.time() - request_start:.3f}s")
|
395
|
+
|
335
396
|
if trace:
|
336
|
-
|
337
|
-
|
338
|
-
self.langfuse_eval_response(trace_id=trace.id, eval_percent=all_input.get('eval_percent'))
|
397
|
+
# Do final trace operations in background
|
398
|
+
_thread_pool.submit(self._finalize_trace_background, trace, span, response, all_input)
|
339
399
|
|
340
400
|
return response
|
341
401
|
|
@@ -654,78 +714,136 @@ if __name__ == "__main__":
|
|
654
714
|
tags = tags,
|
655
715
|
release = package_version
|
656
716
|
)
|
717
|
+
|
718
|
+
def _create_langfuse_trace_background(self, request, vector_name, trace_id):
|
719
|
+
"""Create Langfuse trace in background"""
|
720
|
+
try:
|
721
|
+
return self.create_langfuse_trace(request, vector_name, trace_id)
|
722
|
+
except Exception as e:
|
723
|
+
log.warning(f"Background trace creation failed: {e}")
|
724
|
+
return None
|
657
725
|
|
726
|
+
def _handle_file_upload_background(self, file, vector_name):
|
727
|
+
"""Handle file upload in background thread"""
|
728
|
+
try:
|
729
|
+
# Save with timestamp to avoid conflicts
|
730
|
+
temp_filename = f"temp_{int(time.time() * 1000)}_{file.filename}"
|
731
|
+
file.save(temp_filename)
|
732
|
+
|
733
|
+
# Upload to GCS
|
734
|
+
image_uri = add_file_to_gcs(temp_filename, vector_name)
|
735
|
+
|
736
|
+
# Clean up
|
737
|
+
os.remove(temp_filename)
|
738
|
+
|
739
|
+
return {"image_uri": image_uri, "mime": file.mimetype}
|
740
|
+
except Exception as e:
|
741
|
+
log.error(f"Background file upload failed: {e}")
|
742
|
+
return {}
|
743
|
+
|
658
744
|
def prep_vac(self, request, vector_name):
|
745
|
+
start_time = time.time()
|
746
|
+
|
747
|
+
# Fast request parsing
|
748
|
+
try:
|
749
|
+
if request.content_type.startswith('application/json'):
|
750
|
+
data = request.get_json()
|
751
|
+
elif request.content_type.startswith('multipart/form-data'):
|
752
|
+
data = request.form.to_dict()
|
753
|
+
# Handle file upload in background if present
|
754
|
+
if 'file' in request.files:
|
755
|
+
file = request.files['file']
|
756
|
+
if file.filename != '':
|
757
|
+
log.info(f"Found file: {file.filename} - uploading in background")
|
758
|
+
# Start file upload in background, don't block
|
759
|
+
upload_future = _thread_pool.submit(self._handle_file_upload_background, file, vector_name)
|
760
|
+
data["_upload_future"] = upload_future
|
761
|
+
else:
|
762
|
+
return {"error": "Unsupported content type"}, 400
|
763
|
+
except Exception as e:
|
764
|
+
return {"error": f"Request parsing error: {str(e)}"}, 400
|
659
765
|
|
660
|
-
|
661
|
-
data = request.get_json()
|
662
|
-
elif request.content_type.startswith('multipart/form-data'):
|
663
|
-
data = request.form.to_dict()
|
664
|
-
if 'file' in request.files:
|
665
|
-
file = request.files['file']
|
666
|
-
if file.filename != '':
|
667
|
-
log.info(f"Found file: {file.filename} to upload to GCS")
|
668
|
-
try:
|
669
|
-
image_uri, mime_type = self.handle_file_upload(file, vector_name)
|
670
|
-
data["image_uri"] = image_uri
|
671
|
-
data["mime"] = mime_type
|
672
|
-
except Exception as e:
|
673
|
-
log.error(traceback.format_exc())
|
674
|
-
return jsonify({'error': str(e), 'traceback': traceback.format_exc()}), 500
|
675
|
-
else:
|
676
|
-
log.error("No file selected")
|
677
|
-
return jsonify({"error": "No file selected"}), 400
|
678
|
-
else:
|
679
|
-
return jsonify({"error": "Unsupported content type"}), 400
|
766
|
+
log.info(f"vac/{vector_name} got data keys: {list(data.keys())}")
|
680
767
|
|
681
|
-
|
768
|
+
# Extract essential data first
|
769
|
+
try:
|
770
|
+
user_input = data.pop('user_input').strip()
|
771
|
+
stream_wait_time = data.pop('stream_wait_time', 7)
|
772
|
+
stream_timeout = data.pop('stream_timeout', 120)
|
773
|
+
chat_history = data.pop('chat_history', None)
|
774
|
+
eval_percent = data.pop('eval_percent', 0.01)
|
775
|
+
vector_name_param = data.pop('vector_name', vector_name)
|
776
|
+
trace_id = data.pop('trace_id', None)
|
777
|
+
|
778
|
+
if not user_input:
|
779
|
+
return {"error": "No user input provided"}, 400
|
780
|
+
|
781
|
+
except Exception as e:
|
782
|
+
return {"error": f"Required field missing: {str(e)}"}, 400
|
682
783
|
|
683
|
-
|
684
|
-
span = None
|
685
|
-
if self.add_langfuse_eval:
|
686
|
-
trace_id = data.get('trace_id')
|
687
|
-
trace = self.create_langfuse_trace(request, vector_name, trace_id)
|
688
|
-
log.info(f"Using existing langfuse trace: {trace_id}")
|
689
|
-
|
690
|
-
#config, _ = load_config("config/llm_config.yaml")
|
784
|
+
# Get config from cache (should be very fast)
|
691
785
|
try:
|
692
|
-
vac_config =
|
786
|
+
vac_config = self._get_cached_config(vector_name)
|
693
787
|
except Exception as e:
|
694
|
-
|
788
|
+
log.error(f"Config error: {e}")
|
789
|
+
return {"error": f"Unable to find vac_config for {vector_name} - {str(e)}"}, 500
|
695
790
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
791
|
+
# Process chat history with caching (should be fast)
|
792
|
+
paired_messages = extract_chat_history_with_cache(chat_history)
|
793
|
+
|
794
|
+
# Start tracing in background (don't block)
|
795
|
+
trace = None
|
796
|
+
span = None
|
797
|
+
if self.add_langfuse_eval:
|
798
|
+
trace_future = _thread_pool.submit(self._create_langfuse_trace_background, request, vector_name, trace_id)
|
799
|
+
# We'll get the trace result later if needed
|
701
800
|
|
702
|
-
|
801
|
+
# Wait for file upload if it was started (with timeout)
|
802
|
+
if "_upload_future" in data:
|
803
|
+
try:
|
804
|
+
upload_result = data["_upload_future"].result(timeout=3.0) # 3 sec max wait
|
805
|
+
data.update(upload_result)
|
806
|
+
log.info(f"File upload completed: {upload_result.get('image_uri', 'no uri')}")
|
807
|
+
except Exception as e:
|
808
|
+
log.warning(f"File upload failed or timed out: {e}")
|
809
|
+
finally:
|
810
|
+
data.pop("_upload_future", None)
|
703
811
|
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
812
|
+
# Build final input
|
813
|
+
all_input = {
|
814
|
+
'user_input': user_input,
|
815
|
+
'vector_name': vector_name_param,
|
816
|
+
'chat_history': paired_messages,
|
817
|
+
'stream_wait_time': stream_wait_time,
|
818
|
+
'stream_timeout': stream_timeout,
|
819
|
+
'eval_percent': eval_percent,
|
820
|
+
'kwargs': data
|
821
|
+
}
|
711
822
|
|
712
|
-
|
823
|
+
# Try to get trace result if available (don't block long)
|
824
|
+
if self.add_langfuse_eval:
|
825
|
+
try:
|
826
|
+
trace = trace_future.result(timeout=0.5) # 500ms max wait
|
827
|
+
if trace:
|
828
|
+
this_vac_config = vac_config.configs_by_kind.get("vacConfig")
|
829
|
+
metadata_config = None
|
830
|
+
if this_vac_config:
|
831
|
+
metadata_config = this_vac_config.get(vector_name)
|
832
|
+
trace.update(input=data, metadata=metadata_config)
|
833
|
+
|
834
|
+
span = trace.span(
|
835
|
+
name="VAC",
|
836
|
+
metadata=vac_config.configs_by_kind,
|
837
|
+
input=all_input
|
838
|
+
)
|
839
|
+
except Exception as e:
|
840
|
+
log.warning(f"Langfuse trace creation timed out or failed: {e}")
|
841
|
+
trace = None
|
842
|
+
span = None
|
713
843
|
|
714
|
-
|
715
|
-
|
716
|
-
'chat_history': paired_messages,
|
717
|
-
'stream_wait_time': stream_wait_time,
|
718
|
-
'stream_timeout': stream_timeout,
|
719
|
-
'eval_percent': eval_percent,
|
720
|
-
'kwargs': data}
|
844
|
+
prep_time = time.time() - start_time
|
845
|
+
log.info(f"prep_vac completed in {prep_time:.3f}s")
|
721
846
|
|
722
|
-
if trace:
|
723
|
-
span = trace.span(
|
724
|
-
name="VAC",
|
725
|
-
metadata=vac_config.configs_by_kind,
|
726
|
-
input = all_input
|
727
|
-
)
|
728
|
-
|
729
847
|
return {
|
730
848
|
"trace": trace,
|
731
849
|
"span": span,
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# Copyright [2024] [Holosun ApS]
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
def create_app(name):
|
15
|
-
from flask import Flask
|
16
|
-
|
17
|
-
app = Flask(name)
|
18
|
-
|
19
|
-
app.config['TRAP_HTTP_EXCEPTIONS'] = True
|
20
|
-
app.config['PROPAGATE_EXCEPTIONS'] = True
|
21
|
-
|
22
|
-
return app
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|