hyperforge 1.0.0.post412__tar.gz → 1.0.0.post418__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 (142) hide show
  1. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/PKG-INFO +1 -1
  2. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/pyproject.toml +1 -1
  3. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/pyproject.toml.orig +1 -1
  4. hyperforge-1.0.0.post418/src/hyperforge/alembic/versions/8c7d2e4f91ab_structure_legacy_llm_fields.py +24 -0
  5. hyperforge-1.0.0.post418/src/hyperforge/db/legacy_llm_migration.py +132 -0
  6. hyperforge-1.0.0.post418/src/hyperforge/errors.py +105 -0
  7. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/session.py +11 -5
  8. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/README.md +0 -0
  9. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/VERSION +0 -0
  10. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/__init__.py +0 -0
  11. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/__init__.py +0 -0
  12. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/auth.py +0 -0
  13. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/card.py +0 -0
  14. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/context.py +0 -0
  15. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/executor.py +0 -0
  16. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/run.py +0 -0
  17. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/server.py +0 -0
  18. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/settings.py +0 -0
  19. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/a2a/task_store.py +0 -0
  20. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/agent.py +0 -0
  21. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/README +0 -0
  22. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/env.py +0 -0
  23. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/script.py.mako +0 -0
  24. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/049a4aed19e1_create_generation_table.py +0 -0
  25. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/1416cb41bd49_add_downloads_table.py +0 -0
  26. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/2847934e2d59_unique_driver.py +0 -0
  27. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/4d89a36c8bda_memory.py +0 -0
  28. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/6a23d8ef74c1_add_sync_oauth_credentials.py +0 -0
  29. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/72b997edb05d_identifier_for_drivers.py +0 -0
  30. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/9c6f6a1b4e7f_workflows_default_fk.py +0 -0
  31. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/b8edb72295e0_.py +0 -0
  32. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic/versions/f3d2c1b0a9e8_soft_delete_workflows.py +0 -0
  33. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/alembic.ini +0 -0
  34. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/__init__.py +0 -0
  35. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/app.py +0 -0
  36. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/authentication.py +0 -0
  37. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/commands.py +0 -0
  38. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/logging.py +0 -0
  39. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/mcp_server_pool.py +0 -0
  40. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/models.py +0 -0
  41. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/session.py +0 -0
  42. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/settings.py +0 -0
  43. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/utils.py +0 -0
  44. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/__init__.py +0 -0
  45. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/agents.py +0 -0
  46. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/interaction.py +0 -0
  47. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/mcp_content.py +0 -0
  48. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/mcp_interaction.py +0 -0
  49. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/oauth.py +0 -0
  50. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/prompt.py +0 -0
  51. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/router.py +0 -0
  52. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/schema.py +0 -0
  53. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/session.py +0 -0
  54. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/utils.py +0 -0
  55. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/api/v1/workflows.py +0 -0
  56. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/arag.py +0 -0
  57. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/broker/__init__.py +0 -0
  58. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/broker/local.py +0 -0
  59. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/broker/redis.py +0 -0
  60. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/codemode/__init__.py +0 -0
  61. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/codemode/model.py +0 -0
  62. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/codemode/sandbox.py +0 -0
  63. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/codemode/worker.py +0 -0
  64. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/completions.py +0 -0
  65. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/configure.py +0 -0
  66. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/context/__init__.py +0 -0
  67. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/context/agent.py +0 -0
  68. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/context/config.py +0 -0
  69. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/database.py +0 -0
  70. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/db/__init__.py +0 -0
  71. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/db/agents.py +0 -0
  72. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/db/encryption.py +0 -0
  73. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/db/exceptions.py +0 -0
  74. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/db/llm_migration_utils.py +0 -0
  75. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/db/settings.py +0 -0
  76. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/db/workflow_cleanup.py +0 -0
  77. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/definition.py +0 -0
  78. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/driver.py +0 -0
  79. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/dummy.py +0 -0
  80. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/engine.py +0 -0
  81. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/exceptions.py +0 -0
  82. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/feature_flag.py +0 -0
  83. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/fixtures.py +0 -0
  84. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness/__init__.py +0 -0
  85. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness/agent.py +0 -0
  86. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness/config.py +0 -0
  87. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/__init__.py +0 -0
  88. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/agents.py +0 -0
  89. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/clients.py +0 -0
  90. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/context.py +0 -0
  91. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/harness.py +0 -0
  92. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/models.py +0 -0
  93. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/schema.py +0 -0
  94. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/storage.py +0 -0
  95. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/tools/__init__.py +0 -0
  96. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/tools/codemode.py +0 -0
  97. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/tools/core.py +0 -0
  98. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/tools/utils.py +0 -0
  99. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/harness_sdk/usage.py +0 -0
  100. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/interaction.py +0 -0
  101. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/json_schema.py +0 -0
  102. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/llm.py +0 -0
  103. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/llm_config.py +0 -0
  104. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/manager.py +0 -0
  105. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/memory/__init__.py +0 -0
  106. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/memory/memory.py +0 -0
  107. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/minimal_fixtures.py +0 -0
  108. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/models.py +0 -0
  109. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/openapi.py +0 -0
  110. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/prompts.py +0 -0
  111. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/pubsub.py +0 -0
  112. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/py.typed +0 -0
  113. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/redis_utils.py +0 -0
  114. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/result_payload.py +0 -0
  115. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/retrieval/__init__.py +0 -0
  116. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/retrieval/agent.py +0 -0
  117. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/retrieval/config.py +0 -0
  118. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/__init__.py +0 -0
  119. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/cache.py +0 -0
  120. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/run.py +0 -0
  121. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/sandbox.py +0 -0
  122. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/settings.py +0 -0
  123. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/utils.py +0 -0
  124. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/server/web.py +0 -0
  125. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/settings.py +0 -0
  126. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/__init__.py +0 -0
  127. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/agent.py +0 -0
  128. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/app.py +0 -0
  129. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/config.py +0 -0
  130. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/const.py +0 -0
  131. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/oauth.py +0 -0
  132. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/run.py +0 -0
  133. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/settings.py +0 -0
  134. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/standalone/ui_router.py +0 -0
  135. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/static/assets/index-DLejS5jC.js +0 -0
  136. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/static/assets/index-MoxkZ1_3.css +0 -0
  137. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/static/favicon.svg +0 -0
  138. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/static/index.html +0 -0
  139. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/trace.py +0 -0
  140. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/utils/__init__.py +0 -0
  141. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/utils/http.py +0 -0
  142. {hyperforge-1.0.0.post412 → hyperforge-1.0.0.post418}/src/hyperforge/workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperforge
3
- Version: 1.0.0.post412
3
+ Version: 1.0.0.post418
4
4
  Summary: Agentic Framework for Orchestrated Runtime, Governance, and Execution
5
5
  Author: AI Data Team
6
6
  Author-email: AI Data Team <learning@nuclia.com>
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "hyperforge"
7
- version = "1.0.0.post412"
7
+ version = "1.0.0.post418"
8
8
  description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "hyperforge"
7
- version = "1.0.0.post412"
7
+ version = "1.0.0.post418"
8
8
  authors = [{ name = "AI Data Team", email = "learning@nuclia.com" }]
9
9
  description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
10
10
  readme = "README.md"
@@ -0,0 +1,24 @@
1
+ """Structure legacy string model fields in stored agent configurations.
2
+
3
+ Revision ID: 8c7d2e4f91ab
4
+ Revises: 6a23d8ef74c1
5
+ """
6
+
7
+ from typing import Sequence, Union
8
+
9
+ from alembic import op
10
+
11
+ from hyperforge.db.legacy_llm_migration import migrate_legacy_llm_fields
12
+
13
+ revision: str = "8c7d2e4f91ab"
14
+ down_revision: Union[str, None] = "6a23d8ef74c1"
15
+ branch_labels: Union[str, Sequence[str], None] = None
16
+ depends_on: Union[str, Sequence[str], None] = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ migrate_legacy_llm_fields(op.get_bind())
21
+
22
+
23
+ def downgrade() -> None:
24
+ pass
@@ -0,0 +1,132 @@
1
+ """Convert old model strings in stored Hyperforge agent configs to LLMConfig.
2
+
3
+ Only fields already declared as LLMField in Hyperforge are included. Agent
4
+ plugins with string-typed model fields must remain strings until their schemas
5
+ and runtimes are migrated separately.
6
+ """
7
+
8
+ import json
9
+ from typing import Any
10
+
11
+ from sqlalchemy import text
12
+ from sqlalchemy.engine import Connection
13
+
14
+ from hyperforge.db.llm_migration_utils import HYPERFORGE_LLM_TABLES
15
+ from hyperforge.llm_config import LLM_CONFIG_TYPE
16
+
17
+ # Keep this inventory aligned with the LLMField declarations in Hyperforge.
18
+ # Context fields are inherited by ContextAgentConfig subclasses, including
19
+ # the installed progress-agents plugins. Their own string-typed model fields
20
+ # are deliberately excluded from MODEL_FIELDS.
21
+ CONTEXT_MODULES = {
22
+ "a2a",
23
+ "advanced_ask",
24
+ "ask",
25
+ "basic_ask",
26
+ "brave",
27
+ "context_conditional",
28
+ "cypher",
29
+ "google",
30
+ "http",
31
+ "marklogic",
32
+ "mcp",
33
+ "multi_mcp",
34
+ "pandas",
35
+ "perplexity",
36
+ "perplexity_search",
37
+ "restricted",
38
+ "salesforce",
39
+ "smart",
40
+ "snowflake",
41
+ "sparql",
42
+ "sql",
43
+ "static",
44
+ "static_string",
45
+ "sitefinity",
46
+ "sync",
47
+ }
48
+ CONTEXT_FIELDS = {"context_validation_model", "rephrase_model", "summarize_model"}
49
+ MODEL_FIELDS = {
50
+ "advanced_ask": {"generative_model"},
51
+ "ask": {"configuration_model"},
52
+ "basic_ask": {"generative_model"},
53
+ "context_conditional": {"model"},
54
+ "external": {"model"},
55
+ "generate": {"model"},
56
+ "generation_conditional": {"model"},
57
+ "mcp": {"tool_choice_model", "sampling_model"},
58
+ "multi_mcp": {"summarize_model", "tool_choice_model"},
59
+ "post_conditional": {"model"},
60
+ "pre_conditional": {"model"},
61
+ "related": {"model"},
62
+ "rephrase": {"model"},
63
+ "restart": {"model"},
64
+ "restricted": {"decision_model"},
65
+ "salesforce": {"tool_choice_model", "sampling_model"},
66
+ "smart": {"planner_model", "executor_model"},
67
+ "summarize": {"model"},
68
+ "sync": {"generative_model"},
69
+ }
70
+ NESTED_FIELDS_BY_MODULE = {
71
+ "advanced_generation": {"summarize_config", "data_viz_config"},
72
+ "context_conditional": {"then", "else_"},
73
+ "generation_conditional": {"then", "else_"},
74
+ "multi_mcp": {"configs"},
75
+ "post_conditional": {"then", "else_"},
76
+ "pre_conditional": {"then", "else_"},
77
+ "restricted": {"agents"},
78
+ "smart": {"registered_agents"},
79
+ }
80
+
81
+
82
+ def wrap_legacy_llm_fields(config: Any) -> bool:
83
+ """Wrap string-valued LLMFields, including those inside nested agents."""
84
+ changed = False
85
+ if isinstance(config, list):
86
+ for item in config:
87
+ changed = wrap_legacy_llm_fields(item) or changed
88
+ elif isinstance(config, dict) and isinstance(config.get("module"), str):
89
+ module = config["module"]
90
+ fields = MODEL_FIELDS.get(module, set())
91
+ if module in CONTEXT_MODULES:
92
+ fields = fields | CONTEXT_FIELDS
93
+ for field in fields:
94
+ value = config.get(field)
95
+ if isinstance(value, str):
96
+ config[field] = {"_type": LLM_CONFIG_TYPE, "model_id": value}
97
+ changed = True
98
+ nested_fields = NESTED_FIELDS_BY_MODULE.get(module, set())
99
+ if module in CONTEXT_MODULES:
100
+ nested_fields = nested_fields | {"fallback", "next_agent"}
101
+ for field in nested_fields:
102
+ if field in config:
103
+ changed = wrap_legacy_llm_fields(config[field]) or changed
104
+ return changed
105
+
106
+
107
+ def migrate_legacy_llm_fields(conn: Connection) -> int:
108
+ """Convert stored agent configs, leaving unrelated and structured data alone."""
109
+ modified = 0
110
+ for table, column in HYPERFORGE_LLM_TABLES:
111
+ last_id = "00000000-0000-0000-0000-000000000000"
112
+ while rows := conn.execute(
113
+ text(
114
+ f"SELECT id, {column} FROM {table} "
115
+ "WHERE id > CAST(:last_id AS uuid) ORDER BY id LIMIT 500"
116
+ ),
117
+ {"last_id": last_id},
118
+ ).fetchall():
119
+ for row_id, config in rows:
120
+ if isinstance(config, str):
121
+ config = json.loads(config)
122
+ if wrap_legacy_llm_fields(config):
123
+ conn.execute(
124
+ text(
125
+ f"UPDATE {table} SET {column} = CAST(:config AS jsonb) "
126
+ "WHERE id = :id"
127
+ ),
128
+ {"config": json.dumps(config), "id": row_id},
129
+ )
130
+ modified += 1
131
+ last_id = str(rows[-1][0])
132
+ return modified
@@ -0,0 +1,105 @@
1
+ import asyncio
2
+ import re
3
+ from collections.abc import Iterator
4
+
5
+ _AUTH_CREDENTIAL = re.compile(r"(?i)\b(Bearer|Basic)\s+[^\s,;]+")
6
+ _AUTHORIZATION_VALUE = re.compile(
7
+ r"(?i)\b(authorization)(\s*[:=]\s*)"
8
+ r"(?:\"[^\"]*\"|'[^']*'|(?:(?:Basic|Bearer)\s+)?[^\s,;]+)"
9
+ )
10
+ _COOKIE_VALUE = re.compile(r"(?i)\b(set-cookie|cookie)(\s*:\s*)[^\r\n]+")
11
+ _SECRET_KEYS = (
12
+ r"api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|"
13
+ r"credential|password|private[_-]?key|refresh[_-]?token|secret|session[_-]?id|"
14
+ r"oauth[_-]?token|token"
15
+ )
16
+ _QUOTED_SECRET_VALUE = re.compile(
17
+ rf"(?i)\b({_SECRET_KEYS})\b(\s*[\"']?\s*[:=]\s*)([\"'])[^\"']*\3"
18
+ )
19
+ _SECRET_VALUE = re.compile(
20
+ rf"(?i)\b({_SECRET_KEYS})\b(\s*[\"']?\s*[:=]\s*)([^\"',\s;&}}]+)"
21
+ )
22
+ _URL_CREDENTIALS = re.compile(r"(?i)(https?://)[^\s/:@]+:[^\s/@]+@")
23
+
24
+
25
+ def _redact_secrets(detail: str) -> str:
26
+ detail = _URL_CREDENTIALS.sub(r"\1[REDACTED]:[REDACTED]@", detail)
27
+ detail = _AUTHORIZATION_VALUE.sub(r"\1\2[REDACTED]", detail)
28
+ detail = _AUTH_CREDENTIAL.sub(r"\1 [REDACTED]", detail)
29
+ detail = _COOKIE_VALUE.sub(r"\1\2[REDACTED]", detail)
30
+ detail = _QUOTED_SECRET_VALUE.sub(r"\1\2\3[REDACTED]\3", detail)
31
+ return _SECRET_VALUE.sub(r"\1\2[REDACTED]", detail)
32
+
33
+
34
+ def _leaf_exceptions(exc: BaseException) -> Iterator[BaseException]:
35
+ if isinstance(exc, BaseExceptionGroup):
36
+ for nested in exc.exceptions:
37
+ yield from _leaf_exceptions(nested)
38
+ return
39
+
40
+ yield exc
41
+
42
+
43
+ def actionable_exception_group(exc: BaseExceptionGroup) -> BaseExceptionGroup:
44
+ """Return regular exceptions while allowing only cancellation to be discarded."""
45
+ actionable, remainder = exc.split(Exception)
46
+ if actionable is None or (
47
+ remainder is not None
48
+ and any(
49
+ not isinstance(item, asyncio.CancelledError)
50
+ for item in _leaf_exceptions(remainder)
51
+ )
52
+ ):
53
+ raise exc
54
+ return actionable
55
+
56
+
57
+ def _http_status(exc: BaseException) -> int | None:
58
+ response = getattr(exc, "response", None)
59
+ status = getattr(response, "status_code", None)
60
+ if isinstance(status, int):
61
+ return status
62
+
63
+ status = getattr(exc, "status", None)
64
+ return status if isinstance(status, int) else None
65
+
66
+
67
+ def _exception_detail(exc: BaseException) -> str:
68
+ detail = _redact_secrets(str(exc).strip() or type(exc).__name__)
69
+ status = _http_status(exc)
70
+ if status == 401:
71
+ return f"Authentication failed (HTTP 401): {detail}"
72
+ if status == 403:
73
+ return f"Authorization failed (HTTP 403): {detail}"
74
+ return detail
75
+
76
+
77
+ def _exception_priority(exc: BaseException) -> int:
78
+ status = _http_status(exc)
79
+ if status == 401:
80
+ return 0
81
+ if status == 403:
82
+ return 1
83
+ return 2
84
+
85
+
86
+ def exception_detail(exc: BaseException) -> str:
87
+ """Return useful, bounded details from an exception or exception group."""
88
+ leaves = list(_leaf_exceptions(exc))
89
+ actionable = [
90
+ item for item in leaves if not isinstance(item, asyncio.CancelledError)
91
+ ]
92
+ if actionable:
93
+ leaves = actionable
94
+
95
+ leaves.sort(key=_exception_priority)
96
+ details = list(dict.fromkeys(_exception_detail(item) for item in leaves))
97
+ if len(details) == 1:
98
+ return details[0]
99
+
100
+ visible_details = details[:3]
101
+ if len(details) > len(visible_details):
102
+ visible_details.append(
103
+ f"and {len(details) - len(visible_details)} more error(s)"
104
+ )
105
+ return "Multiple errors: " + "; ".join(visible_details)
@@ -19,6 +19,7 @@ from hyperforge.broker import Broker
19
19
  from hyperforge.configure import load_all_configurations, scan
