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.
Files changed (208) hide show
  1. {flowcept-0.9.6 → flowcept-0.9.7}/PKG-INFO +14 -1
  2. flowcept-0.9.7/docs/task_schema.rst +157 -0
  3. {flowcept-0.9.6 → flowcept-0.9.7}/pyproject.toml +7 -1
  4. {flowcept-0.9.6 → flowcept-0.9.7}/resources/sample_settings.yaml +2 -1
  5. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/gui/agent_gui.py +31 -20
  6. flowcept-0.9.7/src/flowcept/agents/gui/audio_utils.py +129 -0
  7. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/gui/gui_utils.py +157 -5
  8. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/prompts/general_prompts.py +2 -2
  9. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/prompts/in_memory_query_prompts.py +5 -4
  10. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/general_tools.py +3 -0
  11. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/in_memory_queries/in_memory_queries_tools.py +3 -1
  12. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/task_data_preprocess.py +2 -1
  13. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/configs.py +7 -0
  14. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/version.py +1 -1
  15. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/flowcept_explicit_tasks.py +14 -3
  16. flowcept-0.9.6/docs/task_schema.rst +0 -114
  17. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/checks.yml +0 -0
  18. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/create-release-n-publish.yml +0 -0
  19. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-llm-tests.yml +0 -0
  20. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-all-dbs.yml +0 -0
  21. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-in-container.yml +0 -0
  22. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-kafka.yml +0 -0
  23. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-py313.yml +0 -0
  24. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests-simple.yml +0 -0
  25. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run-tests.yml +0 -0
  26. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/run_examples.sh +0 -0
  27. {flowcept-0.9.6 → flowcept-0.9.7}/.github/workflows/version_bumper.py +0 -0
  28. {flowcept-0.9.6 → flowcept-0.9.7}/.gitignore +0 -0
  29. {flowcept-0.9.6 → flowcept-0.9.7}/.readthedocs.yaml +0 -0
  30. {flowcept-0.9.6 → flowcept-0.9.7}/CONTRIBUTING.md +0 -0
  31. {flowcept-0.9.6 → flowcept-0.9.7}/LICENSE +0 -0
  32. {flowcept-0.9.6 → flowcept-0.9.7}/Makefile +0 -0
  33. {flowcept-0.9.6 → flowcept-0.9.7}/README.md +0 -0
  34. {flowcept-0.9.6 → flowcept-0.9.7}/deployment/Dockerfile +0 -0
  35. {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-grafana.yml +0 -0
  36. {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-kafka.yml +0 -0
  37. {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-mofka.yml +0 -0
  38. {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose-mongo.yml +0 -0
  39. {flowcept-0.9.6 → flowcept-0.9.7}/deployment/compose.yml +0 -0
  40. {flowcept-0.9.6 → flowcept-0.9.7}/docs/api-reference.rst +0 -0
  41. {flowcept-0.9.6 → flowcept-0.9.7}/docs/architecture.rst +0 -0
  42. {flowcept-0.9.6 → flowcept-0.9.7}/docs/cli-reference.rst +0 -0
  43. {flowcept-0.9.6 → flowcept-0.9.7}/docs/conf.py +0 -0
  44. {flowcept-0.9.6 → flowcept-0.9.7}/docs/contributing.rst +0 -0
  45. {flowcept-0.9.6 → flowcept-0.9.7}/docs/img/architecture-diagram.png +0 -0
  46. {flowcept-0.9.6 → flowcept-0.9.7}/docs/img/flowcept-logo-dark.png +0 -0
  47. {flowcept-0.9.6 → flowcept-0.9.7}/docs/img/flowcept-logo.png +0 -0
  48. {flowcept-0.9.6 → flowcept-0.9.7}/docs/index.rst +0 -0
  49. {flowcept-0.9.6 → flowcept-0.9.7}/docs/prov_capture.rst +0 -0
  50. {flowcept-0.9.6 → flowcept-0.9.7}/docs/prov_storage.rst +0 -0
  51. {flowcept-0.9.6 → flowcept-0.9.7}/docs/quick_start.rst +0 -0
  52. {flowcept-0.9.6 → flowcept-0.9.7}/docs/schemas.rst +0 -0
  53. {flowcept-0.9.6 → flowcept-0.9.7}/docs/setup.rst +0 -0
  54. {flowcept-0.9.6 → flowcept-0.9.7}/docs/telemetry_capture.rst +0 -0
  55. {flowcept-0.9.6 → flowcept-0.9.7}/docs/workflow_schema.rst +0 -0
  56. {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/a2a/README.md +0 -0
  57. {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/a2a/agent1.py +0 -0
  58. {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/a2a/agent2.py +0 -0
  59. {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/aec_agent_context_manager.py +0 -0
  60. {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/aec_agent_mock.py +0 -0
  61. {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/aec_prompts.py +0 -0
  62. {flowcept-0.9.6 → flowcept-0.9.7}/examples/agents/opt_driver_mock.py +0 -0
  63. {flowcept-0.9.6 → flowcept-0.9.7}/examples/consumers/ping_pong_example.py +0 -0
  64. {flowcept-0.9.6 → flowcept-0.9.7}/examples/consumers/simple_consumer.py +0 -0
  65. {flowcept-0.9.6 → flowcept-0.9.7}/examples/consumers/simple_publisher.py +0 -0
  66. {flowcept-0.9.6 → flowcept-0.9.7}/examples/convergence_loop_example.py +0 -0
  67. {flowcept-0.9.6 → flowcept-0.9.7}/examples/dask_example.py +0 -0
  68. {flowcept-0.9.6 → flowcept-0.9.7}/examples/distributed_consumer_example.py +0 -0
  69. {flowcept-0.9.6 → flowcept-0.9.7}/examples/instrumented_loop_example.py +0 -0
  70. {flowcept-0.9.6 → flowcept-0.9.7}/examples/instrumented_simple_example.py +0 -0
  71. {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/README.md +0 -0
  72. {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/custom_provenance_id_mapping.yaml +0 -0
  73. {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_dataprep.py +0 -0
  74. {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_main_example.py +0 -0
  75. {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_model.py +0 -0
  76. {flowcept-0.9.6 → flowcept-0.9.7}/examples/llm_complex/llm_test_runner.py +0 -0
  77. {flowcept-0.9.6 → flowcept-0.9.7}/examples/mlflow_example.py +0 -0
  78. {flowcept-0.9.6 → flowcept-0.9.7}/examples/mqtt_example.py +0 -0
  79. {flowcept-0.9.6 → flowcept-0.9.7}/examples/single_layer_perceptron_example.py +0 -0
  80. {flowcept-0.9.6 → flowcept-0.9.7}/examples/start_here.py +0 -0
  81. {flowcept-0.9.6 → flowcept-0.9.7}/examples/tensorboard_example.py +0 -0
  82. {flowcept-0.9.6 → flowcept-0.9.7}/examples/unmanaged/main.py +0 -0
  83. {flowcept-0.9.6 → flowcept-0.9.7}/examples/unmanaged/simple_task.py +0 -0
  84. {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/analytics.ipynb +0 -0
  85. {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/dask.ipynb +0 -0
  86. {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/dask_from_CLI.ipynb +0 -0
  87. {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/mlflow.ipynb +0 -0
  88. {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/reset_dask_nb_exec_counts.py +0 -0
  89. {flowcept-0.9.6 → flowcept-0.9.7}/notebooks/tensorboard.ipynb +0 -0
  90. {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/bedrock_setup.sh +0 -0
  91. {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/consumer.py +0 -0
  92. {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/mofka-requirements.yaml +0 -0
  93. {flowcept-0.9.6 → flowcept-0.9.7}/resources/mofka/mofka_config.json +0 -0
  94. {flowcept-0.9.6 → flowcept-0.9.7}/resources/simple_redis_consumer.py +0 -0
  95. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/__init__.py +0 -0
  96. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/__init__.py +0 -0
  97. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/agent_client.py +0 -0
  98. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/agents_utils.py +0 -0
  99. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/dynamic_schema_tracker.py +0 -0
  100. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/flowcept_agent.py +0 -0
  101. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/flowcept_ctx_manager.py +0 -0
  102. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/gui/__init__.py +0 -0
  103. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/llms/__init__.py +0 -0
  104. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/llms/claude_gcp.py +0 -0
  105. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/llms/gemini25.py +0 -0
  106. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/prompts/__init__.py +0 -0
  107. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/__init__.py +0 -0
  108. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/in_memory_queries/__init__.py +0 -0
  109. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/agents/tools/in_memory_queries/pandas_agent_utils.py +0 -0
  110. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/__init__.py +0 -0
  111. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/analytics_utils.py +0 -0
  112. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/data_augmentation.py +0 -0
  113. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/analytics/plot.py +0 -0
  114. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/cli.py +0 -0
  115. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/__init__.py +0 -0
  116. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/autoflush_buffer.py +0 -0
  117. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/__init__.py +0 -0
  118. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/__init__.py +0 -0
  119. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/docdb_dao_base.py +0 -0
  120. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/lmdb_dao.py +0 -0
  121. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/docdb_dao/mongodb_dao.py +0 -0
  122. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/keyvalue_dao.py +0 -0
  123. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/__init__.py +0 -0
  124. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_base.py +0 -0
  125. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_kafka.py +0 -0
  126. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_mofka.py +0 -0
  127. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/mq_dao/mq_dao_redis.py +0 -0
  128. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/daos/redis_conn.py +0 -0
  129. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/__init__.py +0 -0
  130. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/base_settings_dataclasses.py +0 -0
  131. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/task_object.py +0 -0
  132. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/telemetry.py +0 -0
  133. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_dataclasses/workflow_object.py +0 -0
  134. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/flowcept_logger.py +0 -0
  135. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/query_utils.py +0 -0
  136. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/settings_factory.py +0 -0
  137. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/utils.py +0 -0
  138. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/commons/vocabulary.py +0 -0
  139. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/__init__.py +0 -0
  140. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/db_api.py +0 -0
  141. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/flowcept_controller.py +0 -0
  142. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_api/task_query_api.py +0 -0
  143. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/__init__.py +0 -0
  144. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/app.py +0 -0
  145. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/resources/__init__.py +0 -0
  146. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/resources/query_rsrc.py +0 -0
  147. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowcept_webserver/resources/task_messages_rsrc.py +0 -0
  148. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/__init__.py +0 -0
  149. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/__init__.py +0 -0
  150. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/base_interceptor.py +0 -0
  151. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/brokers/__init__.py +0 -0
  152. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/brokers/mqtt_interceptor.py +0 -0
  153. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/__init__.py +0 -0
  154. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/dask_dataclasses.py +0 -0
  155. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/dask_interceptor.py +0 -0
  156. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/dask/dask_plugins.py +0 -0
  157. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/instrumentation_interceptor.py +0 -0
  158. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/interceptor_state_manager.py +0 -0
  159. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/__init__.py +0 -0
  160. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/interception_event_handler.py +0 -0
  161. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/mlflow_dao.py +0 -0
  162. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/mlflow_dataclasses.py +0 -0
  163. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/mlflow/mlflow_interceptor.py +0 -0
  164. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/tensorboard/__init__.py +0 -0
  165. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/tensorboard/tensorboard_dataclasses.py +0 -0
  166. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/adapters/tensorboard/tensorboard_interceptor.py +0 -0
  167. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/__init__.py +0 -0
  168. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/agent/__init__.py +0 -0
  169. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/agent/base_agent_context_manager.py +0 -0
  170. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/base_consumer.py +0 -0
  171. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/consumer_utils.py +0 -0
  172. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/consumers/document_inserter.py +0 -0
  173. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/flowceptor/telemetry_capture.py +0 -0
  174. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/__init__.py +0 -0
  175. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_agent_task.py +0 -0
  176. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_decorator.py +0 -0
  177. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_loop.py +0 -0
  178. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_task.py +0 -0
  179. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/flowcept_torch.py +0 -0
  180. {flowcept-0.9.6 → flowcept-0.9.7}/src/flowcept/instrumentation/task_capture.py +0 -0
  181. {flowcept-0.9.6 → flowcept-0.9.7}/tests/__init__.py +0 -0
  182. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/__init__.py +0 -0
  183. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/dask_test_utils.py +0 -0
  184. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_broker.py +0 -0
  185. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_dask.py +0 -0
  186. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_dask_with_context_mgmt.py +0 -0
  187. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_file_observer.py +0 -0
  188. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_mlflow.py +0 -0
  189. {flowcept-0.9.6 → flowcept-0.9.7}/tests/adapters/test_tensorboard.py +0 -0
  190. {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/__init__.py +0 -0
  191. {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/db_api_test.py +0 -0
  192. {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/flowcept_api_test.py +0 -0
  193. {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/sample_data.json +0 -0
  194. {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/sample_data_with_telemetry_and_rai.json +0 -0
  195. {flowcept-0.9.6 → flowcept-0.9.7}/tests/api/task_query_api_test.py +0 -0
  196. {flowcept-0.9.6 → flowcept-0.9.7}/tests/doc_db_inserter/__init__.py +0 -0
  197. {flowcept-0.9.6 → flowcept-0.9.7}/tests/doc_db_inserter/doc_db_inserter_test.py +0 -0
  198. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/__init__.py +0 -0
  199. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/flowcept_loop_test.py +0 -0
  200. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/flowcept_task_decorator_test.py +0 -0
  201. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/__init__.py +0 -0
  202. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/dl_trainer.py +0 -0
  203. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/ml_decorator_dask_test.py +0 -0
  204. {flowcept-0.9.6 → flowcept-0.9.7}/tests/instrumentation_tests/ml_tests/ml_decorator_test.py +0 -0
  205. {flowcept-0.9.6 → flowcept-0.9.7}/tests/misc_tests/__init__.py +0 -0
  206. {flowcept-0.9.6 → flowcept-0.9.7}/tests/misc_tests/log_test.py +0 -0
  207. {flowcept-0.9.6 → flowcept-0.9.7}/tests/misc_tests/singleton_test.py +0 -0
  208. {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.6
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", "langchain_openai"]
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.6 # Version of the Flowcept package. This setting file is compatible with this version.
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
- st.title(PAGE_TITLE)
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 a **Workflow Provenance Specialist**.\n\n"
27
+ "Hi, there! I'm your **Workflow Provenance Assistant**.\n\n"
21
28
  "I am tracking workflow executions and I can:\n"
22
- "- 🔍 Analyze running workflows\n"
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 Streamlit Function."""
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
- # st.session_state.chat_history.append({"role": "human", "content": user_input})
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 [generate_result_df.__name__, generate_plot_code.__name__, run_df_code.__name__]:
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
- from flowcept.agents.gui import AI
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"""Accepts bytes/bytearray/memoryview or a repr like \"b'\\x89PNG...'\" and returns a data URL."""
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) # turn repr into bytes
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
- return val # path/URL, etc.
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)