GeneralManager 0.79.0__tar.gz → 0.79.2__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 (332) hide show
  1. {generalmanager-0.79.0/src/GeneralManager.egg-info → generalmanager-0.79.2}/PKG-INFO +1 -1
  2. {generalmanager-0.79.0 → generalmanager-0.79.2}/pyproject.toml +1 -1
  3. {generalmanager-0.79.0 → generalmanager-0.79.2/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/GeneralManager.egg-info/SOURCES.txt +1 -0
  5. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_groups.py +15 -3
  6. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/consumer.py +203 -49
  7. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/context.py +49 -11
  8. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/errors.py +19 -1
  9. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/runner.py +29 -11
  10. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/rate_limits.py +16 -25
  11. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/settings.py +21 -0
  12. generalmanager-0.79.2/src/general_manager/chat/turns.py +84 -0
  13. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/views.py +203 -112
  14. {generalmanager-0.79.0 → generalmanager-0.79.2}/LICENSE +0 -0
  15. {generalmanager-0.79.0 → generalmanager-0.79.2}/README.md +0 -0
  16. {generalmanager-0.79.0 → generalmanager-0.79.2}/setup.cfg +0 -0
  17. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  18. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/GeneralManager.egg-info/requires.txt +0 -0
  19. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/GeneralManager.egg-info/top_level.txt +0 -0
  20. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/__init__.py +0 -0
  21. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/__init__.py +0 -0
  22. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/api.py +0 -0
  23. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/bucket.py +0 -0
  24. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/cache.py +0 -0
  25. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/dataframes.py +0 -0
  26. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/factory.py +0 -0
  27. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/general_manager.py +0 -0
  28. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/interface.py +0 -0
  29. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/manager.py +0 -0
  30. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/measurement.py +0 -0
  31. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/permission.py +0 -0
  32. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/rule.py +0 -0
  33. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/search.py +0 -0
  34. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/_types/utils.py +0 -0
  35. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/__init__.py +0 -0
  36. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql.py +0 -0
  37. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_as_of.py +0 -0
  38. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_errors.py +0 -0
  39. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_mutations.py +0 -0
  40. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_ordering.py +0 -0
  41. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_output.py +0 -0
  42. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_prefetch.py +0 -0
  43. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_relations.py +0 -0
  44. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_resolvers.py +0 -0
  45. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_search.py +0 -0
  46. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  47. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_subscriptions.py +0 -0
  48. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_type.py +0 -0
  49. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_view.py +0 -0
  50. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_warmup.py +0 -0
  51. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  52. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  53. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/mutation.py +0 -0
  54. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/notification_batching.py +0 -0
  55. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/property.py +0 -0
  56. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/registry.py +0 -0
  57. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/remote_api.py +0 -0
  58. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/remote_invalidation.py +0 -0
  59. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/api/remote_invalidation_client.py +0 -0
  60. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/apps.py +0 -0
  61. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/as_of.py +0 -0
  62. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bootstrap.py +0 -0
  63. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/__init__.py +0 -0
  64. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/_materialized_bucket.py +0 -0
  65. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/_ordering.py +0 -0
  66. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/base_bucket.py +0 -0
  67. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/calculation_bucket.py +0 -0
  68. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/database_bucket.py +0 -0
  69. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/excel_bucket.py +0 -0
  70. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/group_bucket.py +0 -0
  71. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/indexing.py +0 -0
  72. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/projection.py +0 -0
  73. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/bucket/request_bucket.py +0 -0
  74. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/__init__.py +0 -0
  75. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/cache_decorator.py +0 -0
  76. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/cache_tracker.py +0 -0
  77. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/data_change_context.py +0 -0
  78. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/dependency_cache.py +0 -0
  79. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/dependency_index.py +0 -0
  80. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/dependency_matching.py +0 -0
  81. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/dependency_publish.py +0 -0
  82. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/dependency_shards.py +0 -0
  83. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/model_dependency_collector.py +0 -0
  84. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/run_context.py +0 -0
  85. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/run_context_lru.py +0 -0
  86. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/cache/signals.py +0 -0
  87. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/__init__.py +0 -0
  88. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/audit.py +0 -0
  89. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/bootstrap.py +0 -0
  90. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/checks.py +0 -0
  91. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/__init__.py +0 -0
  92. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/__main__.py +0 -0
  93. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/baseline.py +0 -0
  94. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
  95. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
  96. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
  97. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
  98. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
  99. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
  100. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/datasets/planned_orchestration.yaml +0 -0
  101. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/diagnostics.py +0 -0
  102. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/fingerprints.py +0 -0
  103. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/fixtures.py +0 -0
  104. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  105. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  106. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/judges/contract.py +0 -0
  107. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  108. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  109. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/readiness.py +0 -0
  110. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/evals/traces.py +0 -0
  111. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/grounding.py +0 -0
  112. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/models.py +0 -0
  113. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/__init__.py +0 -0
  114. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/budget.py +0 -0
  115. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/calculations.py +0 -0
  116. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/catalog.py +0 -0
  117. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/config.py +0 -0
  118. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/events.py +0 -0
  119. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/evidence.py +0 -0
  120. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/models.py +0 -0
  121. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/planner.py +0 -0
  122. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/provider_calls.py +0 -0
  123. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/resolver.py +0 -0
  124. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/routing.py +0 -0
  125. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/scheduler.py +0 -0
  126. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/synthesis.py +0 -0
  127. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/planned/validation.py +0 -0
  128. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/providers/__init__.py +0 -0
  129. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/providers/_shared.py +0 -0
  130. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/providers/anthropic.py +0 -0
  131. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/providers/base.py +0 -0
  132. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/providers/google.py +0 -0
  133. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/providers/ollama.py +0 -0
  134. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/providers/openai.py +0 -0
  135. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/routing.py +0 -0
  136. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/schema_index.py +0 -0
  137. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/signals.py +0 -0
  138. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/system_prompt.py +0 -0
  139. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/tool_metadata.py +0 -0
  140. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/chat/tools.py +0 -0
  141. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/conf.py +0 -0
  142. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/dataframes/__init__.py +0 -0
  143. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/dataframes/measurements.py +0 -0
  144. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/factory/__init__.py +0 -0
  145. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/factory/auto_factory.py +0 -0
  146. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/factory/factories.py +0 -0
  147. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/factory/factory_methods.py +0 -0
  148. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/__init__.py +0 -0
  149. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/base_interface.py +0 -0
  150. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/bundles/__init__.py +0 -0
  151. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/bundles/calculation.py +0 -0
  152. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/bundles/database.py +0 -0
  153. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/bundles/excel.py +0 -0
  154. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  155. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/bundles/request.py +0 -0
  156. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/__init__.py +0 -0
  157. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/base.py +0 -0
  158. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/builtin.py +0 -0
  159. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  160. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  161. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/calculation/input_resolution.py +0 -0
  162. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  163. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/configuration.py +0 -0
  164. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  165. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  166. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/excel/__init__.py +0 -0
  167. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  168. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  169. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  170. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  171. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/factory.py +0 -0
  172. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  173. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  174. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  175. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  176. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
  177. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  178. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  179. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  180. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  181. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  182. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  183. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  184. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  185. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  186. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  187. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/registry.py +0 -0
  188. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  189. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  190. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/excel.py +0 -0
  191. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/excel_store.py +0 -0
  192. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/excel_workbook.py +0 -0
  193. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  194. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  195. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  196. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/__init__.py +0 -0
  197. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/calculation.py +0 -0
  198. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/database.py +0 -0
  199. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/excel.py +0 -0
  200. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  201. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/read_only.py +0 -0
  202. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  203. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/interfaces/request.py +0 -0
  204. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/manifests/__init__.py +0 -0
  205. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  206. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  207. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/manifests/capability_models.py +0 -0
  208. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/orm_interface.py +0 -0
  209. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/requests.py +0 -0
  210. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/utils/__init__.py +0 -0
  211. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  212. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/utils/errors.py +0 -0
  213. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/utils/history.py +0 -0
  214. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/interface/utils/models.py +0 -0
  215. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/logging.py +0 -0
  216. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  217. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
  218. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  219. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  220. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/search_index.py +0 -0
  221. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/search_reconcile.py +0 -0
  222. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  223. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/shell.py +0 -0
  224. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  225. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  226. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/manager/__init__.py +0 -0
  227. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/manager/general_manager.py +0 -0
  228. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/manager/group_manager.py +0 -0
  229. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/manager/input.py +0 -0
  230. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/manager/meta.py +0 -0
  231. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/measurement/__init__.py +0 -0
  232. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/measurement/measurement.py +0 -0
  233. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/measurement/measurement_field.py +0 -0
  234. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/metrics/__init__.py +0 -0
  235. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/metrics/graphql.py +0 -0
  236. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0001_initial.py +0 -0
  237. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  238. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  239. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  240. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  241. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  242. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  243. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0007_upload_intent.py +0 -0
  244. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
  245. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
  246. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
  247. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
  248. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/0012_chat_context_watermarks.py +0 -0
  249. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/migrations/__init__.py +0 -0
  250. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/models.py +0 -0
  251. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/__init__.py +0 -0
  252. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/audit.py +0 -0
  253. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/base_permission.py +0 -0
  254. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/graphql_capabilities.py +0 -0
  255. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/manager_based_permission.py +0 -0
  256. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/mutation_permission.py +0 -0
  257. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/permission_checks.py +0 -0
  258. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/permission_data_manager.py +0 -0
  259. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/permission/utils.py +0 -0
  260. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/public_api_registry.py +0 -0
  261. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/py.typed +0 -0
  262. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/rule/__init__.py +0 -0
  263. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/rule/handler.py +0 -0
  264. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/rule/rule.py +0 -0
  265. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/__init__.py +0 -0
  266. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/async_tasks.py +0 -0
  267. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/backend.py +0 -0
  268. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/backend_registry.py +0 -0
  269. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/backends/__init__.py +0 -0
  270. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/backends/dev.py +0 -0
  271. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/backends/meilisearch.py +0 -0
  272. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/backends/opensearch.py +0 -0
  273. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/backends/typesense.py +0 -0
  274. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/checks.py +0 -0
  275. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/config.py +0 -0
  276. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/indexer.py +0 -0
  277. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/invalidation.py +0 -0
  278. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/m2m_invalidation.py +0 -0
  279. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/models.py +0 -0
  280. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/reconciliation.py +0 -0
  281. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/registry.py +0 -0
  282. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/tasks.py +0 -0
  283. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/search/utils.py +0 -0
  284. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/seeding/__init__.py +0 -0
  285. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/seeding/manager_landscape.py +0 -0
  286. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/__init__.py +0 -0
  287. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/adapters.py +0 -0
  288. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/checks.py +0 -0
  289. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/config.py +0 -0
  290. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/errors.py +0 -0
  291. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/finalization.py +0 -0
  292. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/graphql.py +0 -0
  293. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/graphql_types.py +0 -0
  294. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/metrics.py +0 -0
  295. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/models.py +0 -0
  296. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/public.py +0 -0
  297. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/s3.py +0 -0
  298. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/services.py +0 -0
  299. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/tokens.py +0 -0
  300. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/types.py +0 -0
  301. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/urls.py +0 -0
  302. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/uploads/views.py +0 -0
  303. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/__init__.py +0 -0
  304. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/_args_to_kwargs.py +0 -0
  305. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/_cache_key_encoder.py +0 -0
  306. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/_make_cache_key.py +0 -0
  307. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/_none_to_zero.py +0 -0
  308. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/filter_parser.py +0 -0
  309. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/format_string.py +0 -0
  310. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/json_encoder.py +0 -0
  311. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/path_mapping.py +0 -0
  312. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/public_api.py +0 -0
  313. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/testing.py +0 -0
  314. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/utils/type_checks.py +0 -0
  315. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/__init__.py +0 -0
  316. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/actions.py +0 -0
  317. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/backend_registry.py +0 -0
  318. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/backends/__init__.py +0 -0
  319. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/backends/celery.py +0 -0
  320. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/backends/local.py +0 -0
  321. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/backends/n8n.py +0 -0
  322. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/config.py +0 -0
  323. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/engine.py +0 -0
  324. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/event_registry.py +0 -0
  325. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/events.py +0 -0
  326. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/models.py +0 -0
  327. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/signal_bridge.py +0 -0
  328. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/tasks.py +0 -0
  329. {generalmanager-0.79.0 → generalmanager-0.79.2}/src/general_manager/workflow/telemetry.py +0 -0
  330. {generalmanager-0.79.0 → generalmanager-0.79.2}/tests/test_settings.py +0 -0
  331. {generalmanager-0.79.0 → generalmanager-0.79.2}/tests/test_urls.py +0 -0
  332. {generalmanager-0.79.0 → generalmanager-0.79.2}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.79.0
3
+ Version: 0.79.2
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.79.0"
7
+ version = "0.79.2"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.79.0
3
+ Version: 0.79.2
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -96,6 +96,7 @@ src/general_manager/chat/signals.py
96
96
  src/general_manager/chat/system_prompt.py
97
97
  src/general_manager/chat/tool_metadata.py
98
98
  src/general_manager/chat/tools.py
99
+ src/general_manager/chat/turns.py
99
100
  src/general_manager/chat/views.py
100
101
  src/general_manager/chat/evals/__init__.py
101
102
  src/general_manager/chat/evals/__main__.py
@@ -94,7 +94,10 @@ def create_group_types(
94
94
  )
95
95
  else:
96
96
  sum_fields[field_name] = map_field(sum_type, field_name, field_info)
97
- sum_resolvers[f"resolve_{field_name}"] = _sum_resolver(field_name)
97
+ sum_resolvers[f"resolve_{field_name}"] = _sum_resolver(
98
+ field_name,
99
+ text_values=sum_type is not None and issubclass(sum_type, str),
100
+ )
98
101
 
99
102
  key_type = type(
100
103
  f"{manager_class.__name__}GroupKeys",
@@ -201,7 +204,7 @@ def _is_sum_type(field_type: object) -> bool:
201
204
  return (
202
205
  concrete_type is not None
203
206
  and not issubclass(concrete_type, bool)
204
- and issubclass(concrete_type, (int, float, Decimal, Measurement))
207
+ and issubclass(concrete_type, (int, float, Decimal, Measurement, str))
205
208
  )
206
209
 
207
210
 
@@ -288,13 +291,22 @@ def _measurement_key_resolver(field_name: str) -> Callable[..., object]:
288
291
  return resolver
289
292
 
290
293
 
291
- def _sum_resolver(field_name: str) -> Callable[..., object]:
294
+ def _sum_resolver(
295
+ field_name: str, *, text_values: bool = False
296
+ ) -> Callable[..., object]:
292
297
  def resolver(
293
298
  group: GroupManager[GeneralManager],
294
299
  info: GraphQLResolveInfo,
295
300
  target_unit: str | None = None,
296
301
  ) -> object:
297
302
  _validate_group_field_permission(group, info, field_name)
303
+ if text_values:
304
+ values = dict.fromkeys(
305
+ str(value)
306
+ for member in group.members
307
+ if (value := getattr(member, field_name)) is not None
308
+ )
309
+ return ", ".join(values) if values else None
298
310
  value = group.sum(field_name)
299
311
  if isinstance(value, Measurement):
300
312
  return measurement_to_graphql_payload(value, target_unit)
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  from datetime import datetime, timedelta
7
+ import inspect
7
8
  import json
8
9
  from typing import TYPE_CHECKING, Any
9
10
 
@@ -54,6 +55,7 @@ from general_manager.chat.tools import (
54
55
  execute_chat_tool,
55
56
  get_tool_definitions,
56
57
  )
58
+ from general_manager.chat.turns import TurnState
57
59
 
58
60
  if TYPE_CHECKING:
59
61
  from general_manager.chat.models import ChatConversation
@@ -99,14 +101,23 @@ async def _iter_provider_events(
99
101
  stream_timeout = float(provider_config.get("stream_timeout_seconds", 30))
100
102
  stream = provider.complete(messages, tools).__aiter__()
101
103
  first_chunk = True
102
- while True:
103
- timeout = request_timeout if first_chunk else stream_timeout
104
- try:
105
- event = await asyncio.wait_for(stream.__anext__(), timeout=timeout)
106
- except StopAsyncIteration:
107
- return
108
- first_chunk = False
109
- yield event
104
+ try:
105
+ while True:
106
+ timeout = request_timeout if first_chunk else stream_timeout
107
+ try:
108
+ event = await asyncio.wait_for(stream.__anext__(), timeout=timeout)
109
+ except StopAsyncIteration:
110
+ return
111
+ first_chunk = False
112
+ yield event
113
+ if isinstance(event, DoneEvent):
114
+ return
115
+ finally:
116
+ close = getattr(stream, "aclose", None)
117
+ if callable(close):
118
+ result = close()
119
+ if inspect.isawaitable(result):
120
+ await result
110
121
 
111
122
 
112
123
  def _last_user_text(messages: list[Message]) -> str:
@@ -371,6 +382,7 @@ class ChatConsumer(_ChatConsumerBase):
371
382
  {"message": text, "session_key": self.session_key},
372
383
  )
373
384
  messages = [Message(role="system", content=build_system_prompt())]
385
+ turn_state = TurnState.from_settings(get_chat_settings())
374
386
  if self.conversation is not None:
375
387
  from general_manager.chat.context import prepare_conversation_messages
376
388
 
@@ -379,6 +391,7 @@ class ChatConsumer(_ChatConsumerBase):
379
391
  self.provider,
380
392
  allow_summarization=not get_planned_chat_settings().enabled,
381
393
  scope=self.scope,
394
+ turn_state=turn_state,
382
395
  )
383
396
  else:
384
397
  messages.extend(
@@ -391,7 +404,7 @@ class ChatConsumer(_ChatConsumerBase):
391
404
  conversation_id=getattr(self.conversation, "pk", None),
392
405
  )
393
406
  started_background_turn = await self._stream_message_turn(
394
- text, messages, history
407
+ text, messages, history, turn_state=turn_state
395
408
  )
396
409
  except Exception as exc: # noqa: BLE001
397
410
  emit_chat_error(
@@ -416,18 +429,47 @@ class ChatConsumer(_ChatConsumerBase):
416
429
  tool_retries: int,
417
430
  tool_calls: list[dict[str, Any]] | None = None,
418
431
  recovered_missing_tools: bool = False,
432
+ turn_state: TurnState | None = None,
433
+ allow_tools: bool = True,
419
434
  ) -> None:
420
435
  tool_calls = list(tool_calls or [])
421
436
  assistant_chunks: list[str] = []
422
437
  provider_tool_events: list[ToolCallEvent] = []
423
- self._provider_task = asyncio.current_task()
424
438
  recover_missing_tools = bool(
425
439
  get_chat_settings().get("recover_missing_tool_calls", False)
426
440
  )
441
+ turn_state = turn_state or TurnState.from_settings(get_chat_settings())
442
+ turn_state.tool_retries = tool_retries
443
+ if not turn_state.reserve_round():
444
+ await self.send_json(
445
+ {
446
+ "type": "error",
447
+ "message": "Chat turn limit exceeded.",
448
+ "code": "turn_limit",
449
+ }
450
+ )
451
+ return
452
+ rate_limited = await sync_to_async(enforce_chat_rate_limit)(
453
+ self.scope, count_request=False
454
+ )
455
+ if isinstance(rate_limited, dict):
456
+ await self.send_json(
457
+ {
458
+ "type": "error",
459
+ "message": "Chat rate limit exceeded. Try again later.",
460
+ "code": "rate_limited",
461
+ "retry_after_seconds": rate_limited["retry_after_seconds"],
462
+ }
463
+ )
464
+ return
465
+ self._provider_task = asyncio.current_task()
466
+ provider_events = _iter_provider_events(
467
+ self.provider,
468
+ messages,
469
+ self._build_tool_definitions() if allow_tools else [],
470
+ )
427
471
  try:
428
- async for event in _iter_provider_events(
429
- self.provider, messages, self._build_tool_definitions()
430
- ):
472
+ async for event in provider_events:
431
473
  if isinstance(event, TextChunkEvent):
432
474
  assistant_chunks.append(event.content)
433
475
  if not recover_missing_tools:
@@ -438,13 +480,37 @@ class ChatConsumer(_ChatConsumerBase):
438
480
  provider_tool_events.append(event)
439
481
  continue
440
482
  elif isinstance(event, DoneEvent):
441
- if provider_tool_events:
442
- await sync_to_async(enforce_chat_rate_limit)(
443
- self.scope,
444
- input_tokens=event.usage.input_tokens,
445
- output_tokens=event.usage.output_tokens,
446
- count_request=False,
483
+ turn_state.record_usage(event.usage)
484
+ rate_limited = await sync_to_async(enforce_chat_rate_limit)(
485
+ self.scope,
486
+ input_tokens=event.usage.input_tokens,
487
+ output_tokens=event.usage.output_tokens,
488
+ count_request=False,
489
+ )
490
+ # Account completion before cleanup, but do not continue or finish first.
491
+ await provider_events.aclose()
492
+ if isinstance(rate_limited, dict):
493
+ await self.send_json(
494
+ {
495
+ "type": "error",
496
+ "message": "Chat rate limit exceeded. Try again later.",
497
+ "code": "rate_limited",
498
+ "retry_after_seconds": rate_limited[
499
+ "retry_after_seconds"
500
+ ],
501
+ }
447
502
  )
503
+ return
504
+ if provider_tool_events:
505
+ if not allow_tools:
506
+ await self.send_json(
507
+ {
508
+ "type": "error",
509
+ "message": "Chat tool retry limit exceeded.",
510
+ "code": "tool_retry_limit",
511
+ }
512
+ )
513
+ return
448
514
  await self._handle_tool_batch(
449
515
  provider_tool_events,
450
516
  messages,
@@ -452,6 +518,7 @@ class ChatConsumer(_ChatConsumerBase):
452
518
  tool_retries=tool_retries,
453
519
  tool_calls=tool_calls,
454
520
  recovered_missing_tools=recovered_missing_tools,
521
+ turn_state=turn_state,
455
522
  )
456
523
  return
457
524
  if assistant_chunks:
@@ -480,6 +547,7 @@ class ChatConsumer(_ChatConsumerBase):
480
547
  tool_retries=tool_retries,
481
548
  tool_calls=tool_calls,
482
549
  recovered_missing_tools=True,
550
+ turn_state=turn_state,
483
551
  )
484
552
  return
485
553
  if (
@@ -505,6 +573,7 @@ class ChatConsumer(_ChatConsumerBase):
505
573
  tool_retries=tool_retries,
506
574
  tool_calls=tool_calls,
507
575
  recovered_missing_tools=True,
576
+ turn_state=turn_state,
508
577
  )
509
578
  return
510
579
  if recover_missing_tools:
@@ -541,24 +610,28 @@ class ChatConsumer(_ChatConsumerBase):
541
610
  tool_retries=tool_retries,
542
611
  tool_calls=tool_calls,
543
612
  recovered_missing_tools=True,
613
+ turn_state=turn_state,
544
614
  )
