flowcept 0.9.6__tar.gz → 0.9.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.
- {flowcept-0.9.6 → flowcept-0.9.7}/PKG-INFO +14 -1
- flowcept-0.9.7/docs/task_schema.rst +157 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/pyproject.toml +7 -1
- {flowcept-0.9.6 → flowcept-0.9.7}/resources/sample_settings.yaml +2 -1
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/gui/agent_gui.py +31 -20
- flowcept-0.9.7/src/flowcept/agents/gui/audio_utils.py +129 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/gui/gui_utils.py +157 -5
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/prompts/general_prompts.py +2 -2
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/prompts/in_memory_query_prompts.py +5 -4
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/general_tools.py +3 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/in_memory_queries/in_memory_queries_tools.py +3 -1
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/task_data_preprocess.py +2 -1
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/configs.py +7 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/version.py +1 -1
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/flowcept_explicit_tasks.py +14 -3
- flowcept-0.9.6/docs/task_schema.rst +0 -114
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/checks.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/create-release-n-publish.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-llm-tests.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-all-dbs.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-in-container.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-kafka.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-py313.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-simple.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run_examples.sh +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/version_bumper.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.gitignore +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/.readthedocs.yaml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/CONTRIBUTING.md +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/LICENSE +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/Makefile +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/README.md +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/deployment/Dockerfile +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-grafana.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-kafka.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-mofka.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-mongo.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose.yml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/api-reference.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/architecture.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/cli-reference.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/conf.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/contributing.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/img/architecture-diagram.png +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/img/flowcept-logo-dark.png +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/img/flowcept-logo.png +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/index.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/prov_capture.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/prov_storage.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/quick_start.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/schemas.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/setup.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/telemetry_capture.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/docs/workflow_schema.rst +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/a2a/README.md +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/a2a/agent1.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/a2a/agent2.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/aec_agent_context_manager.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/aec_agent_mock.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/aec_prompts.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/opt_driver_mock.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/consumers/ping_pong_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/consumers/simple_consumer.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/consumers/simple_publisher.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/convergence_loop_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/dask_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/distributed_consumer_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/instrumented_loop_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/instrumented_simple_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/README.md +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/custom_provenance_id_mapping.yaml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_dataprep.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_main_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_model.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_test_runner.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/mlflow_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/mqtt_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/single_layer_perceptron_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/start_here.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/tensorboard_example.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/unmanaged/main.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/examples/unmanaged/simple_task.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/analytics.ipynb +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/dask.ipynb +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/dask_from_CLI.ipynb +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/mlflow.ipynb +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/reset_dask_nb_exec_counts.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/tensorboard.ipynb +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/bedrock_setup.sh +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/consumer.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/mofka-requirements.yaml +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/mofka_config.json +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/resources/simple_redis_consumer.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/agent_client.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/agents_utils.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/dynamic_schema_tracker.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/flowcept_agent.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/flowcept_ctx_manager.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/gui/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/llms/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/llms/claude_gcp.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/llms/gemini25.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/prompts/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/in_memory_queries/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/in_memory_queries/pandas_agent_utils.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/analytics_utils.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/data_augmentation.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/plot.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/cli.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/autoflush_buffer.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/docdb_dao_base.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/lmdb_dao.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/mongodb_dao.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/keyvalue_dao.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_base.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_kafka.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_mofka.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_redis.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/redis_conn.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/base_settings_dataclasses.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/task_object.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/telemetry.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/workflow_object.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_logger.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/query_utils.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/settings_factory.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/utils.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/vocabulary.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/db_api.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/flowcept_controller.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/task_query_api.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/app.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/resources/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/resources/query_rsrc.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/resources/task_messages_rsrc.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/base_interceptor.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/brokers/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/brokers/mqtt_interceptor.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/dask_dataclasses.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/dask_interceptor.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/dask_plugins.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/instrumentation_interceptor.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/interceptor_state_manager.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/interception_event_handler.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/mlflow_dao.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/mlflow_dataclasses.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/mlflow_interceptor.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/tensorboard/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/tensorboard/tensorboard_dataclasses.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/tensorboard/tensorboard_interceptor.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/agent/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/agent/base_agent_context_manager.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/base_consumer.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/consumer_utils.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/document_inserter.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/telemetry_capture.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_agent_task.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_decorator.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_loop.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_task.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_torch.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/task_capture.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/dask_test_utils.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_broker.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_dask.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_dask_with_context_mgmt.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_file_observer.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_mlflow.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_tensorboard.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/db_api_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/flowcept_api_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/sample_data.json +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/sample_data_with_telemetry_and_rai.json +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/task_query_api_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/doc_db_inserter/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/doc_db_inserter/doc_db_inserter_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/flowcept_loop_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/flowcept_task_decorator_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/dl_trainer.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/ml_decorator_dask_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/ml_decorator_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/misc_tests/__init__.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/misc_tests/log_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/misc_tests/singleton_test.py +0 -0
- {flowcept-0.9.6 → flowcept-0.9.7}/tests/misc_tests/telemetry_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowcept
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.7
|
|
4
4
|
Summary: Capture and query workflow provenance data using data observability
|
|
5
5
|
Author: Oak Ridge National Laboratory
|
|
6
6
|
License-Expression: MIT
|
|
@@ -38,6 +38,7 @@ Requires-Dist: psutil>=6.1.1; extra == 'all'
|
|
|
38
38
|
Requires-Dist: py-cpuinfo; extra == 'all'
|
|
39
39
|
Requires-Dist: pyarrow; extra == 'all'
|
|
40
40
|
Requires-Dist: pymongo; extra == 'all'
|
|
41
|
+
Requires-Dist: pymupdf; extra == 'all'
|
|
41
42
|
Requires-Dist: pytest; extra == 'all'
|
|
42
43
|
Requires-Dist: pyyaml; extra == 'all'
|
|
43
44
|
Requires-Dist: redis; extra == 'all'
|
|
@@ -88,12 +89,24 @@ Provides-Extra: llm-agent
|
|
|
88
89
|
Requires-Dist: langchain-community; extra == 'llm-agent'
|
|
89
90
|
Requires-Dist: langchain-openai; extra == 'llm-agent'
|
|
90
91
|
Requires-Dist: mcp[cli]; extra == 'llm-agent'
|
|
92
|
+
Requires-Dist: pymupdf; extra == 'llm-agent'
|
|
91
93
|
Requires-Dist: streamlit; extra == 'llm-agent'
|
|
94
|
+
Provides-Extra: llm-agent-audio
|
|
95
|
+
Requires-Dist: gtts; extra == 'llm-agent-audio'
|
|
96
|
+
Requires-Dist: langchain-community; extra == 'llm-agent-audio'
|
|
97
|
+
Requires-Dist: langchain-openai; extra == 'llm-agent-audio'
|
|
98
|
+
Requires-Dist: mcp[cli]; extra == 'llm-agent-audio'
|
|
99
|
+
Requires-Dist: pydub; extra == 'llm-agent-audio'
|
|
100
|
+
Requires-Dist: pymupdf; extra == 'llm-agent-audio'
|
|
101
|
+
Requires-Dist: speechrecognition; extra == 'llm-agent-audio'
|
|
102
|
+
Requires-Dist: streamlit; extra == 'llm-agent-audio'
|
|
103
|
+
Requires-Dist: streamlit-mic-recorder; extra == 'llm-agent-audio'
|
|
92
104
|
Provides-Extra: llm-google
|
|
93
105
|
Requires-Dist: google-genai; extra == 'llm-google'
|
|
94
106
|
Requires-Dist: langchain-community; extra == 'llm-google'
|
|
95
107
|
Requires-Dist: langchain-openai; extra == 'llm-google'
|
|
96
108
|
Requires-Dist: mcp[cli]; extra == 'llm-google'
|
|
109
|
+
Requires-Dist: pymupdf; extra == 'llm-google'
|
|
97
110
|
Requires-Dist: streamlit; extra == 'llm-google'
|
|
98
111
|
Provides-Extra: lmdb
|
|
99
112
|
Requires-Dist: lmdb; extra == 'lmdb'
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Task Data Schema
|
|
2
|
+
================
|
|
3
|
+
|
|
4
|
+
This document describes the schema of a task record used to capture metadata, telemetry, and provenance in a workflow.
|
|
5
|
+
A task represents one unit of work, including inputs, outputs, execution context, system telemetry, and runtime provenance.
|
|
6
|
+
|
|
7
|
+
Each task record may include fields for identifiers, timing, telemetry, user and system context, dependencies, and custom metadata.
|
|
8
|
+
|
|
9
|
+
Task Fields
|
|
10
|
+
-----------
|
|
11
|
+
|
|
12
|
+
- **type**: Constant type label (``"task"``) (string)
|
|
13
|
+
- **subtype**: Optional subtype of the task, e.g., iteration, ML step, custom (string)
|
|
14
|
+
|
|
15
|
+
Identifiers
|
|
16
|
+
~~~~~~~~~~~
|
|
17
|
+
|
|
18
|
+
- **task_id**: Unique identifier for the task (string)
|
|
19
|
+
- **workflow_id**: Identifier for the workflow this task belongs to (string)
|
|
20
|
+
- **workflow_name**: Name of the workflow this task belongs to (string)
|
|
21
|
+
- **campaign_id**: Identifier for the campaign this task belongs to (string)
|
|
22
|
+
- **activity_id**: Identifier for the activity performed by the task (usually a function name) (string)
|
|
23
|
+
- **group_id**: Identifier grouping related tasks, e.g., loop iterations (string)
|
|
24
|
+
- **parent_task_id**: Identifier of the parent task, if nested (string)
|
|
25
|
+
- **agent_id**: Identifier of the agent responsible for executing the task (string)
|
|
26
|
+
- **adapter_id**: Identifier of the adapter that produced this task (string)
|
|
27
|
+
- **environment_id**: Identifier of the environment where the task ran (string)
|
|
28
|
+
|
|
29
|
+
Timing
|
|
30
|
+
~~~~~~
|
|
31
|
+
|
|
32
|
+
- **utc_timestamp**: UTC timestamp when the task object was created (float)
|
|
33
|
+
- **submitted_at**: Timestamp when the task was submitted (float)
|
|
34
|
+
- **started_at**: Timestamp when execution started (float)
|
|
35
|
+
- **ended_at**: Timestamp when execution ended (float)
|
|
36
|
+
- **registered_at**: Timestamp when registered in storage (float)
|
|
37
|
+
|
|
38
|
+
Provenance Data
|
|
39
|
+
~~~~~~~~~~~~~~~
|
|
40
|
+
|
|
41
|
+
- **used**: Inputs consumed by the task, such as parameters, files, or resources (dictionary)
|
|
42
|
+
- **generated**: Outputs produced by the task, e.g., results, artifacts, files (dictionary)
|
|
43
|
+
- **dependencies**: List of task IDs this task depends on (list)
|
|
44
|
+
- **dependents**: List of task IDs that depend on this task (list)
|
|
45
|
+
|
|
46
|
+
Execution Metadata
|
|
47
|
+
~~~~~~~~~~~~~~~~~~
|
|
48
|
+
|
|
49
|
+
- **status**: Execution status of the task (e.g., FINISHED, ERROR) (string)
|
|
50
|
+
- **stdout**: Captured standard output (string or dictionary)
|
|
51
|
+
- **stderr**: Captured standard error (string or dictionary)
|
|
52
|
+
- **data**: Arbitrary raw payload associated with the task (any type)
|
|
53
|
+
- **custom_metadata**: User- or developer-provided metadata dictionary (dictionary)
|
|
54
|
+
- **tags**: User-defined tags attached to the task (list)
|
|
55
|
+
|
|
56
|
+
User and System Context
|
|
57
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
58
|
+
|
|
59
|
+
- **user**: User who executed or triggered the task (string)
|
|
60
|
+
- **login_name**: Login name of the user in the environment (string)
|
|
61
|
+
- **node_name**: Node where the task executed (string)
|
|
62
|
+
- **hostname**: Hostname of the machine executing the task (string)
|
|
63
|
+
- **public_ip**: Public IP address (string)
|
|
64
|
+
- **private_ip**: Private IP address (string)
|
|
65
|
+
- **address**: Optional network address (string)
|
|
66
|
+
- **mq_host**: Message queue host associated with the task (string)
|
|
67
|
+
|
|
68
|
+
Telemetry Data Schema
|
|
69
|
+
---------------------
|
|
70
|
+
|
|
71
|
+
If telemetry capture is enabled, telemetry snapshots are stored in ``telemetry_at_start`` and ``telemetry_at_end``.
|
|
72
|
+
Each is a dictionary with the following structure:
|
|
73
|
+
|
|
74
|
+
CPU
|
|
75
|
+
~~~
|
|
76
|
+
|
|
77
|
+
- **times_avg**: Dictionary with CPU times
|
|
78
|
+
- **user**, **nice**, **system**, **idle**
|
|
79
|
+
- **percent_all**: Overall CPU usage percentage
|
|
80
|
+
- **frequency**: CPU frequency
|
|
81
|
+
- **times_per_cpu**: List of dictionaries of per-core times
|
|
82
|
+
- **percent_per_cpu**: List of per-core usage percentages
|
|
83
|
+
|
|
84
|
+
Process
|
|
85
|
+
~~~~~~~
|
|
86
|
+
|
|
87
|
+
- **pid**: Process ID
|
|
88
|
+
- **memory**: Dictionary with memory stats
|
|
89
|
+
- **rss**, **vms**, **pfaults**, **pageins**
|
|
90
|
+
- **memory_percent**: Memory usage percentage
|
|
91
|
+
- **cpu_times**: Dictionary with CPU times
|
|
92
|
+
- **user**, **system**, **children_user**, **children_system**
|
|
93
|
+
- **cpu_percent**: CPU usage percentage
|
|
94
|
+
- **executable**: Path to executable
|
|
95
|
+
- **cmd_line**: Command line arguments
|
|
96
|
+
- **num_open_file_descriptors**, **num_connections**, **num_open_files**, **num_threads**
|
|
97
|
+
- **num_ctx_switches**: Dictionary with
|
|
98
|
+
- **voluntary**, **involuntary**
|
|
99
|
+
|
|
100
|
+
Memory
|
|
101
|
+
~~~~~~
|
|
102
|
+
|
|
103
|
+
- **virtual**: Dictionary with virtual memory stats
|
|
104
|
+
- **total**, **available**, **percent**, **used**, **free**, **active**, **inactive**, **wired**
|
|
105
|
+
- **swap**: Dictionary with swap memory stats
|
|
106
|
+
- **total**, **used**, **free**, **percent**, **sin**, **sout**
|
|
107
|
+
|
|
108
|
+
Disk
|
|
109
|
+
~~~~
|
|
110
|
+
|
|
111
|
+
- **disk_usage**: Dictionary with usage stats
|
|
112
|
+
- **total**, **used**, **free**, **percent**
|
|
113
|
+
- **io_sum**: Dictionary with I/O stats
|
|
114
|
+
- **read_count**, **write_count**, **read_bytes**, **write_bytes**, **read_time**, **write_time**
|
|
115
|
+
|
|
116
|
+
Network
|
|
117
|
+
~~~~~~~
|
|
118
|
+
|
|
119
|
+
- **netio_sum**: Dictionary with aggregate network I/O
|
|
120
|
+
- **bytes_sent**, **bytes_recv**, **packets_sent**, **packets_recv**, **errin**, **errout**, **dropin**, **dropout**
|
|
121
|
+
- **netio_per_interface**: Dictionary keyed by interface with same metrics
|
|
122
|
+
|
|
123
|
+
GPU
|
|
124
|
+
~~~
|
|
125
|
+
|
|
126
|
+
GPU telemetry data, if available, is in the ``gpu`` field. Structure depends on vendor.
|
|
127
|
+
|
|
128
|
+
**Common Fields**
|
|
129
|
+
|
|
130
|
+
- **gpu_ix**: GPU index (int)
|
|
131
|
+
- **used**: Memory used (bytes)
|
|
132
|
+
- **temperature**: Dictionary or integer temperature
|
|
133
|
+
- **power**: Dictionary or value of power usage
|
|
134
|
+
- **id**: Device UUID
|
|
135
|
+
- **name**: GPU name (NVIDIA only)
|
|
136
|
+
- **activity**: GPU activity percentage (AMD only)
|
|
137
|
+
- **others**: Clock/performance data (AMD only)
|
|
138
|
+
|
|
139
|
+
**AMD GPU**
|
|
140
|
+
|
|
141
|
+
- **temperature**: edge, hotspot, mem, vrgfx, vrmem, hbm, fan_speed
|
|
142
|
+
- **power**: average_socket_power, energy_accumulator
|
|
143
|
+
- **others**: current_gfxclk, current_socclk, current_uclk, current_vclk0, current_dclk0
|
|
144
|
+
|
|
145
|
+
**NVIDIA GPU**
|
|
146
|
+
|
|
147
|
+
- **temperature**: Celsius value
|
|
148
|
+
- **power**: Milliwatts usage
|
|
149
|
+
- **used**: Memory used (bytes)
|
|
150
|
+
- **name**: Model name
|
|
151
|
+
- **id**: Device UUID
|
|
152
|
+
|
|
153
|
+
Notes
|
|
154
|
+
-----
|
|
155
|
+
|
|
156
|
+
Telemetry values vary depending on system capabilities, GPU vendor APIs,
|
|
157
|
+
and what is enabled in the configuration.
|
|
@@ -65,8 +65,14 @@ mlflow = ["mlflow-skinny", "SQLAlchemy", "alembic", "watchdog", "cryptography"]
|
|
|
65
65
|
nvidia = ["nvidia-ml-py"]
|
|
66
66
|
mqtt = ["paho-mqtt"]
|
|
67
67
|
tensorboard = ["tensorboard", "tensorflow", "tbparse"]
|
|
68
|
-
llm_agent = ["mcp[cli]", "langchain_community", "streamlit", "
|
|
68
|
+
llm_agent = ["mcp[cli]", "langchain_community", "langchain_openai", "streamlit", "PyMuPDF"]
|
|
69
69
|
llm_google = ["flowcept[llm_agent]", "google-genai"]
|
|
70
|
+
llm_agent_audio = ["flowcept[llm_agent]", "streamlit-mic-recorder", "SpeechRecognition", "pydub", "gTTS"]
|
|
71
|
+
# System dependency (required for pydub)
|
|
72
|
+
# Install ffmpeg on the system (not a pip package):
|
|
73
|
+
# macOS (Homebrew): brew install ffmpeg
|
|
74
|
+
# Conda (any OS): conda install -c conda-forge ffmpeg
|
|
75
|
+
# Debian/Ubuntu: sudo apt-get install ffmpeg
|
|
70
76
|
|
|
71
77
|
dev = [
|
|
72
78
|
"flowcept[docs]",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
flowcept_version: 0.9.
|
|
1
|
+
flowcept_version: 0.9.7 # Version of the Flowcept package. This setting file is compatible with this version.
|
|
2
2
|
|
|
3
3
|
project:
|
|
4
4
|
debug: true # Toggle debug mode. This will add a property `debug: true` to all saved data, making it easier to retrieve/delete them later.
|
|
@@ -91,6 +91,7 @@ agent:
|
|
|
91
91
|
model: '?'
|
|
92
92
|
service_provider: '?'
|
|
93
93
|
model_kwargs: {}
|
|
94
|
+
audio_enabled: false
|
|
94
95
|
|
|
95
96
|
databases:
|
|
96
97
|
|
|
@@ -1,76 +1,87 @@
|
|
|
1
1
|
import streamlit as st
|
|
2
|
+
|
|
2
3
|
from flowcept.agents.gui import AI, PAGE_TITLE
|
|
4
|
+
from flowcept.agents.gui.audio_utils import get_audio_text
|
|
3
5
|
from flowcept.agents.gui.gui_utils import (
|
|
4
6
|
query_agent,
|
|
5
7
|
display_ai_msg,
|
|
6
8
|
display_ai_msg_from_tool,
|
|
7
9
|
display_df_tool_response,
|
|
10
|
+
resolve_logo_path,
|
|
11
|
+
render_title_with_logo,
|
|
8
12
|
)
|
|
9
|
-
|
|
10
13
|
from flowcept.agents.tools.in_memory_queries.in_memory_queries_tools import (
|
|
11
14
|
generate_result_df,
|
|
12
15
|
generate_plot_code,
|
|
13
16
|
run_df_code,
|
|
14
17
|
)
|
|
18
|
+
from flowcept.configs import AGENT_AUDIO
|
|
15
19
|
|
|
20
|
+
# ---- Page setup & header with logo ----
|
|
16
21
|
st.set_page_config(page_title=PAGE_TITLE, page_icon=AI)
|
|
17
|
-
|
|
22
|
+
|
|
23
|
+
LOGO_PATH = resolve_logo_path(package="flowcept", resource="docs/img/flowcept-logo.png")
|
|
24
|
+
render_title_with_logo(PAGE_TITLE, LOGO_PATH, logo_width=150, add_to_sidebar=False, debug=False)
|
|
18
25
|
|
|
19
26
|
GREETING = (
|
|
20
|
-
"Hi, there! I'm
|
|
27
|
+
"Hi, there! I'm your **Workflow Provenance Assistant**.\n\n"
|
|
21
28
|
"I am tracking workflow executions and I can:\n"
|
|
22
|
-
"- 🔍
|
|
29
|
+
"- 🔍 Query running workflows\n"
|
|
23
30
|
"- 📊 Plot graphs\n"
|
|
24
31
|
"- 🤖 Answer general questions about provenance data\n\n"
|
|
25
32
|
"How can I help you today?"
|
|
26
33
|
)
|
|
27
|
-
|
|
28
|
-
|
|
29
34
|
display_ai_msg(GREETING)
|
|
30
35
|
|
|
31
|
-
# if "chat_history" not in st.session_state:
|
|
32
|
-
# st.session_state.chat_history = [{"role": "system", "content":GREETING}]
|
|
33
|
-
#
|
|
34
|
-
# for msg in st.session_state.chat_history:
|
|
35
|
-
# with st.chat_message(msg["role"], avatar=AI):
|
|
36
|
-
# st.markdown(msg["content"])
|
|
37
|
-
|
|
38
36
|
|
|
39
37
|
def main():
|
|
40
|
-
"""Main
|
|
38
|
+
"""Main Agent GUI function."""
|
|
39
|
+
st.caption(
|
|
40
|
+
"💡 Tip: Ask about workflow metrics, generate plots, or summarize data. "
|
|
41
|
+
"Inputs are mapped to `used` and outputs to `generated` fields. "
|
|
42
|
+
"Use @record <your query guidance> if you have custom guidance."
|
|
43
|
+
)
|
|
44
|
+
|
|
41
45
|
user_input = st.chat_input("Send a message")
|
|
42
|
-
st.caption("💡 Tip: Ask about workflow metrics, generate plots, or summarize data.")
|
|
43
46
|
|
|
44
47
|
if user_input:
|
|
45
|
-
|
|
48
|
+
st.session_state["speak_reply"] = False
|
|
49
|
+
|
|
50
|
+
if AGENT_AUDIO:
|
|
51
|
+
user_input = get_audio_text(user_input)
|
|
46
52
|
|
|
53
|
+
if user_input:
|
|
47
54
|
with st.chat_message("human"):
|
|
48
55
|
st.markdown(user_input)
|
|
49
56
|
|
|
50
57
|
try:
|
|
51
58
|
with st.spinner("🤖 Thinking..."):
|
|
52
59
|
tool_result = query_agent(user_input)
|
|
53
|
-
print(tool_result)
|
|
54
60
|
|
|
55
61
|
if tool_result.result_is_str():
|
|
56
62
|
display_ai_msg_from_tool(tool_result)
|
|
63
|
+
|
|
57
64
|
elif tool_result.is_success_dict():
|
|
58
65
|
tool_name = tool_result.tool_name
|
|
59
|
-
if tool_name in
|
|
66
|
+
if tool_name in (
|
|
67
|
+
generate_result_df.__name__,
|
|
68
|
+
generate_plot_code.__name__,
|
|
69
|
+
run_df_code.__name__,
|
|
70
|
+
):
|
|
60
71
|
display_df_tool_response(tool_result)
|
|
61
72
|
else:
|
|
62
73
|
display_ai_msg(f"⚠️ Received unexpected response from agent: {tool_result}")
|
|
63
74
|
st.stop()
|
|
64
75
|
else:
|
|
65
76
|
display_df_tool_response(tool_result)
|
|
66
|
-
# display_ai_msg(f"⚠️ Received unexpected response from agent: {tool_result}")
|
|
67
77
|
st.stop()
|
|
68
78
|
|
|
69
79
|
except Exception as e:
|
|
70
80
|
display_ai_msg(f"❌ Error talking to MCP agent:\n\n```text\n{e}\n```")
|
|
71
81
|
st.stop()
|
|
72
82
|
|
|
73
|
-
# st.session_state.chat_history.append({"role": "system", "content": agent_reply})
|
|
74
83
|
|
|
84
|
+
if "speak_reply" not in st.session_state:
|
|
85
|
+
st.session_state["speak_reply"] = False
|
|
75
86
|
|
|
76
87
|
main()
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import tempfile
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
import base64
|
|
5
|
+
|
|
6
|
+
import streamlit as st
|
|
7
|
+
from gtts import gTTS
|
|
8
|
+
from streamlit_mic_recorder import mic_recorder
|
|
9
|
+
import speech_recognition as sr
|
|
10
|
+
from pydub import AudioSegment # needs ffmpeg installed
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _normalize_mic_output(out) -> bytes | None:
|
|
14
|
+
"""Handle different return shapes from streamlit-mic-recorder."""
|
|
15
|
+
if not isinstance(out, dict):
|
|
16
|
+
return None
|
|
17
|
+
if out.get("wav"):
|
|
18
|
+
return out["wav"]
|
|
19
|
+
if out.get("bytes"):
|
|
20
|
+
return out["bytes"]
|
|
21
|
+
if out.get("b64"):
|
|
22
|
+
return base64.b64decode(out["b64"])
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _is_wav_pcm(blob: bytes) -> bool:
|
|
27
|
+
"""Quick RIFF/WAVE header check."""
|
|
28
|
+
h = blob[:12]
|
|
29
|
+
return h.startswith(b"RIFF") and h[8:12] == b"WAVE"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _to_pcm_wav_16k(blob: bytes) -> bytes:
|
|
33
|
+
"""
|
|
34
|
+
Convert arbitrary audio bytes (webm/ogg/mp3/…) to 16-bit PCM WAV mono @16k.
|
|
35
|
+
Requires ffmpeg via pydub.
|
|
36
|
+
"""
|
|
37
|
+
if _is_wav_pcm(blob):
|
|
38
|
+
return blob
|
|
39
|
+
seg = AudioSegment.from_file(BytesIO(blob)) # ffmpeg does the heavy lifting
|
|
40
|
+
seg = seg.set_channels(1).set_frame_rate(16000).set_sample_width(2)
|
|
41
|
+
buf = BytesIO()
|
|
42
|
+
seg.export(buf, format="wav")
|
|
43
|
+
return buf.getvalue()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_audio_text(user_input: str) -> str:
|
|
47
|
+
"""
|
|
48
|
+
User Audio Getter.
|
|
49
|
+
"""
|
|
50
|
+
# Voice input expander
|
|
51
|
+
with st.expander("🎤 Voice input", expanded=False):
|
|
52
|
+
st.caption("Click **Speak**, talk, then **Stop**. Allow mic permission in your browser.")
|
|
53
|
+
out = mic_recorder(
|
|
54
|
+
start_prompt="🎙️ Speak",
|
|
55
|
+
stop_prompt="⏹️ Stop",
|
|
56
|
+
key="mic_rec_1",
|
|
57
|
+
use_container_width=True,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Normalize outputs from the component
|
|
61
|
+
raw_audio = _normalize_mic_output(out)
|
|
62
|
+
|
|
63
|
+
if raw_audio:
|
|
64
|
+
try:
|
|
65
|
+
wav_bytes = _to_pcm_wav_16k(raw_audio)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
st.error(f"Could not convert audio to WAV (need ffmpeg/ffprobe?): {e}")
|
|
68
|
+
wav_bytes = None
|
|
69
|
+
|
|
70
|
+
if wav_bytes:
|
|
71
|
+
st.audio(wav_bytes, format="audio/wav")
|
|
72
|
+
|
|
73
|
+
# Transcribe with SpeechRecognition
|
|
74
|
+
r = sr.Recognizer()
|
|
75
|
+
try:
|
|
76
|
+
with sr.AudioFile(BytesIO(wav_bytes)) as source:
|
|
77
|
+
audio = r.record(source)
|
|
78
|
+
voice_text = r.recognize_google(audio) # type: ignore[attr-defined]
|
|
79
|
+
st.success(f"You said: {voice_text}")
|
|
80
|
+
if not user_input:
|
|
81
|
+
user_input = voice_text
|
|
82
|
+
st.session_state["speak_reply"] = True # speak back only when voice was used
|
|
83
|
+
print(f"Setting session state to {st.session_state['speak_reply']}")
|
|
84
|
+
except Exception as e:
|
|
85
|
+
st.warning(f"Transcription failed: {e}")
|
|
86
|
+
|
|
87
|
+
return user_input
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def speech_to_text():
|
|
91
|
+
"""Record from mic, return transcribed text or None."""
|
|
92
|
+
rec = mic_recorder(
|
|
93
|
+
start_prompt="🎙️ Speak",
|
|
94
|
+
stop_prompt="⏹️ Stop",
|
|
95
|
+
key="mic",
|
|
96
|
+
use_container_width=True,
|
|
97
|
+
)
|
|
98
|
+
if rec and "wav" in rec:
|
|
99
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
|
|
100
|
+
tmp.write(rec["wav"])
|
|
101
|
+
tmp.flush()
|
|
102
|
+
r = sr.Recognizer()
|
|
103
|
+
with sr.AudioFile(tmp.name) as source:
|
|
104
|
+
audio = r.record(source)
|
|
105
|
+
try:
|
|
106
|
+
return r.recognize_google(audio)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
st.warning(f"Speech recognition failed: {e}")
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def speak(text: str):
|
|
113
|
+
"""Synthesize speech for the agent reply and play it."""
|
|
114
|
+
if not text:
|
|
115
|
+
return
|
|
116
|
+
try:
|
|
117
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
|
|
118
|
+
gTTS(text).save(tmp.name)
|
|
119
|
+
st.audio(tmp.name, format="audio/mp3")
|
|
120
|
+
except Exception as e:
|
|
121
|
+
st.warning(f"TTS failed: {e}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _md_to_plain_text(s: str) -> str:
|
|
125
|
+
"""Very light Markdown cleanup for TTS."""
|
|
126
|
+
s = re.sub(r"```.*?```", lambda m: m.group(0).replace("```", ""), s, flags=re.S) # drop fences
|
|
127
|
+
s = s.replace("`", "") # inline code ticks
|
|
128
|
+
s = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", s) # links: [text](url) -> text
|
|
129
|
+
return s.strip()
|
|
@@ -2,14 +2,20 @@ import base64
|
|
|
2
2
|
import ast
|
|
3
3
|
import io
|
|
4
4
|
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from importlib.resources import files as pkg_files
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
5
9
|
|
|
6
10
|
import streamlit as st
|
|
11
|
+
from flowcept.agents.gui import AI
|
|
7
12
|
from flowcept.agents import prompt_handler
|
|
8
13
|
from flowcept.agents.agent_client import run_tool
|
|
9
14
|
from flowcept.agents.agents_utils import ToolResult
|
|
10
|
-
import pandas as pd
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
|
|
17
|
+
from flowcept.agents.gui.audio_utils import _md_to_plain_text, speak
|
|
18
|
+
from flowcept.configs import AGENT_AUDIO
|
|
13
19
|
|
|
14
20
|
|
|
15
21
|
def query_agent(user_input: str) -> ToolResult:
|
|
@@ -125,6 +131,8 @@ def display_ai_msg_from_tool(tool_result: ToolResult):
|
|
|
125
131
|
|
|
126
132
|
|
|
127
133
|
def _sniff_mime(b: bytes) -> str:
|
|
134
|
+
if b.startswith(b"%PDF-"):
|
|
135
|
+
return "application/pdf"
|
|
128
136
|
if b.startswith(b"\x89PNG\r\n\x1a\n"):
|
|
129
137
|
return "image/png"
|
|
130
138
|
if b.startswith(b"\xff\xd8\xff"):
|
|
@@ -138,23 +146,72 @@ def _sniff_mime(b: bytes) -> str:
|
|
|
138
146
|
return "application/octet-stream"
|
|
139
147
|
|
|
140
148
|
|
|
149
|
+
def _pdf_first_page_to_png(pdf_bytes: bytes, zoom: float = 2.0) -> bytes:
|
|
150
|
+
"""
|
|
151
|
+
Convert the first page of a PDF to PNG bytes using PyMuPDF (fitz).
|
|
152
|
+
zoom ~2.0 gives a good thumbnail; increase for higher resolution.
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
import fitz # PyMuPDF
|
|
156
|
+
except Exception as e:
|
|
157
|
+
# PyMuPDF not installed; caller can decide how to handle
|
|
158
|
+
raise ImportError("PyMuPDF (fitz) is required to render PDF thumbnails") from e
|
|
159
|
+
|
|
160
|
+
doc = fitz.open(stream=pdf_bytes, filetype="pdf")
|
|
161
|
+
try:
|
|
162
|
+
page = doc.load_page(0)
|
|
163
|
+
pix = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom), alpha=False)
|
|
164
|
+
return pix.tobytes("png")
|
|
165
|
+
finally:
|
|
166
|
+
doc.close()
|
|
167
|
+
|
|
168
|
+
|
|
141
169
|
def ensure_data_uri(val):
|
|
142
|
-
r"""
|
|
170
|
+
r"""Accept bytes/bytearray/memoryview or a repr like "b'\x89PNG...'", or a file path/URL.
|
|
171
|
+
Returns a data URL suitable for st.column_config.ImageColumn. If input is a PDF, converts
|
|
172
|
+
the first page to PNG (requires PyMuPDF).
|
|
173
|
+
"""
|
|
174
|
+
# Already a data URI?
|
|
143
175
|
if isinstance(val, str) and val.startswith("data:"):
|
|
144
176
|
return val
|
|
177
|
+
|
|
178
|
+
# Bytes repr string? -> real bytes
|
|
145
179
|
if isinstance(val, str) and val.startswith("b'"):
|
|
146
180
|
try:
|
|
147
|
-
val = ast.literal_eval(val)
|
|
181
|
+
val = ast.literal_eval(val)
|
|
148
182
|
except Exception:
|
|
149
183
|
return None
|
|
184
|
+
|
|
185
|
+
# Paths that point to a PDF: convert
|
|
186
|
+
if isinstance(val, str) and val.lower().endswith(".pdf") and Path(val).is_file():
|
|
187
|
+
try:
|
|
188
|
+
pdf_bytes = Path(val).read_bytes()
|
|
189
|
+
png_bytes = _pdf_first_page_to_png(pdf_bytes)
|
|
190
|
+
return f"data:image/png;base64,{base64.b64encode(png_bytes).decode('ascii')}"
|
|
191
|
+
except Exception:
|
|
192
|
+
# Fallback: no preview; caller will show blank cell
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
# Normalize to bytes if memoryview/bytearray
|
|
150
196
|
if isinstance(val, memoryview):
|
|
151
197
|
val = val.tobytes()
|
|
152
198
|
if isinstance(val, bytearray):
|
|
153
199
|
val = bytes(val)
|
|
200
|
+
|
|
201
|
+
# Raw bytes? detect and convert if PDF
|
|
154
202
|
if isinstance(val, bytes):
|
|
155
203
|
mime = _sniff_mime(val)
|
|
204
|
+
if mime == "application/pdf":
|
|
205
|
+
try:
|
|
206
|
+
png_bytes = _pdf_first_page_to_png(val)
|
|
207
|
+
return f"data:image/png;base64,{base64.b64encode(png_bytes).decode('ascii')}"
|
|
208
|
+
except Exception:
|
|
209
|
+
return None
|
|
210
|
+
# Regular image bytes -> data URI
|
|
156
211
|
return f"data:{mime};base64,{base64.b64encode(val).decode('ascii')}"
|
|
157
|
-
|
|
212
|
+
|
|
213
|
+
# Otherwise (URL/path to an image) let Streamlit try; PDFs won’t render as images
|
|
214
|
+
return val
|
|
158
215
|
|
|
159
216
|
|
|
160
217
|
def _render_df(df: pd.DataFrame, image_width: int = 90, row_height: int = 90):
|
|
@@ -242,6 +299,17 @@ def display_df_tool_response(tool_result: ToolResult):
|
|
|
242
299
|
st.markdown("📝 Summary:")
|
|
243
300
|
print(f"THIS IS THE SUMMARY\n{summary}")
|
|
244
301
|
st.markdown(summary)
|
|
302
|
+
|
|
303
|
+
if AGENT_AUDIO:
|
|
304
|
+
# 🔊 Speak only if user spoke to us this turn
|
|
305
|
+
print(f"This is the session state nowww: {st.session_state['speak_reply']}")
|
|
306
|
+
if st.session_state.get("speak_reply"):
|
|
307
|
+
try:
|
|
308
|
+
plain_text = _md_to_plain_text(summary)
|
|
309
|
+
print(f"Trying to speak plain text {plain_text}")
|
|
310
|
+
speak(plain_text) # uses your existing gTTS-based speak()
|
|
311
|
+
except Exception as e:
|
|
312
|
+
st.warning(f"TTS failed: {e}")
|
|
245
313
|
elif summary_error:
|
|
246
314
|
st.markdown(f"⚠️ Encountered this error when summarizing the result dataframe:\n```text\n{summary_error}")
|
|
247
315
|
|
|
@@ -288,3 +356,87 @@ def exec_st_plot_code(code, result_df, st_module):
|
|
|
288
356
|
code,
|
|
289
357
|
{"result": result_df, "st": st_module, "plt": __import__("matplotlib.pyplot"), "alt": __import__("altair")},
|
|
290
358
|
)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _resolve_logo() -> str | None:
|
|
362
|
+
# Try package resource
|
|
363
|
+
try:
|
|
364
|
+
p = pkg_files("flowcept").joinpath("docs/img/flowcept-logo.png")
|
|
365
|
+
if p.is_file():
|
|
366
|
+
return str(p)
|
|
367
|
+
except Exception:
|
|
368
|
+
pass
|
|
369
|
+
# Fallbacks for dev checkouts
|
|
370
|
+
here = Path(__file__).resolve()
|
|
371
|
+
candidates = [
|
|
372
|
+
here.parents[3] / "docs/img/flowcept-logo.png",
|
|
373
|
+
here.parents[2] / "docs/img/flowcept-logo.png",
|
|
374
|
+
here.parents[1] / "docs/img/flowcept-logo.png",
|
|
375
|
+
Path("flowcept/docs/img/flowcept-logo.png"),
|
|
376
|
+
]
|
|
377
|
+
for c in candidates:
|
|
378
|
+
if c.is_file():
|
|
379
|
+
return str(c)
|
|
380
|
+
print(str(c))
|
|
381
|
+
return None
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def resolve_logo_path(package: str = "flowcept", resource: str = "docs/img/flowcept-logo.png") -> str | None:
|
|
385
|
+
"""
|
|
386
|
+
Resolve the Flowcept logo whether running from an installed package or a src/ layout repo.
|
|
387
|
+
Returns an absolute string path or None if not found.
|
|
388
|
+
"""
|
|
389
|
+
# 1) Try packaged resource (works if docs/img is included in the wheel/sdist)
|
|
390
|
+
try:
|
|
391
|
+
p = pkg_files(package).joinpath(resource)
|
|
392
|
+
if hasattr(p, "is_file") and p.is_file():
|
|
393
|
+
return str(p)
|
|
394
|
+
except Exception:
|
|
395
|
+
pass
|
|
396
|
+
|
|
397
|
+
here = Path(__file__).resolve()
|
|
398
|
+
|
|
399
|
+
# 2) src/ layout repo: .../<repo>/flowcept/src/flowcept/agents/gui/gui_utils.py
|
|
400
|
+
# Find the nearest 'src' ancestor, then go to repo root (src/..), then docs/img/...
|
|
401
|
+
try:
|
|
402
|
+
src_dir = next(p for p in here.parents if p.name == "src")
|
|
403
|
+
repo_root = src_dir.parent # <repo>/flowcept
|
|
404
|
+
cand = repo_root / "docs" / "img" / "flowcept-logo.png"
|
|
405
|
+
if cand.is_file():
|
|
406
|
+
return str(cand)
|
|
407
|
+
except StopIteration:
|
|
408
|
+
pass
|
|
409
|
+
|
|
410
|
+
# 3) Editable install package dir: .../src/flowcept (package root)
|
|
411
|
+
pkg_dir = here.parents[2] # .../src/flowcept
|
|
412
|
+
cand = pkg_dir / "docs" / "img" / "flowcept-logo.png"
|
|
413
|
+
if cand.is_file():
|
|
414
|
+
return str(cand)
|
|
415
|
+
|
|
416
|
+
# 4) CWD fallback
|
|
417
|
+
cand = Path.cwd() / "flowcept" / "docs" / "img" / "flowcept-logo.png"
|
|
418
|
+
if cand.is_file():
|
|
419
|
+
return str(cand)
|
|
420
|
+
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def render_title_with_logo(
|
|
425
|
+
page_title: str, logo_path: str | None, logo_width: int = 150, add_to_sidebar: bool = True, debug: bool = False
|
|
426
|
+
):
|
|
427
|
+
"""
|
|
428
|
+
Render a header row with an optional logo next to the title; optionally mirror it in the sidebar.
|
|
429
|
+
"""
|
|
430
|
+
if debug:
|
|
431
|
+
st.caption(f"Logo path resolved to: {logo_path or 'NOT FOUND'}")
|
|
432
|
+
|
|
433
|
+
if logo_path and Path(logo_path).is_file():
|
|
434
|
+
col_logo, col_title = st.columns([1, 6])
|
|
435
|
+
with col_logo:
|
|
436
|
+
st.image(logo_path, width=logo_width)
|
|
437
|
+
with col_title:
|
|
438
|
+
st.title(page_title)
|
|
439
|
+
if add_to_sidebar:
|
|
440
|
+
st.sidebar.image(logo_path, width=logo_width)
|
|
441
|
+
else:
|
|
442
|
+
st.title(page_title)
|