20
20
  from hyperforge.db.agents import AgentManager
21
21
  from hyperforge.engine import State, get_state
22
+ from hyperforge.errors import actionable_exception_group, exception_detail
22
23
  from hyperforge.interaction import (
23
24
  AnswerOperation,
24
25
  AragAnswer,
@@ -375,10 +376,16 @@ class SessionManager:
375
376
  async with asyncio.timeout(self.settings.question_timeout_seconds):
376
377
  await state.agent(question_memory, state.manager)
377
378
 
379
+ except BaseExceptionGroup as exc:
380
+ error_group = actionable_exception_group(exc)
381
+ logger.exception("Answering exception")
382
+ errors.capture_exception(error_group)
383
+ error = ARAGException(detail=exception_detail(error_group))
384
+ observation.set_status("error")
378
385
  except Exception as e:
379
386
  logger.exception("Answering exception")
380
387
  errors.capture_exception(e)
381
- error = ARAGException(detail=str(e))
388
+ error = ARAGException(detail=exception_detail(e))
382
389
  observation.set_status("error")
383
390
  finally:
384
391
  if state.manager is not None:
@@ -387,10 +394,9 @@ class SessionManager:
387
394
  except Exception as e:
388
395
  logger.exception("Error closing Manager")
389
396
  errors.capture_exception(e)
390
-
391
- observation.end()
392
- answer_running.dec()
393
- keepalive.cancel()
397
+ observation.end()
398
+ answer_running.dec()
399
+ keepalive.cancel()
394
400
 
395
401
  await self.callback(
396
402
  topic,