545
615
  return
546
- await sync_to_async(enforce_chat_rate_limit)(
547
- self.scope,
548
- input_tokens=event.usage.input_tokens,
549
- output_tokens=event.usage.output_tokens,
550
- count_request=False,
551
- )
552
616
  await self.send_json(
553
617
  {
554
618
  "type": "done",
555
619
  "usage": {
556
- "input_tokens": event.usage.input_tokens,
557
- "output_tokens": event.usage.output_tokens,
620
+ "input_tokens": turn_state.usage.input_tokens,
621
+ "output_tokens": turn_state.usage.output_tokens,
558
622
  },
559
623
  }
560
624
  )
561
625
  if provider_tool_events:
626
+ if not allow_tools:
627
+ await self.send_json(
628
+ {
629
+ "type": "error",
630
+ "message": "Chat tool retry limit exceeded.",
631
+ "code": "tool_retry_limit",
632
+ }
633
+ )
634
+ return
562
635
  await self._handle_tool_batch(
563
636
  provider_tool_events,
564
637
  messages,
@@ -566,9 +639,13 @@ class ChatConsumer(_ChatConsumerBase):
566
639
  tool_retries=tool_retries,
567
640
  tool_calls=tool_calls,
568
641
  recovered_missing_tools=recovered_missing_tools,
642
+ turn_state=turn_state,
569
643
  )
