sunholo 0.140.2__tar.gz → 0.140.5__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.2/src/sunholo.egg-info → sunholo-0.140.5}/PKG-INFO +1 -1
- {sunholo-0.140.2 → sunholo-0.140.5}/pyproject.toml +1 -1
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/__init__.py +1 -1
- sunholo-0.140.5/src/sunholo/agents/chat_history.py +523 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/flask/__init__.py +0 -1
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/flask/vac_routes.py +22 -24
- {sunholo-0.140.2 → sunholo-0.140.5/src/sunholo.egg-info}/PKG-INFO +1 -1
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo.egg-info/SOURCES.txt +0 -1
- sunholo-0.140.2/src/sunholo/agents/chat_history.py +0 -245
- sunholo-0.140.2/src/sunholo/agents/flask/qna_routes.py +0 -604
- {sunholo-0.140.2 → sunholo-0.140.5}/LICENSE.txt +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/MANIFEST.in +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/README.md +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/setup.cfg +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/langserve.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/route.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/agents/swagger.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/archive/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/archive/archive.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/auth/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/auth/gcloud.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/auth/refresh.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/auth/run.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/azure/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/azure/auth.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/azure/blobs.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/azure/event_grid.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/bots/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/bots/discord.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/bots/webapp.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/azure.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/encode_metadata.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/images.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/process_chunker_data.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/publish.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/pubsub.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/cli.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/configs.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/deploy.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/embedder.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/swagger.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/cli/vertex.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/components/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/components/llm.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/components/retriever.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/custom_logging.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/alloydb.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/alloydb_client.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/database.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/lancedb.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/database/uuid.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/discovery_engine/cli.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/discovery_engine/discovery_engine_client.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/embedder/embed_metadata.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/excel/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/excel/plugin.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/gcs/download_folder.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/gcs/download_gcs_text.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/gcs/extract_and_sign.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/genai/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/genai/file_handling.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/genai/genaiv2.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/genai/images.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/genai/init.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/genai/process_funcs_cls.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/genai/safety.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/invoke/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/invoke/async_class.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/invoke/direct_vac_func.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/langchain_types.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/langfuse/evals.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/llamaindex/user_history.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/mcp/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/mcp/cli.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/ollama/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/ollama/ollama_images.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/qna/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/qna/parsers.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/qna/retry.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/senses/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/senses/stream_voice.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/agent/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/agent/agent_service.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/agent/app.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/agent/my_log.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/agent/tools/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/agent/vac_service.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/project/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/project/app.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/project/my_log.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/project/vac_service.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/system_services/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/system_services/app.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/templates/system_services/my_log.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/terraform/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/terraform/tfvars_editor.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/tools/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/tools/web_browser.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/api_key.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/big_context.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/config.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/config_class.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/gcp.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/mime.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/parsers.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/utils/version.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/extensions_call.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/extensions_class.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/genai_functions.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/init.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/memory_tools.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/safety.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo/vertex/type_dict_to_json.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/src/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/tests/test_async.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/tests/test_async_genai2.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/tests/test_chat_history.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/tests/test_config.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/tests/test_genai2.py +0 -0
- {sunholo-0.140.2 → sunholo-0.140.5}/tests/test_unstructured.py +0 -0
@@ -2,6 +2,6 @@ from .chat_history import extract_chat_history
|
|
2
2
|
from .dispatch_to_qa import send_to_qa, send_to_qa_async
|
3
3
|
from .pubsub import process_pubsub
|
4
4
|
from .special_commands import handle_special_commands, app_to_store, handle_files
|
5
|
-
from .flask import
|
5
|
+
from .flask import create_app, VACRoutes
|
6
6
|
from .fastapi import register_qna_fastapi_routes, create_fastapi_app
|
7
7
|
from .swagger import config_to_swagger
|
@@ -0,0 +1,523 @@
|
|
1
|
+
import json
|
2
|
+
from ..custom_logging import log
|
3
|
+
import time
|
4
|
+
import hashlib
|
5
|
+
from functools import lru_cache
|
6
|
+
from typing import List, Tuple, Optional
|
7
|
+
|
8
|
+
|
9
|
+
class ChatHistoryCache:
|
10
|
+
"""
|
11
|
+
Incremental cache for chat history processing.
|
12
|
+
|
13
|
+
Caches processed message pairs and only processes new messages
|
14
|
+
when the chat history is extended.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, max_cache_size: int = 1000):
|
18
|
+
self.cache = {}
|
19
|
+
self.max_cache_size = max_cache_size
|
20
|
+
|
21
|
+
def _get_cache_key(self, chat_history: List[dict]) -> str:
|
22
|
+
"""Generate a cache key based on the chat history content."""
|
23
|
+
# Use the hash of the serialized chat history for the key
|
24
|
+
# Only hash the first few and last few messages to balance performance vs accuracy
|
25
|
+
if len(chat_history) <= 10:
|
26
|
+
content = str(chat_history)
|
27
|
+
else:
|
28
|
+
# Hash first 5 and last 5 messages + length
|
29
|
+
content = str(chat_history[:5] + chat_history[-5:] + [len(chat_history)])
|
30
|
+
|
31
|
+
return hashlib.md5(content.encode()).hexdigest()
|
32
|
+
|
33
|
+
def _find_cached_prefix(self, current_history: List[dict]) -> Tuple[Optional[List[Tuple]], int]:
|
34
|
+
"""
|
35
|
+
Find the longest cached prefix of the current chat history.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Tuple of (cached_pairs, cache_length) or (None, 0) if no cache found
|
39
|
+
"""
|
40
|
+
current_length = len(current_history)
|
41
|
+
|
42
|
+
# Check for cached versions of prefixes, starting from longest
|
43
|
+
for cache_length in range(current_length - 1, 0, -1):
|
44
|
+
prefix = current_history[:cache_length]
|
45
|
+
cache_key = self._get_cache_key(prefix)
|
46
|
+
|
47
|
+
if cache_key in self.cache:
|
48
|
+
cached_data = self.cache[cache_key]
|
49
|
+
cached_pairs = cached_data['pairs']
|
50
|
+
|
51
|
+
# Verify the cache is still valid by checking a few messages
|
52
|
+
if self._verify_cache_validity(prefix, cached_data['original_history']):
|
53
|
+
return cached_pairs, cache_length
|
54
|
+
else:
|
55
|
+
# Cache is stale, remove it
|
56
|
+
del self.cache[cache_key]
|
57
|
+
|
58
|
+
return None, 0
|
59
|
+
|
60
|
+
def _verify_cache_validity(self, current_prefix: List[dict], cached_prefix: List[dict]) -> bool:
|
61
|
+
"""Quick verification that cached data is still valid."""
|
62
|
+
if len(current_prefix) != len(cached_prefix):
|
63
|
+
return False
|
64
|
+
|
65
|
+
# Check first and last few messages for equality
|
66
|
+
check_indices = [0, -1] if len(current_prefix) >= 2 else [0]
|
67
|
+
|
68
|
+
for i in check_indices:
|
69
|
+
if current_prefix[i] != cached_prefix[i]:
|
70
|
+
return False
|
71
|
+
|
72
|
+
return True
|
73
|
+
|
74
|
+
def extract_chat_history_incremental(self, chat_history: List[dict]) -> List[Tuple]:
|
75
|
+
"""
|
76
|
+
Extract chat history with incremental caching.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
chat_history: List of chat message dictionaries
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
List of (human_message, ai_message) tuples
|
83
|
+
"""
|
84
|
+
if not chat_history:
|
85
|
+
return []
|
86
|
+
|
87
|
+
# Try to find cached prefix
|
88
|
+
cached_pairs, cache_length = self._find_cached_prefix(chat_history)
|
89
|
+
|
90
|
+
if cached_pairs is not None:
|
91
|
+
log.debug(f"Found cached pairs for {cache_length} messages, processing {len(chat_history) - cache_length} new messages")
|
92
|
+
|
93
|
+
# Process only the new messages
|
94
|
+
new_messages = chat_history[cache_length:]
|
95
|
+
new_pairs = self._process_new_messages(new_messages, cached_pairs)
|
96
|
+
|
97
|
+
# Combine cached and new pairs
|
98
|
+
all_pairs = cached_pairs + new_pairs
|
99
|
+
else:
|
100
|
+
log.debug(f"No cache found, processing all {len(chat_history)} messages")
|
101
|
+
# Process all messages from scratch
|
102
|
+
all_pairs = self._extract_chat_history_full(chat_history)
|
103
|
+
|
104
|
+
# Cache the result
|
105
|
+
self._update_cache(chat_history, all_pairs)
|
106
|
+
|
107
|
+
return all_pairs
|
108
|
+
|
109
|
+
def _process_new_messages(self, new_messages: List[dict], cached_pairs: List[Tuple]) -> List[Tuple]:
|
110
|
+
"""
|
111
|
+
Process only the new messages, considering the state from cached pairs.
|
112
|
+
|
113
|
+
Args:
|
114
|
+
new_messages: New messages to process
|
115
|
+
cached_pairs: Previously processed message pairs
|
116
|
+
|
117
|
+
Returns:
|
118
|
+
List of new message pairs
|
119
|
+
"""
|
120
|
+
if not new_messages:
|
121
|
+
return []
|
122
|
+
|
123
|
+
new_pairs = []
|
124
|
+
|
125
|
+
# Determine if we're waiting for a bot response based on cached pairs
|
126
|
+
waiting_for_bot = True
|
127
|
+
if cached_pairs:
|
128
|
+
last_pair = cached_pairs[-1]
|
129
|
+
# If last pair has both human and AI message, we're ready for a new human message
|
130
|
+
waiting_for_bot = not (last_pair[0] and last_pair[1])
|
131
|
+
|
132
|
+
# If we ended with an unpaired human message, get it
|
133
|
+
last_human_message = ""
|
134
|
+
if cached_pairs and waiting_for_bot:
|
135
|
+
last_human_message = cached_pairs[-1][0]
|
136
|
+
|
137
|
+
# Process new messages
|
138
|
+
for message in new_messages:
|
139
|
+
try:
|
140
|
+
is_human_msg = is_human(message)
|
141
|
+
content = create_message_element(message)
|
142
|
+
|
143
|
+
if is_human_msg:
|
144
|
+
last_human_message = content
|
145
|
+
waiting_for_bot = True
|
146
|
+
else: # Bot message
|
147
|
+
if waiting_for_bot and last_human_message:
|
148
|
+
new_pairs.append((last_human_message, content))
|
149
|
+
last_human_message = ""
|
150
|
+
waiting_for_bot = False
|
151
|
+
# If not waiting for bot or no human message, this is an orphaned bot message
|
152
|
+
|
153
|
+
except (KeyError, TypeError) as e:
|
154
|
+
log.warning(f"Error processing new message: {e}")
|
155
|
+
continue
|
156
|
+
|
157
|
+
return new_pairs
|
158
|
+
|
159
|
+
def _extract_chat_history_full(self, chat_history: List[dict]) -> List[Tuple]:
|
160
|
+
"""Full extraction when no cache is available."""
|
161
|
+
# Use the optimized version from before
|
162
|
+
paired_messages = []
|
163
|
+
|
164
|
+
# Handle initial bot message
|
165
|
+
start_idx = 0
|
166
|
+
if chat_history and is_bot(chat_history[0]):
|
167
|
+
try:
|
168
|
+
first_message = chat_history[0]
|
169
|
+
blank_element = ""
|
170
|
+
bot_element = create_message_element(first_message)
|
171
|
+
paired_messages.append((blank_element, bot_element))
|
172
|
+
start_idx = 1
|
173
|
+
except (KeyError, TypeError):
|
174
|
+
pass
|
175
|
+
|
176
|
+
# Process remaining messages
|
177
|
+
last_human_message = ""
|
178
|
+
for i in range(start_idx, len(chat_history)):
|
179
|
+
message = chat_history[i]
|
180
|
+
|
181
|
+
try:
|
182
|
+
is_human_msg = is_human(message)
|
183
|
+
content = create_message_element(message)
|
184
|
+
|
185
|
+
if is_human_msg:
|
186
|
+
last_human_message = content
|
187
|
+
else: # Bot message
|
188
|
+
if last_human_message:
|
189
|
+
paired_messages.append((last_human_message, content))
|
190
|
+
last_human_message = ""
|
191
|
+
|
192
|
+
except (KeyError, TypeError) as e:
|
193
|
+
log.warning(f"Error processing message {i}: {e}")
|
194
|
+
continue
|
195
|
+
|
196
|
+
return paired_messages
|
197
|
+
|
198
|
+
def _update_cache(self, chat_history: List[dict], pairs: List[Tuple]):
|
199
|
+
"""Update cache with new result."""
|
200
|
+
# Only cache if the history is of reasonable size
|
201
|
+
if len(chat_history) < 2:
|
202
|
+
return
|
203
|
+
|
204
|
+
cache_key = self._get_cache_key(chat_history)
|
205
|
+
|
206
|
+
# Implement simple LRU by removing oldest entries
|
207
|
+
if len(self.cache) >= self.max_cache_size:
|
208
|
+
# Remove 20% of oldest entries
|
209
|
+
remove_count = self.max_cache_size // 5
|
210
|
+
oldest_keys = list(self.cache.keys())[:remove_count]
|
211
|
+
for key in oldest_keys:
|
212
|
+
del self.cache[key]
|
213
|
+
|
214
|
+
self.cache[cache_key] = {
|
215
|
+
'pairs': pairs,
|
216
|
+
'original_history': chat_history.copy(), # Store copy for validation
|
217
|
+
'timestamp': time.time()
|
218
|
+
}
|
219
|
+
|
220
|
+
log.debug(f"Cached {len(pairs)} pairs for history of length {len(chat_history)}")
|
221
|
+
|
222
|
+
def clear_cache(self):
|
223
|
+
"""Clear the entire cache."""
|
224
|
+
self.cache.clear()
|
225
|
+
log.info("Chat history cache cleared")
|
226
|
+
|
227
|
+
|
228
|
+
# Global cache instance
|
229
|
+
_chat_history_cache = ChatHistoryCache()
|
230
|
+
|
231
|
+
|
232
|
+
def extract_chat_history_with_cache(chat_history: List[dict] = None) -> List[Tuple]:
|
233
|
+
"""
|
234
|
+
Main function to replace the original extract_chat_history.
|
235
|
+
|
236
|
+
Uses incremental caching for better performance with growing chat histories.
|
237
|
+
"""
|
238
|
+
if not chat_history:
|
239
|
+
log.debug("No chat history found")
|
240
|
+
return []
|
241
|
+
|
242
|
+
return _chat_history_cache.extract_chat_history_incremental(chat_history)
|
243
|
+
|
244
|
+
|
245
|
+
# Async version that wraps the cached version
|
246
|
+
async def extract_chat_history_async_cached(chat_history: List[dict] = None) -> List[Tuple]:
|
247
|
+
"""
|
248
|
+
Async version that uses the cache and runs in a thread pool if needed.
|
249
|
+
"""
|
250
|
+
import asyncio
|
251
|
+
|
252
|
+
if not chat_history:
|
253
|
+
return []
|
254
|
+
|
255
|
+
# For very large histories, run in thread pool to avoid blocking
|
256
|
+
if len(chat_history) > 1000:
|
257
|
+
loop = asyncio.get_event_loop()
|
258
|
+
return await loop.run_in_executor(
|
259
|
+
None,
|
260
|
+
extract_chat_history_with_cache,
|
261
|
+
chat_history
|
262
|
+
)
|
263
|
+
else:
|
264
|
+
# For smaller histories, just run directly
|
265
|
+
return extract_chat_history_with_cache(chat_history)
|
266
|
+
|
267
|
+
|
268
|
+
# Utility function to warm up the cache
|
269
|
+
def warm_up_cache(chat_histories: List[List[dict]]):
|
270
|
+
"""
|
271
|
+
Pre-populate cache with common chat histories.
|
272
|
+
|
273
|
+
Args:
|
274
|
+
chat_histories: List of chat history lists to cache
|
275
|
+
"""
|
276
|
+
for history in chat_histories:
|
277
|
+
extract_chat_history_with_cache(history)
|
278
|
+
|
279
|
+
log.info(f"Warmed up cache with {len(chat_histories)} chat histories")
|
280
|
+
|
281
|
+
|
282
|
+
async def extract_chat_history_async(chat_history=None):
|
283
|
+
"""
|
284
|
+
Extracts paired chat history between human and AI messages.
|
285
|
+
|
286
|
+
For this lightweight processing, we use a simpler approach that minimizes overhead.
|
287
|
+
|
288
|
+
Args:
|
289
|
+
chat_history (list): List of chat messages.
|
290
|
+
|
291
|
+
Returns:
|
292
|
+
list: List of tuples with paired human and AI messages.
|
293
|
+
"""
|
294
|
+
if not chat_history:
|
295
|
+
log.info("No chat history found")
|
296
|
+
return []
|
297
|
+
|
298
|
+
log.info(f"Extracting chat history: {chat_history}")
|
299
|
+
paired_messages = []
|
300
|
+
|
301
|
+
# Handle special case of initial bot message
|
302
|
+
if chat_history and is_bot(chat_history[0]):
|
303
|
+
first_message = chat_history[0]
|
304
|
+
log.info(f"Extracting first_message: {first_message}")
|
305
|
+
blank_human_message = {"name": "Human", "content": "", "embeds": []}
|
306
|
+
|
307
|
+
# Since create_message_element is so lightweight, we don't need async here
|
308
|
+
blank_element = create_message_element(blank_human_message)
|
309
|
+
bot_element = create_message_element(first_message)
|
310
|
+
|
311
|
+
paired_messages.append((blank_element, bot_element))
|
312
|
+
chat_history = chat_history[1:]
|
313
|
+
|
314
|
+
# Pre-process all messages in one batch (more efficient than one-by-one)
|
315
|
+
message_types = []
|
316
|
+
message_contents = []
|
317
|
+
|
318
|
+
for message in chat_history:
|
319
|
+
is_human_msg = is_human(message)
|
320
|
+
is_bot_msg = is_bot(message)
|
321
|
+
|
322
|
+
# Extract content for all messages at once
|
323
|
+
content = create_message_element(message)
|
324
|
+
|
325
|
+
message_types.append((is_human_msg, is_bot_msg))
|
326
|
+
message_contents.append(content)
|
327
|
+
|
328
|
+
# Pair messages efficiently
|
329
|
+
last_human_message = ""
|
330
|
+
for i, ((is_human_msg, is_bot_msg), content) in enumerate(zip(message_types, message_contents)):
|
331
|
+
if is_human_msg:
|
332
|
+
last_human_message = content
|
333
|
+
log.info(f"Extracted human message: {last_human_message}")
|
334
|
+
elif is_bot_msg:
|
335
|
+
ai_message = content
|
336
|
+
log.info(f"Extracted AI message: {ai_message}")
|
337
|
+
paired_messages.append((last_human_message, ai_message))
|
338
|
+
last_human_message = ""
|
339
|
+
|
340
|
+
log.info(f"Paired messages: {paired_messages}")
|
341
|
+
return paired_messages
|
342
|
+
|
343
|
+
|
344
|
+
def extract_chat_history(chat_history=None):
|
345
|
+
"""
|
346
|
+
Extracts paired chat history between human and AI messages.
|
347
|
+
|
348
|
+
This function takes a chat history and returns a list of pairs of messages,
|
349
|
+
where each pair consists of a human message followed by the corresponding AI response.
|
350
|
+
|
351
|
+
Args:
|
352
|
+
chat_history (list): List of chat messages.
|
353
|
+
|
354
|
+
Returns:
|
355
|
+
list: List of tuples with paired human and AI messages.
|
356
|
+
|
357
|
+
Example:
|
358
|
+
```python
|
359
|
+
chat_history = [
|
360
|
+
{"name": "Human", "text": "Hello, AI!"},
|
361
|
+
{"name": "AI", "text": "Hello, Human! How can I help you today?"}
|
362
|
+
]
|
363
|
+
paired_messages = extract_chat_history(chat_history)
|
364
|
+
print(paired_messages)
|
365
|
+
# Output: [("Hello, AI!", "Hello, Human! How can I help you today?")]
|
366
|
+
```
|
367
|
+
"""
|
368
|
+
if not chat_history:
|
369
|
+
log.info("No chat history found")
|
370
|
+
return []
|
371
|
+
|
372
|
+
log.info(f"Extracting chat history: {chat_history}")
|
373
|
+
paired_messages = []
|
374
|
+
|
375
|
+
first_message = chat_history[0]
|
376
|
+
log.info(f"Extracting first_message: {first_message}")
|
377
|
+
if is_bot(first_message):
|
378
|
+
blank_human_message = {"name": "Human", "content": "", "embeds": []}
|
379
|
+
paired_messages.append((create_message_element(blank_human_message),
|
380
|
+
create_message_element(first_message)))
|
381
|
+
chat_history = chat_history[1:]
|
382
|
+
|
383
|
+
last_human_message = ""
|
384
|
+
for message in chat_history:
|
385
|
+
log.info(f"Extracing message: {message}")
|
386
|
+
if is_human(message):
|
387
|
+
last_human_message = create_message_element(message)
|
388
|
+
log.info(f"Extracted human message: {last_human_message}")
|
389
|
+
elif is_bot(message):
|
390
|
+
ai_message = create_message_element(message)
|
391
|
+
log.info(f"Extracted AI message: {ai_message}")
|
392
|
+
paired_messages.append((last_human_message, ai_message))
|
393
|
+
last_human_message = ""
|
394
|
+
|
395
|
+
log.info(f"Paired messages: {paired_messages}")
|
396
|
+
|
397
|
+
return paired_messages
|
398
|
+
|
399
|
+
def embeds_to_json(message: dict):
|
400
|
+
"""
|
401
|
+
Converts the 'embeds' field in a message to a JSON string.
|
402
|
+
|
403
|
+
Args:
|
404
|
+
message (dict): The message containing the 'embeds' field.
|
405
|
+
|
406
|
+
Returns:
|
407
|
+
str: JSON string representation of the 'embeds' field or an empty string if no embeds are found.
|
408
|
+
|
409
|
+
Example:
|
410
|
+
```python
|
411
|
+
message = {"embeds": [{"type": "image", "url": "https://example.com/image.png"}]}
|
412
|
+
json_string = embeds_to_json(message)
|
413
|
+
print(json_string)
|
414
|
+
# Output: '[{"type": "image", "url": "https://example.com/image.png"}]'
|
415
|
+
```
|
416
|
+
"""
|
417
|
+
if 'embeds' in message and len(message['embeds']) > 0:
|
418
|
+
return json.dumps(message.get("embeds"))
|
419
|
+
else:
|
420
|
+
return ""
|
421
|
+
|
422
|
+
def create_message_element(message: dict):
|
423
|
+
"""
|
424
|
+
Extracts the main content of a message.
|
425
|
+
|
426
|
+
Args:
|
427
|
+
message (dict): The message to extract content from.
|
428
|
+
|
429
|
+
Returns:
|
430
|
+
str: The text or content of the message.
|
431
|
+
|
432
|
+
Raises:
|
433
|
+
KeyError: If neither 'content' nor 'text' fields are found.
|
434
|
+
|
435
|
+
Example:
|
436
|
+
```python
|
437
|
+
message = {"text": "Hello, AI!"}
|
438
|
+
content = create_message_element(message)
|
439
|
+
print(content)
|
440
|
+
# Output: 'Hello, AI!'
|
441
|
+
```
|
442
|
+
"""
|
443
|
+
if 'text' in message: # This is a Slack or Google Chat message
|
444
|
+
log.info(f"Found text element - {message['text']}")
|
445
|
+
return message['text']
|
446
|
+
elif 'content' in message: # Discord or OpenAI history message
|
447
|
+
log.info(f"Found content element - {message['content']}")
|
448
|
+
return message['content']
|
449
|
+
else:
|
450
|
+
raise KeyError(f"Could not extract 'content' or 'text' element from message: {message}, {type(message)}")
|
451
|
+
|
452
|
+
def is_human(message: dict):
|
453
|
+
"""
|
454
|
+
Checks if a message was sent by a human.
|
455
|
+
|
456
|
+
Args:
|
457
|
+
message (dict): The message to check.
|
458
|
+
|
459
|
+
Returns:
|
460
|
+
bool: True if the message was sent by a human, otherwise False.
|
461
|
+
|
462
|
+
Example:
|
463
|
+
```python
|
464
|
+
message = {"name": "Human"}
|
465
|
+
print(is_human(message))
|
466
|
+
# Output: True
|
467
|
+
```
|
468
|
+
"""
|
469
|
+
if 'name' in message:
|
470
|
+
return message["name"] == "Human"
|
471
|
+
elif 'sender' in message: # Google Chat
|
472
|
+
return message['sender']['type'] == 'HUMAN'
|
473
|
+
elif 'role' in message:
|
474
|
+
return message['role'] == 'user'
|
475
|
+
else:
|
476
|
+
# Slack: Check for the 'user' field and absence of 'bot_id' field
|
477
|
+
return 'user' in message and 'bot_id' not in message
|
478
|
+
|
479
|
+
def is_bot(message: dict):
|
480
|
+
"""
|
481
|
+
Checks if a message was sent by a bot.
|
482
|
+
|
483
|
+
Args:
|
484
|
+
message (dict): The message to check.
|
485
|
+
|
486
|
+
Returns:
|
487
|
+
bool: True if the message was sent by a bot, otherwise False.
|
488
|
+
|
489
|
+
Example:
|
490
|
+
```python
|
491
|
+
message = {"name": "AI"}
|
492
|
+
print(is_bot(message))
|
493
|
+
# Output: True
|
494
|
+
```
|
495
|
+
"""
|
496
|
+
return not is_human(message)
|
497
|
+
|
498
|
+
def is_ai(message: dict):
|
499
|
+
"""
|
500
|
+
Checks if a message was specifically sent by an AI.
|
501
|
+
|
502
|
+
Args:
|
503
|
+
message (dict): The message to check.
|
504
|
+
|
505
|
+
Returns:
|
506
|
+
bool: True if the message was sent by an AI, otherwise False.
|
507
|
+
|
508
|
+
Example:
|
509
|
+
```python
|
510
|
+
message = {"name": "AI"}
|
511
|
+
print(is_ai(message))
|
512
|
+
# Output: True
|
513
|
+
```
|
514
|
+
"""
|
515
|
+
if 'name' in message:
|
516
|
+
return message["name"] == "AI"
|
517
|
+
elif 'sender' in message: # Google Chat
|
518
|
+
return message['sender']['type'] == 'BOT'
|
519
|
+
elif 'role' in message:
|
520
|
+
return message['role'] == 'assistant'
|
521
|
+
else:
|
522
|
+
return 'bot_id' in message # Slack
|
523
|
+
|
@@ -7,8 +7,8 @@ from functools import partial
|
|
7
7
|
import inspect
|
8
8
|
import asyncio
|
9
9
|
|
10
|
-
from ...agents import
|
11
|
-
from ..chat_history import
|
10
|
+
from ...agents import handle_special_commands
|
11
|
+
from ..chat_history import extract_chat_history_with_cache, extract_chat_history_async_cached
|
12
12
|
from ...qna.parsers import parse_output
|
13
13
|
from ...streaming import start_streaming_chat, start_streaming_chat_async
|
14
14
|
from ...archive import archive_qa
|
@@ -58,12 +58,18 @@ if __name__ == "__main__":
|
|
58
58
|
```
|
59
59
|
|
60
60
|
"""
|
61
|
-
def __init__(self, app,
|
61
|
+
def __init__(self, app,
|
62
|
+
stream_interpreter: callable,
|
63
|
+
vac_interpreter:callable=None,
|
64
|
+
additional_routes:dict=None,
|
65
|
+
async_stream:bool=False,
|
66
|
+
add_langfuse_eval:bool=True):
|
62
67
|
self.app = app
|
63
68
|
self.stream_interpreter = stream_interpreter
|
64
69
|
self.vac_interpreter = vac_interpreter or partial(self.vac_interpreter_default)
|
65
70
|
self.additional_routes = additional_routes if additional_routes is not None else []
|
66
71
|
self.async_stream = async_stream
|
72
|
+
self.add_langfuse_eval = add_langfuse_eval
|
67
73
|
self.register_routes()
|
68
74
|
|
69
75
|
|
@@ -96,13 +102,9 @@ if __name__ == "__main__":
|
|
96
102
|
# Basic routes
|
97
103
|
self.app.route("/", methods=['GET'])(self.home)
|
98
104
|
self.app.route("/health", methods=['GET'])(self.health)
|
99
|
-
|
100
|
-
# Streaming VAC
|
101
|
-
self.app.route('/vac/streaming/<vector_name>',
|
102
|
-
methods=['POST'],
|
103
|
-
provide_automatic_options=False)(self.handle_stream_vac)
|
104
105
|
|
105
106
|
if self.async_stream: # Use async treatment
|
107
|
+
log.info("async_stream enabled")
|
106
108
|
self.app.route('/vac/streaming/<vector_name>',
|
107
109
|
methods=['POST'],
|
108
110
|
provide_automatic_options=False)(self.handle_stream_vac_async)
|
@@ -351,10 +353,10 @@ if __name__ == "__main__":
|
|
351
353
|
|
352
354
|
# Use the async version of prep_vac
|
353
355
|
prep = await self.prep_vac_async(request, vector_name)
|
354
|
-
log.info(f"Processing prep: {prep}")
|
356
|
+
log.info(f"Processing async prep: {prep}")
|
355
357
|
all_input = prep["all_input"]
|
356
358
|
|
357
|
-
log.info(f'Streaming data with: {all_input}')
|
359
|
+
log.info(f'Streaming async data with: {all_input}')
|
358
360
|
|
359
361
|
async def generate_response_content():
|
360
362
|
try:
|
@@ -378,12 +380,12 @@ if __name__ == "__main__":
|
|
378
380
|
yield chunk
|
379
381
|
|
380
382
|
except Exception as e:
|
381
|
-
yield f"Streaming Error: {str(e)} {traceback.format_exc()}"
|
383
|
+
yield f"Streaming async Error: {str(e)} {traceback.format_exc()}"
|
382
384
|
|
383
385
|
response = Response(generate_response_content(), content_type='text/plain; charset=utf-8')
|
384
386
|
response.headers['Transfer-Encoding'] = 'chunked'
|
385
387
|
|
386
|
-
log.debug(f"streaming response: {response}")
|
388
|
+
log.debug(f"streaming async response: {response}")
|
387
389
|
|
388
390
|
return response
|
389
391
|
|
@@ -554,7 +556,8 @@ if __name__ == "__main__":
|
|
554
556
|
else:
|
555
557
|
log.info(f"User message: {user_message}")
|
556
558
|
|
557
|
-
paired_messages =
|
559
|
+
paired_messages = extract_chat_history_with_cache(chat_history)
|
560
|
+
|
558
561
|
command_response = handle_special_commands(user_message, vector_name, paired_messages)
|
559
562
|
|
560
563
|
if command_response is not None:
|
@@ -698,10 +701,10 @@ if __name__ == "__main__":
|
|
698
701
|
|
699
702
|
trace = None
|
700
703
|
span = None
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
704
|
+
if self.add_langfuse_eval:
|
705
|
+
trace_id = data.get('trace_id')
|
706
|
+
trace = self.create_langfuse_trace(request, vector_name, trace_id)
|
707
|
+
log.info(f"Using existing langfuse trace: {trace_id}")
|
705
708
|
|
706
709
|
#config, _ = load_config("config/llm_config.yaml")
|
707
710
|
try:
|
@@ -725,7 +728,7 @@ if __name__ == "__main__":
|
|
725
728
|
vector_name = data.pop('vector_name', vector_name)
|
726
729
|
data.pop('trace_id', None) # to ensure not in kwargs
|
727
730
|
|
728
|
-
paired_messages =
|
731
|
+
paired_messages = extract_chat_history_with_cache(chat_history)
|
729
732
|
|
730
733
|
all_input = {'user_input': user_input,
|
731
734
|
'vector_name': vector_name,
|
@@ -741,15 +744,10 @@ if __name__ == "__main__":
|
|
741
744
|
metadata=vac_config.configs_by_kind,
|
742
745
|
input = all_input
|
743
746
|
)
|
744
|
-
command_response = handle_special_commands(user_input, vector_name, paired_messages)
|
745
|
-
if command_response is not None:
|
746
|
-
if trace:
|
747
|
-
trace.update(output=jsonify(command_response))
|
748
747
|
|
749
748
|
return {
|
750
749
|
"trace": trace,
|
751
750
|
"span": span,
|
752
|
-
"command_response": command_response,
|
753
751
|
"all_input": all_input,
|
754
752
|
"vac_config": vac_config
|
755
753
|
}
|
@@ -793,7 +791,7 @@ if __name__ == "__main__":
|
|
793
791
|
data.pop('trace_id', None) # to ensure not in kwargs
|
794
792
|
|
795
793
|
# Task 3: Process chat history
|
796
|
-
chat_history_task = asyncio.create_task(
|
794
|
+
chat_history_task = asyncio.create_task(extract_chat_history_async_cached(chat_history))
|
797
795
|
tasks.append(chat_history_task)
|
798
796
|
|
799
797
|
# Await all tasks concurrently
|