570
644
  finally:
571
- self._provider_task = None
645
+ try:
646
+ await provider_events.aclose()
647
+ finally:
648
+ self._provider_task = None
572
649
 
573
650
  @staticmethod
574
651
  def _planned_catalog_summary(settings: Any) -> dict[str, Any]:
@@ -595,11 +672,15 @@ class ChatConsumer(_ChatConsumerBase):
595
672
  text: str,
596
673
  messages: list[Message],
597
674
  history: list[dict[str, str]],
675
+ *,
676
+ turn_state: TurnState | None = None,
598
677
  ) -> bool:
599
678
  """Plan after admission, retaining the unchanged legacy turn as fallback."""
600
679
  planned_settings = get_planned_chat_settings()
601
680
  if not planned_settings.enabled:
602
- await self._stream_provider_turn(messages, history, tool_retries=0)
681
+ await self._stream_provider_turn(
682
+ messages, history, tool_retries=0, turn_state=turn_state
683
+ )
603
684
  return False
604
685
  self._provider_task = asyncio.create_task(
605
686
  self._stream_planned_turn(text, messages, history, planned_settings)
@@ -669,6 +750,8 @@ class ChatConsumer(_ChatConsumerBase):
669
750
  tool_retries: int,
670
751
  tool_calls: list[dict[str, Any]] | None = None,
671
752
  recovered_missing_tools: bool = False,
753
+ turn_state: TurnState | None = None,
754
+ allow_tools: bool = True,
672
755
  ) -> bool:
673
756
  """Execute a completed non-mutation tool batch before continuing once."""
674
757
  if len(events) > 1 and any(event.name == "mutate" for event in events):
@@ -688,6 +771,8 @@ class ChatConsumer(_ChatConsumerBase):
688
771
  tool_retries=tool_retries,
689
772
  tool_calls=tool_calls,
690
773
  recovered_missing_tools=recovered_missing_tools,
774
+ turn_state=turn_state,
775
+ allow_tools=allow_tools,
691
776
  )
692
777
 
693
778
  max_retries = int(get_chat_settings().get("max_retries_per_message", 8))
@@ -704,6 +789,19 @@ class ChatConsumer(_ChatConsumerBase):
704
789
  tool_calls = list(tool_calls or [])
705
790
  results: list[tuple[ToolCallEvent, Any]] = []
706
791
  for event in events:
792
+ rate_limited = await sync_to_async(enforce_chat_rate_limit)(
793
+ self.scope, count_request=False
794
+ )
795
+ if isinstance(rate_limited, dict):
796
+ await self.send_json(
797
+ {
798
+ "type": "error",
799
+ "message": "Chat rate limit exceeded. Try again later.",
800
+ "code": "rate_limited",
801
+ "retry_after_seconds": rate_limited["retry_after_seconds"],
802
+ }
803
+ )
804
+ return False
707
805
  emit_chat_audit_event(
708
806
  "tool_call",
709
807
  {
@@ -778,21 +876,14 @@ class ChatConsumer(_ChatConsumerBase):
778
876
  tool_call_id=event.id,
779
877
  )
780
878
 
781
- if tool_retries + len(events) >= max_retries:
782
- await self.send_json(
783
- {
784
- "type": "error",
785
- "message": "Chat tool retry limit exceeded.",
786
- "code": "tool_retry_limit",
787
- }
788
- )
789
- return True
790
879
  await self._stream_provider_turn(
791
880
  messages,
792
881
  history,
793
882
  tool_retries=tool_retries + len(events),
794
883
  tool_calls=tool_calls,
795
884
  recovered_missing_tools=recovered_missing_tools,
885
+ turn_state=turn_state,
886
+ allow_tools=tool_retries + len(events) < max_retries,
796
887
  )
797
888
  return True
798
889
 
@@ -805,6 +896,8 @@ class ChatConsumer(_ChatConsumerBase):
805
896
  tool_retries: int,
806
897
  tool_calls: list[dict[str, Any]] | None = None,
807
898
  recovered_missing_tools: bool = False,
899
+ turn_state: TurnState | None = None,
900
+ allow_tools: bool = True,
808
901
  ) -> bool:
809
902
  """Retain the single-call compatibility entry point."""
810
903
  return await self._handle_tool_batch(
@@ -814,6 +907,8 @@ class ChatConsumer(_ChatConsumerBase):
814
907
  tool_retries=tool_retries,
815
908
  tool_calls=tool_calls,
816
909
  recovered_missing_tools=recovered_missing_tools,
910
+ turn_state=turn_state,
911
+ allow_tools=allow_tools,
817
912
  )
818
913
 
819
914
  async def _handle_single_tool_call(
@@ -825,8 +920,43 @@ class ChatConsumer(_ChatConsumerBase):
825
920
  tool_retries: int,
826
921
  tool_calls: list[dict[str, Any]] | None = None,
827
922
  recovered_missing_tools: bool = False,
923
+ turn_state: TurnState | None = None,
924
+ allow_tools: bool = True,
828
925
  ) -> bool:
829
926
  tool_calls = list(tool_calls or [])
927
+ turn_state = turn_state or TurnState.from_settings(get_chat_settings())
928
+ rate_limited = await sync_to_async(enforce_chat_rate_limit)(
929
+ self.scope, count_request=False
930
+ )
931
+ if isinstance(rate_limited, dict):
932
+ await self.send_json(
933
+ {
934
+ "type": "error",
935
+ "message": "Chat rate limit exceeded. Try again later.",
936
+ "code": "rate_limited",
937
+ "retry_after_seconds": rate_limited["retry_after_seconds"],
938
+ }
939
+ )
940
+ return False
941
+ max_retries = int(get_chat_settings().get("max_retries_per_message", 8))
942
+ if event.name != "mutate" and tool_retries >= max_retries:
943
+ await self.send_json(
944
+ {
945
+ "type": "error",
946
+ "message": "Chat tool retry limit exceeded.",
947
+ "code": "tool_retry_limit",
948
+ }
949
+ )
950
+ return False
951
+ if event.name == "mutate" and not turn_state.reserve_mutation():
952
+ await self.send_json(
953
+ {
954
+ "type": "error",
955
+ "message": "Chat mutation limit exceeded.",
956
+ "code": "mutation_limit",
957
+ }
958
+ )
959
+ return False
830
960
  emit_chat_audit_event(
831
961
  "tool_call",
832
962
  {
@@ -887,7 +1017,10 @@ class ChatConsumer(_ChatConsumerBase):
887
1017
  self.conversation,
888
1018
  confirmation_id=event.id,
889
1019
  mutation_name=str(result["mutation"]),
890
- payload={"input": result["input"]},
1020
+ payload={
1021
+ "input": result["input"],
1022
+ "_gm_turn_state": turn_state.as_payload(),
1023
+ },
891
1024
  timeout_seconds=timeout_seconds,
892
1025
  )
893
1026
  except Exception as exc: # noqa: BLE001
@@ -922,6 +1055,7 @@ class ChatConsumer(_ChatConsumerBase):
922
1055
  "history": history,
923
1056
  "expires_at": timezone.now() + timedelta(seconds=timeout_seconds),
924
1057
  "durable": durable,
1058
+ "turn_state": turn_state,
925
1059
  }
926
1060
  self._confirmation_waiter = asyncio.get_running_loop().create_future()
927
1061
  self._confirmation_timeout_task = asyncio.create_task(
@@ -985,21 +1119,14 @@ class ChatConsumer(_ChatConsumerBase):
985
1119
  )
986
1120
  next_tool_retries = tool_retries + (0 if event.name == "mutate" else 1)
987
1121
  max_retries = int(get_chat_settings().get("max_retries_per_message", 8))
988
- if event.name != "mutate" and next_tool_retries >= max_retries:
989
- await self.send_json(
990
- {
991
- "type": "error",
992
- "message": "Chat tool retry limit exceeded.",
993
- "code": "tool_retry_limit",
994
- }
995
- )
996
- return True
997
1122
  await self._stream_provider_turn(
998
1123
  messages,
999
1124
  history,
1000
1125
  tool_retries=next_tool_retries,
1001
1126
  tool_calls=tool_calls,
1002
1127
  recovered_missing_tools=recovered_missing_tools,
1128
+ turn_state=turn_state,
1129
+ allow_tools=next_tool_retries < max_retries,
1003
1130
  )
1004
1131
  return True
1005
1132
 
@@ -1150,9 +1277,18 @@ class ChatConsumer(_ChatConsumerBase):
1150
1277
  self.conversation,
1151
1278
  self.provider,
1152
1279
  scope=self.scope,
1280
+ turn_state=pending.get("turn_state"),
1153
1281
  )
1154
1282
  history = await self._load_history()
1155
- await self._stream_provider_turn(messages, history, tool_retries=0)
1283
+ await self._stream_provider_turn(
1284
+ messages,
1285
+ history,
1286
+ tool_retries=(
1287
+ pending.get("turn_state")
1288
+ or TurnState.from_settings(get_chat_settings())
1289
+ ).tool_retries,
1290
+ turn_state=pending.get("turn_state"),
1291
+ )
1156
1292
  finally:
1157
1293
  if not followup_turn.done():
1158
1294
  followup_turn.set_result(None)
@@ -1171,6 +1307,21 @@ class ChatConsumer(_ChatConsumerBase):
1171
1307
 
1172
1308
  from general_manager.chat.models import ChatPendingConfirmation
1173
1309
 
1310
+ if confirmed:
1311
+ rate_limited = await sync_to_async(enforce_chat_rate_limit)(
1312
+ self.scope, count_request=False
1313
+ )
1314
+ if isinstance(rate_limited, dict):
1315
+ await self.send_json(
1316
+ {
1317
+ "type": "error",
1318
+ "message": "Chat rate limit exceeded. Try again later.",
1319
+ "code": "rate_limited",
1320
+ "retry_after_seconds": rate_limited["retry_after_seconds"],
1321
+ }
1322
+ )
1323
+ return
1324
+
1174
1325
  db_pending: Any | None = None
1175
1326
  if pending is None and self.conversation is not None:
1176
1327
  db_pending = await sync_to_async(
@@ -1196,6 +1347,9 @@ class ChatConsumer(_ChatConsumerBase):
1196
1347
  "history": history,
1197
1348
  "expires_at": db_pending.expires_at,
1198
1349
  "durable": False,
1350
+ "turn_state": TurnState.from_payload(
1351
+ get_chat_settings(), db_pending.payload
1352
+ ),
1199
1353
  "rebuild_context_after_tool_result": True,
1200
1354
  }
1201
1355
  if pending is None or confirmation_id != pending.get("id"):
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  from collections.abc import Awaitable, Callable, Mapping
7
+ import inspect
7
8
  from typing import Any
8
9
 
9
10
  from asgiref.sync import sync_to_async
@@ -19,6 +20,17 @@ from general_manager.chat.providers.base import DoneEvent, Message, TextChunkEve
19
20
  from general_manager.chat.rate_limits import enforce_chat_rate_limit
20
21
  from general_manager.chat.settings import get_chat_settings
21
22
  from general_manager.chat.system_prompt import build_system_prompt
23
+ from general_manager.chat.turns import TurnState
24
+
25
+
26
+ class ChatSummaryRateLimitExceeded(RuntimeError):
27
+ """Stop a request when history compression consumes its token allowance."""
28
+
29
+ public_reason = "rate_limited"
30
+
31
+ def __init__(self, retry_after_seconds: int) -> None:
32
+ super().__init__("Chat summary token budget exhausted.")
33
+ self.retry_after_seconds = retry_after_seconds
22
34
 
23
35
 
24
36
  async def summarize_messages_with_provider(
@@ -26,6 +38,7 @@ async def summarize_messages_with_provider(
26
38
  messages: list[Any],
27
39
  *,
28
40
  scope: dict[str, Any] | None = None,
41
+ turn_state: TurnState | None = None,
29
42
  ) -> str:
30
43
  """Summarize one history prefix within a whole-request provider deadline."""
31
44
  prompt_messages = [
@@ -45,16 +58,40 @@ async def summarize_messages_with_provider(
45
58
  chunks: list[str] = []
46
59
 
47
60
  async def consume() -> None:
48
- async for event in provider.complete(prompt_messages, []):
49
- if isinstance(event, TextChunkEvent):
50
- chunks.append(event.content)
51
- elif isinstance(event, DoneEvent) and scope is not None:
52
- await sync_to_async(enforce_chat_rate_limit)(
53
- scope,
54
- input_tokens=event.usage.input_tokens,
55
- output_tokens=event.usage.output_tokens,
56
- count_request=False,
57
- )
61
+ if turn_state is not None and not turn_state.reserve_round():
62
+ return
63
+ if scope is not None:
64
+ rate_limited = await sync_to_async(enforce_chat_rate_limit)(
65
+ scope, count_request=False
66
+ )
67
+ if isinstance(rate_limited, dict):
68
+ raise ChatSummaryRateLimitExceeded(rate_limited["retry_after_seconds"])
69
+ events = provider.complete(prompt_messages, [])
70
+ try:
71
+ async for event in events:
72
+ if isinstance(event, TextChunkEvent):
73
+ chunks.append(event.content)
74
+ elif isinstance(event, DoneEvent):
75
+ if turn_state is not None:
76
+ turn_state.record_usage(event.usage)
77
+ if scope is not None:
78
+ rate_limited = await sync_to_async(enforce_chat_rate_limit)(
79
+ scope,
80
+ input_tokens=event.usage.input_tokens,
81
+ output_tokens=event.usage.output_tokens,
82
+ count_request=False,
83
+ )
84
+ if isinstance(rate_limited, dict):
85
+ raise ChatSummaryRateLimitExceeded(
86
+ rate_limited["retry_after_seconds"]
87
+ )
88
+ return
89
+ finally:
90
+ close = getattr(events, "aclose", None)
91
+ if callable(close):
92
+ result = close()
93
+ if inspect.isawaitable(result):
94
+ await result
58
95
 
59
96
  await asyncio.wait_for(consume(), timeout=timeout_seconds)
60
97
  return "".join(chunks).strip()
@@ -67,6 +104,7 @@ async def prepare_conversation_messages(
67
104
  allow_summarization: bool = True,
68
105
  scope: dict[str, Any] | None = None,
69
106
  summarize: Callable[[Any, list[Any]], Awaitable[str]] | None = None,
107
+ turn_state: TurnState | None = None,
70
108
  ) -> list[Message]:
71
109
  """Return system plus bounded durable history for a legacy provider turn."""
72
110
  settings = get_chat_settings()
@@ -86,7 +124,7 @@ async def prepare_conversation_messages(
86
124
  await summarize(provider, older_messages)
87
125
  if summarize is not None
88
126
  else await summarize_messages_with_provider(
89
- provider, older_messages, scope=scope
127
+ provider, older_messages, scope=scope, turn_state=turn_state
90
128
  )
91
129
  )
92
130
  if summary_text: