aethergraph 0.1.0a1__py3-none-any.whl → 0.1.0a3__py3-none-any.whl

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 (267) hide show
  1. aethergraph/__init__.py +4 -10
  2. aethergraph/__main__.py +296 -0
  3. aethergraph/api/v1/__init__.py +0 -0
  4. aethergraph/api/v1/agents.py +46 -0
  5. aethergraph/api/v1/apps.py +70 -0
  6. aethergraph/api/v1/artifacts.py +415 -0
  7. aethergraph/api/v1/channels.py +89 -0
  8. aethergraph/api/v1/deps.py +168 -0
  9. aethergraph/api/v1/graphs.py +259 -0
  10. aethergraph/api/v1/identity.py +25 -0
  11. aethergraph/api/v1/memory.py +353 -0
  12. aethergraph/api/v1/misc.py +47 -0
  13. aethergraph/api/v1/pagination.py +29 -0
  14. aethergraph/api/v1/runs.py +568 -0
  15. aethergraph/api/v1/schemas.py +535 -0
  16. aethergraph/api/v1/session.py +323 -0
  17. aethergraph/api/v1/stats.py +201 -0
  18. aethergraph/api/v1/viz.py +152 -0
  19. aethergraph/config/config.py +22 -0
  20. aethergraph/config/loader.py +3 -2
  21. aethergraph/config/storage.py +209 -0
  22. aethergraph/contracts/__init__.py +0 -0
  23. aethergraph/contracts/services/__init__.py +0 -0
  24. aethergraph/contracts/services/artifacts.py +27 -14
  25. aethergraph/contracts/services/memory.py +45 -17
  26. aethergraph/contracts/services/metering.py +129 -0
  27. aethergraph/contracts/services/runs.py +50 -0
  28. aethergraph/contracts/services/sessions.py +87 -0
  29. aethergraph/contracts/services/state_stores.py +3 -0
  30. aethergraph/contracts/services/viz.py +44 -0
  31. aethergraph/contracts/storage/artifact_index.py +88 -0
  32. aethergraph/contracts/storage/artifact_store.py +99 -0
  33. aethergraph/contracts/storage/async_kv.py +34 -0
  34. aethergraph/contracts/storage/blob_store.py +50 -0
  35. aethergraph/contracts/storage/doc_store.py +35 -0
  36. aethergraph/contracts/storage/event_log.py +31 -0
  37. aethergraph/contracts/storage/vector_index.py +48 -0
  38. aethergraph/core/__init__.py +0 -0
  39. aethergraph/core/execution/forward_scheduler.py +13 -2
  40. aethergraph/core/execution/global_scheduler.py +21 -15
  41. aethergraph/core/execution/step_forward.py +10 -1
  42. aethergraph/core/graph/__init__.py +0 -0
  43. aethergraph/core/graph/graph_builder.py +8 -4
  44. aethergraph/core/graph/graph_fn.py +156 -15
  45. aethergraph/core/graph/graph_spec.py +8 -0
  46. aethergraph/core/graph/graphify.py +146 -27
  47. aethergraph/core/graph/node_spec.py +0 -2
  48. aethergraph/core/graph/node_state.py +3 -0
  49. aethergraph/core/graph/task_graph.py +39 -1
  50. aethergraph/core/runtime/__init__.py +0 -0
  51. aethergraph/core/runtime/ad_hoc_context.py +64 -4
  52. aethergraph/core/runtime/base_service.py +28 -4
  53. aethergraph/core/runtime/execution_context.py +13 -15
  54. aethergraph/core/runtime/graph_runner.py +222 -37
  55. aethergraph/core/runtime/node_context.py +510 -6
  56. aethergraph/core/runtime/node_services.py +12 -5
  57. aethergraph/core/runtime/recovery.py +15 -1
  58. aethergraph/core/runtime/run_manager.py +783 -0
  59. aethergraph/core/runtime/run_manager_local.py +204 -0
  60. aethergraph/core/runtime/run_registration.py +2 -2
  61. aethergraph/core/runtime/run_types.py +89 -0
  62. aethergraph/core/runtime/runtime_env.py +136 -7
  63. aethergraph/core/runtime/runtime_metering.py +71 -0
  64. aethergraph/core/runtime/runtime_registry.py +36 -13
  65. aethergraph/core/runtime/runtime_services.py +194 -6
  66. aethergraph/core/tools/builtins/toolset.py +1 -1
  67. aethergraph/core/tools/toolkit.py +5 -0
  68. aethergraph/plugins/agents/default_chat_agent copy.py +90 -0
  69. aethergraph/plugins/agents/default_chat_agent.py +171 -0
  70. aethergraph/plugins/agents/shared.py +81 -0
  71. aethergraph/plugins/channel/adapters/webui.py +112 -112
  72. aethergraph/plugins/channel/routes/webui_routes.py +367 -102
  73. aethergraph/plugins/channel/utils/slack_utils.py +115 -59
  74. aethergraph/plugins/channel/utils/telegram_utils.py +88 -47
  75. aethergraph/plugins/channel/websockets/weibui_ws.py +172 -0
  76. aethergraph/runtime/__init__.py +15 -0
  77. aethergraph/server/app_factory.py +196 -34
  78. aethergraph/server/clients/channel_client.py +202 -0
  79. aethergraph/server/http/channel_http_routes.py +116 -0
  80. aethergraph/server/http/channel_ws_routers.py +45 -0
  81. aethergraph/server/loading.py +117 -0
  82. aethergraph/server/server.py +131 -0
  83. aethergraph/server/server_state.py +240 -0
  84. aethergraph/server/start.py +227 -66
  85. aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  86. aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  87. aethergraph/server/ui_static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  88. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  89. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  90. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  91. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  92. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  93. aethergraph/server/ui_static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  94. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  95. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  96. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  97. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  98. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  99. aethergraph/server/ui_static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  100. aethergraph/server/ui_static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  101. aethergraph/server/ui_static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  102. aethergraph/server/ui_static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  103. aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  104. aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  105. aethergraph/server/ui_static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  106. aethergraph/server/ui_static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  107. aethergraph/server/ui_static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  108. aethergraph/server/ui_static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  109. aethergraph/server/ui_static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  110. aethergraph/server/ui_static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  111. aethergraph/server/ui_static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  112. aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  113. aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  114. aethergraph/server/ui_static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  115. aethergraph/server/ui_static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  116. aethergraph/server/ui_static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  117. aethergraph/server/ui_static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  118. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  119. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  120. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  121. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  122. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  123. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  124. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  125. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  126. aethergraph/server/ui_static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  127. aethergraph/server/ui_static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  128. aethergraph/server/ui_static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  129. aethergraph/server/ui_static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  130. aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  131. aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  132. aethergraph/server/ui_static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  133. aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  134. aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  135. aethergraph/server/ui_static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  136. aethergraph/server/ui_static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  137. aethergraph/server/ui_static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  138. aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  139. aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  140. aethergraph/server/ui_static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  141. aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  142. aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  143. aethergraph/server/ui_static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  144. aethergraph/server/ui_static/assets/index-BR5GtXcZ.css +1 -0
  145. aethergraph/server/ui_static/assets/index-CQ0HZZ83.js +400 -0
  146. aethergraph/server/ui_static/index.html +15 -0
  147. aethergraph/server/ui_static/logo.png +0 -0
  148. aethergraph/services/artifacts/__init__.py +0 -0
  149. aethergraph/services/artifacts/facade.py +1239 -132
  150. aethergraph/services/auth/{dev.py → authn.py} +0 -8
  151. aethergraph/services/auth/authz.py +100 -0
  152. aethergraph/services/channel/__init__.py +0 -0
  153. aethergraph/services/channel/channel_bus.py +19 -1
  154. aethergraph/services/channel/factory.py +13 -1
  155. aethergraph/services/channel/ingress.py +311 -0
  156. aethergraph/services/channel/queue_adapter.py +75 -0
  157. aethergraph/services/channel/session.py +502 -19
  158. aethergraph/services/container/default_container.py +122 -43
  159. aethergraph/services/continuations/continuation.py +6 -0
  160. aethergraph/services/continuations/stores/fs_store.py +19 -0
  161. aethergraph/services/eventhub/event_hub.py +76 -0
  162. aethergraph/services/kv/__init__.py +0 -0
  163. aethergraph/services/kv/ephemeral.py +244 -0
  164. aethergraph/services/llm/__init__.py +0 -0
  165. aethergraph/services/llm/generic_client copy.py +691 -0
  166. aethergraph/services/llm/generic_client.py +1288 -187
  167. aethergraph/services/llm/providers.py +3 -1
  168. aethergraph/services/llm/types.py +47 -0
  169. aethergraph/services/llm/utils.py +284 -0
  170. aethergraph/services/logger/std.py +3 -0
  171. aethergraph/services/mcp/__init__.py +9 -0
  172. aethergraph/services/mcp/http_client.py +38 -0
  173. aethergraph/services/mcp/service.py +225 -1
  174. aethergraph/services/mcp/stdio_client.py +41 -6
  175. aethergraph/services/mcp/ws_client.py +44 -2
  176. aethergraph/services/memory/__init__.py +0 -0
  177. aethergraph/services/memory/distillers/llm_long_term.py +234 -0
  178. aethergraph/services/memory/distillers/llm_meta_summary.py +398 -0
  179. aethergraph/services/memory/distillers/long_term.py +225 -0
  180. aethergraph/services/memory/facade/__init__.py +3 -0
  181. aethergraph/services/memory/facade/chat.py +440 -0
  182. aethergraph/services/memory/facade/core.py +447 -0
  183. aethergraph/services/memory/facade/distillation.py +424 -0
  184. aethergraph/services/memory/facade/rag.py +410 -0
  185. aethergraph/services/memory/facade/results.py +315 -0
  186. aethergraph/services/memory/facade/retrieval.py +139 -0
  187. aethergraph/services/memory/facade/types.py +77 -0
  188. aethergraph/services/memory/facade/utils.py +43 -0
  189. aethergraph/services/memory/facade_dep.py +1539 -0
  190. aethergraph/services/memory/factory.py +9 -3
  191. aethergraph/services/memory/utils.py +10 -0
  192. aethergraph/services/metering/eventlog_metering.py +470 -0
  193. aethergraph/services/metering/noop.py +25 -4
  194. aethergraph/services/rag/__init__.py +0 -0
  195. aethergraph/services/rag/facade.py +279 -23
  196. aethergraph/services/rag/index_factory.py +2 -2
  197. aethergraph/services/rag/node_rag.py +317 -0
  198. aethergraph/services/rate_limit/inmem_rate_limit.py +24 -0
  199. aethergraph/services/registry/__init__.py +0 -0
  200. aethergraph/services/registry/agent_app_meta.py +419 -0
  201. aethergraph/services/registry/registry_key.py +1 -1
  202. aethergraph/services/registry/unified_registry.py +74 -6
  203. aethergraph/services/scope/scope.py +159 -0
  204. aethergraph/services/scope/scope_factory.py +164 -0
  205. aethergraph/services/state_stores/serialize.py +5 -0
  206. aethergraph/services/state_stores/utils.py +2 -1
  207. aethergraph/services/viz/__init__.py +0 -0
  208. aethergraph/services/viz/facade.py +413 -0
  209. aethergraph/services/viz/viz_service.py +69 -0
  210. aethergraph/storage/artifacts/artifact_index_jsonl.py +180 -0
  211. aethergraph/storage/artifacts/artifact_index_sqlite.py +426 -0
  212. aethergraph/storage/artifacts/cas_store.py +422 -0
  213. aethergraph/storage/artifacts/fs_cas.py +18 -0
  214. aethergraph/storage/artifacts/s3_cas.py +14 -0
  215. aethergraph/storage/artifacts/utils.py +124 -0
  216. aethergraph/storage/blob/fs_blob.py +86 -0
  217. aethergraph/storage/blob/s3_blob.py +115 -0
  218. aethergraph/storage/continuation_store/fs_cont.py +283 -0
  219. aethergraph/storage/continuation_store/inmem_cont.py +146 -0
  220. aethergraph/storage/continuation_store/kvdoc_cont.py +261 -0
  221. aethergraph/storage/docstore/fs_doc.py +63 -0
  222. aethergraph/storage/docstore/sqlite_doc.py +31 -0
  223. aethergraph/storage/docstore/sqlite_doc_sync.py +90 -0
  224. aethergraph/storage/eventlog/fs_event.py +136 -0
  225. aethergraph/storage/eventlog/sqlite_event.py +47 -0
  226. aethergraph/storage/eventlog/sqlite_event_sync.py +178 -0
  227. aethergraph/storage/factory.py +432 -0
  228. aethergraph/storage/fs_utils.py +28 -0
  229. aethergraph/storage/graph_state_store/state_store.py +64 -0
  230. aethergraph/storage/kv/inmem_kv.py +103 -0
  231. aethergraph/storage/kv/layered_kv.py +52 -0
  232. aethergraph/storage/kv/sqlite_kv.py +39 -0
  233. aethergraph/storage/kv/sqlite_kv_sync.py +98 -0
  234. aethergraph/storage/memory/event_persist.py +68 -0
  235. aethergraph/storage/memory/fs_persist.py +118 -0
  236. aethergraph/{services/memory/hotlog_kv.py → storage/memory/hotlog.py} +8 -2
  237. aethergraph/{services → storage}/memory/indices.py +31 -7
  238. aethergraph/storage/metering/meter_event.py +55 -0
  239. aethergraph/storage/runs/doc_store.py +280 -0
  240. aethergraph/storage/runs/inmen_store.py +82 -0
  241. aethergraph/storage/runs/sqlite_run_store.py +403 -0
  242. aethergraph/storage/sessions/doc_store.py +183 -0
  243. aethergraph/storage/sessions/inmem_store.py +110 -0
  244. aethergraph/storage/sessions/sqlite_session_store.py +399 -0
  245. aethergraph/storage/vector_index/chroma_index.py +138 -0
  246. aethergraph/storage/vector_index/faiss_index.py +179 -0
  247. aethergraph/storage/vector_index/sqlite_index.py +187 -0
  248. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/METADATA +138 -31
  249. aethergraph-0.1.0a3.dist-info/RECORD +356 -0
  250. aethergraph-0.1.0a3.dist-info/entry_points.txt +3 -0
  251. aethergraph/services/artifacts/factory.py +0 -35
  252. aethergraph/services/artifacts/fs_store.py +0 -656
  253. aethergraph/services/artifacts/jsonl_index.py +0 -123
  254. aethergraph/services/artifacts/sqlite_index.py +0 -209
  255. aethergraph/services/memory/distillers/episode.py +0 -116
  256. aethergraph/services/memory/distillers/rolling.py +0 -74
  257. aethergraph/services/memory/facade.py +0 -633
  258. aethergraph/services/memory/persist_fs.py +0 -40
  259. aethergraph/services/rag/index/base.py +0 -27
  260. aethergraph/services/rag/index/faiss_index.py +0 -121
  261. aethergraph/services/rag/index/sqlite_index.py +0 -134
  262. aethergraph-0.1.0a1.dist-info/RECORD +0 -182
  263. aethergraph-0.1.0a1.dist-info/entry_points.txt +0 -2
  264. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/WHEEL +0 -0
  265. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/licenses/LICENSE +0 -0
  266. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/licenses/NOTICE +0 -0
  267. {aethergraph-0.1.0a1.dist-info → aethergraph-0.1.0a3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,419 @@
1
+ # aethergraph/runtime/agent_app_meta.py
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Callable
5
+ import inspect
6
+ from typing import Any, Literal, TypedDict
7
+
8
+ from aethergraph.core.runtime.run_types import RunImportance, RunVisibility
9
+
10
+ # ---------------------------------------------------------------------
11
+ # Config schemas used by decorators
12
+ # ---------------------------------------------------------------------
13
+ SUPPORTED_AGENT_MODES = {"chat_v1"}
14
+
15
+
16
+ class AgentConfig(TypedDict, total=False):
17
+ """
18
+ Configuration metadata for an agent. Register an agent with `as_agent`
19
+ parameter in `@graphify` or `@graph_fn`.
20
+
21
+ All fields are optional except `id` in practice; anything omitted gets
22
+ reasonable defaults in build_agent_meta.
23
+
24
+ Attributes:
25
+ id (str): Unique identifier for the agent. Defaults to graph name.
26
+ title (str): Display name of the agent. Optional, shown in the UI.
27
+ description (str): Brief description of the agent. Optional, shown in the UI.
28
+ short_description (str): Shorter summary (used in cards). Optional.
29
+ icon_key (str): Icon key used in the UI (e.g. "message-circle").
30
+ color (str): Accent color token (e.g. "emerald").
31
+ badge (str): Badge label, e.g. "Chat Agent".
32
+ category (str): Category, e.g. "Core", "R&D Lab", "Infra", "Productivity".
33
+ status (str): "available" | "coming-soon" | "hidden" | "error" | ...
34
+ mode (str): Operational mode. Defaults to "chat_v1" for chat agents.
35
+ session_kind (str): Session type, e.g. "chat". Defaults to "chat".
36
+ flow_id (str): Flow identifier for wiring. Defaults to graph name.
37
+ tags (list[str]): Tags used for search / grouping.
38
+ tool_graphs (list[str]): Related tool graph identifiers.
39
+ features (list[str]): Optional feature bullets for UI.
40
+ run_visibility (RunVisibility): "normal" | "inline" | ...
41
+ run_importance (RunImportance): "normal" | "high" | ...
42
+ memory_level (Literal["user","session","run"]): Memory scope level.
43
+ memory_scope (str): Logical scope, e.g. "session.global", "user.all".
44
+ github_url (str): Optional GitHub link.
45
+ """
46
+
47
+ # Identity & basic UI
48
+ id: str
49
+ title: str
50
+ description: str
51
+ short_description: str
52
+ icon_key: str
53
+ color: str
54
+ badge: str
55
+ category: str
56
+ status: str # "available" | "coming-soon" | "hidden" | ...
57
+
58
+ # Behavior & wiring
59
+ mode: str # "chat_v1", etc.
60
+ session_kind: str # "chat", "batch", ...
61
+
62
+ flow_id: str
63
+ tags: list[str]
64
+ tool_graphs: list[str]
65
+ features: list[str]
66
+
67
+ # Runtime behavior
68
+ run_visibility: RunVisibility
69
+ run_importance: RunImportance
70
+
71
+ # Memory policy
72
+ memory_level: Literal["user", "session", "run"]
73
+ memory_scope: str
74
+
75
+ # Optional metadata
76
+ github_url: str
77
+
78
+
79
+ class AppConfig(TypedDict, total=False):
80
+ """
81
+ Configuration metadata for an application. Register an app with `as_app`
82
+ parameter in `@graphify` or `@graph_fn`.
83
+
84
+ Attributes:
85
+ id (str): Unique identifier for the app. Defaults to graph name.
86
+ name (str): Human-readable name of the app. Defaults to "App for <graph>".
87
+ badge (str): Short badge or label for the app. Optional, shown in the UI.
88
+ short_description (str): Brief summary of the app's purpose. Optional.
89
+ description (str): Detailed description of the app. Optional.
90
+ category (str): Category, e.g. "Core", "R&D Lab", "Infra", "Productivity".
91
+ status (str): "available" | "coming-soon" | "hidden" | "error" | ...
92
+ icon_key (str): Icon key for the app.
93
+ color (str): Accent color token.
94
+ mode (str): App mode, e.g. "no_input_v1". Defaults to "no_input_v1".
95
+ tags (list[str]): Tags for search / grouping.
96
+ features (list[str]): Notable features for the app.
97
+ run_visibility (RunVisibility): "normal" | "inline" | ...
98
+ run_importance (RunImportance): "normal" | "high" | ...
99
+ flow_id (str): Flow identifier. Defaults to graph name.
100
+ github_url (str): Optional GitHub link.
101
+ """
102
+
103
+ # Identity & UI
104
+ id: str
105
+ name: str
106
+ badge: str
107
+ short_description: str
108
+ description: str
109
+ category: str
110
+ status: str
111
+ icon_key: str
112
+ color: str
113
+ mode: str
114
+ tags: list[str]
115
+
116
+ # UX hints
117
+ features: list[str]
118
+
119
+ # Runtime behavior
120
+ run_visibility: RunVisibility
121
+ run_importance: RunImportance
122
+ flow_id: str
123
+
124
+ # Optional metadata
125
+ github_url: str
126
+
127
+
128
+ AGENT_CORE_KEYS = {
129
+ "id",
130
+ "title",
131
+ "description",
132
+ "short_description",
133
+ "icon_key",
134
+ "color",
135
+ "badge",
136
+ "category",
137
+ "status",
138
+ "mode",
139
+ "session_kind",
140
+ "flow_id",
141
+ "tags",
142
+ "tool_graphs",
143
+ "features",
144
+ "run_visibility",
145
+ "run_importance",
146
+ "memory_level",
147
+ "memory_scope",
148
+ "github_url",
149
+ }
150
+
151
+ APP_CORE_KEYS = {
152
+ "id",
153
+ "name",
154
+ "badge",
155
+ "short_description",
156
+ "description",
157
+ "category",
158
+ "status",
159
+ "icon_key",
160
+ "color",
161
+ "mode",
162
+ "tags",
163
+ "features",
164
+ "run_visibility",
165
+ "run_importance",
166
+ "flow_id",
167
+ "github_url",
168
+ }
169
+
170
+ # ---------------------------------------------------------------------
171
+ # Shared constants / validators
172
+ # ---------------------------------------------------------------------
173
+
174
+ CHAT_V1_REQUIRED_INPUTS = [
175
+ "message",
176
+ "files",
177
+ "context_refs",
178
+ "session_id",
179
+ "user_meta",
180
+ ]
181
+
182
+
183
+ def normalize_agent_mode(agent_cfg: AgentConfig) -> str:
184
+ # Default behavior: if user doesn't specify, it's chat_v1
185
+ mode = (agent_cfg.mode or "chat_v1").strip()
186
+
187
+ if mode not in SUPPORTED_AGENT_MODES:
188
+ # this will be caught and turned into status="error" for now
189
+ raise ValueError(
190
+ f"Unsupported agent mode '{mode}'. "
191
+ "Currently only 'chat_v1' is supported. "
192
+ "Omit 'mode' or set mode='chat_v1'."
193
+ )
194
+ return mode
195
+
196
+
197
+ SUPPORTED_APP_MODES = {"no_input_v1"}
198
+
199
+
200
+ def normalize_app_mode(app_cfg: AppConfig) -> str:
201
+ mode = (app_cfg.mode or "no_input_v1").strip()
202
+ if mode not in SUPPORTED_APP_MODES:
203
+ raise ValueError(
204
+ f"Unsupported app mode '{mode}'. "
205
+ "Currently only 'no_input_v1' is supported for gallery apps. "
206
+ "Omit 'mode' or set mode='no_input_v1'."
207
+ )
208
+ return mode
209
+
210
+
211
+ def validate_agent_signature(
212
+ graph_name: str, fn: Callable, agent_cfg: AgentConfig
213
+ ) -> tuple[str, list[str]]:
214
+ mode = normalize_agent_mode(agent_cfg)
215
+
216
+ sig = inspect.signature(fn)
217
+ param_names = list(sig.parameters.keys())
218
+
219
+ if mode == "chat_v1":
220
+ missing = [p for p in CHAT_V1_REQUIRED_INPUTS if p not in param_names]
221
+ if missing:
222
+ raise ValueError(
223
+ f"chat_v1 agent '{graph_name}' is missing parameters: {missing}. "
224
+ f"Expected parameters: {CHAT_V1_REQUIRED_INPUTS}"
225
+ )
226
+ # TODO future: could be more flexible here:
227
+ # - use CHAT_V1_REQUIRED_INPUTS as canonical inputs, or
228
+ # - accept superset but keep these first.
229
+ inputs = CHAT_V1_REQUIRED_INPUTS
230
+ else:
231
+ # Currently unreachable because normalize_agent_mode rejects unknowns,
232
+ # but future-proof if add more modes.
233
+ inputs = param_names
234
+
235
+ return mode, inputs
236
+
237
+
238
+ # ---------------------------------------------------------------------
239
+ # Normalization helpers used by decorators & other runtime code
240
+ # ---------------------------------------------------------------------
241
+
242
+
243
+ def build_agent_meta(
244
+ *,
245
+ graph_name: str,
246
+ version: str,
247
+ graph_meta: dict[str, Any],
248
+ agent_cfg: AgentConfig | None,
249
+ ) -> dict[str, Any] | None:
250
+ """
251
+ Normalize AgentConfig + graph metadata into a registry-ready meta dict.
252
+
253
+ Returns None if agent_cfg is None.
254
+ """
255
+ if agent_cfg is None:
256
+ return None
257
+
258
+ cfg: dict[str, Any] = dict(agent_cfg)
259
+ base_tags = graph_meta.get("tags") or []
260
+
261
+ agent_id = cfg.get("id", graph_name)
262
+ agent_title = cfg.get("title", f"Agent for {graph_name}")
263
+ agent_flow_id = cfg.get("flow_id", graph_meta.get("flow_id", graph_name))
264
+ agent_tags = cfg.get("tags", base_tags)
265
+
266
+ # Anything not in core keys becomes "extra" for future use
267
+ extra = {k: v for k, v in cfg.items() if k not in AGENT_CORE_KEYS}
268
+
269
+ # Memory policy
270
+ memory_level = cfg.get("memory_level", "session")
271
+ memory_scope = cfg.get("memory_scope")
272
+
273
+ # Text fields
274
+ description = cfg.get("description")
275
+ short_description = cfg.get("short_description") or description
276
+
277
+ # Visuals
278
+ icon_key = cfg.get("icon_key")
279
+ accent_color = cfg.get("color")
280
+
281
+ # Behavior
282
+ agent_mode = cfg.get("mode") or "chat_v1"
283
+ session_kind = cfg.get("session_kind", "chat")
284
+
285
+ meta: dict[str, Any] = {
286
+ "kind": "agent",
287
+ "id": agent_id,
288
+ "title": agent_title,
289
+ "description": description,
290
+ "short_description": short_description,
291
+ "icon_key": icon_key,
292
+ "color": accent_color,
293
+ "badge": cfg.get("badge"),
294
+ "category": cfg.get("category"),
295
+ "status": cfg.get("status", "available"),
296
+ "mode": agent_mode,
297
+ "session_kind": session_kind,
298
+ "flow_id": agent_flow_id,
299
+ "tags": agent_tags,
300
+ "tool_graphs": cfg.get("tool_graphs", []),
301
+ "features": cfg.get("features", []),
302
+ "run_visibility": cfg.get("run_visibility", "inline"),
303
+ "run_importance": cfg.get("run_importance", "normal"),
304
+ "memory": {
305
+ "level": memory_level,
306
+ "scope": memory_scope,
307
+ },
308
+ "github_url": cfg.get("github_url"),
309
+ "backing": {
310
+ "type": "graphfn",
311
+ "name": graph_name,
312
+ "version": version,
313
+ },
314
+ "extra": extra,
315
+ }
316
+
317
+ # unified gallery view
318
+ meta["gallery"] = {
319
+ "kind": "agent",
320
+ "id": agent_id,
321
+ "title": agent_title,
322
+ "subtitle": session_kind or agent_mode,
323
+ "badge": cfg.get("badge"),
324
+ "category": cfg.get("category"),
325
+ "status": meta["status"],
326
+ "short_description": short_description,
327
+ "description": description,
328
+ "icon_key": icon_key,
329
+ "accent_color": accent_color,
330
+ "tags": agent_tags,
331
+ "github_url": cfg.get("github_url"),
332
+ "flow_id": agent_flow_id,
333
+ "backing": meta["backing"],
334
+ "extra": extra,
335
+ }
336
+
337
+ return meta
338
+
339
+
340
+ def build_app_meta(
341
+ *,
342
+ graph_name: str,
343
+ version: str,
344
+ graph_meta: dict[str, Any],
345
+ app_cfg: AppConfig | None,
346
+ ) -> dict[str, Any] | None:
347
+ """
348
+ Normalize AppConfig + graph metadata into a registry-ready meta dict.
349
+
350
+ Returns None if app_cfg is None.
351
+ """
352
+ if app_cfg is None:
353
+ return None
354
+
355
+ cfg: dict[str, Any] = dict(app_cfg)
356
+ base_tags = graph_meta.get("tags") or []
357
+
358
+ app_id = cfg.get("id", graph_name)
359
+ app_flow_id = cfg.get("flow_id", graph_meta.get("flow_id", graph_name))
360
+ app_name = cfg.get("name", f"App for {graph_name}")
361
+ app_tags = cfg.get("tags", base_tags)
362
+
363
+ extra = {k: v for k, v in cfg.items() if k not in APP_CORE_KEYS}
364
+
365
+ short_description = cfg.get("short_description") or cfg.get("description")
366
+ description = cfg.get("description")
367
+
368
+ icon_key = cfg.get("icon_key")
369
+ accent_color = cfg.get("color")
370
+ app_mode = cfg.get("mode") or "no_input_v1"
371
+
372
+ meta: dict[str, Any] = {
373
+ "kind": "app",
374
+ "id": app_id,
375
+ "name": app_name,
376
+ "graph_id": graph_name,
377
+ "flow_id": app_flow_id,
378
+ "badge": cfg.get("badge"),
379
+ "category": cfg.get("category"),
380
+ "short_description": short_description,
381
+ "description": description,
382
+ "status": cfg.get("status", "available"),
383
+ "icon_key": icon_key,
384
+ "color": accent_color,
385
+ "mode": app_mode,
386
+ "tags": app_tags,
387
+ "features": cfg.get("features", []),
388
+ "run_visibility": cfg.get("run_visibility", "normal"),
389
+ "run_importance": cfg.get("run_importance", "normal"),
390
+ "github_url": cfg.get("github_url"),
391
+ "backing": {
392
+ "type": "graphfn",
393
+ "name": graph_name,
394
+ "version": version,
395
+ },
396
+ "extra": extra,
397
+ }
398
+
399
+ # unified gallery view
400
+ meta["gallery"] = {
401
+ "kind": "app",
402
+ "id": app_id,
403
+ "title": app_name,
404
+ "subtitle": cfg.get("category"),
405
+ "badge": cfg.get("badge"),
406
+ "category": cfg.get("category"),
407
+ "status": meta["status"],
408
+ "short_description": short_description,
409
+ "description": description,
410
+ "icon_key": icon_key,
411
+ "accent_color": accent_color,
412
+ "tags": app_tags,
413
+ "github_url": cfg.get("github_url"),
414
+ "flow_id": app_flow_id,
415
+ "backing": meta["backing"],
416
+ "extra": extra,
417
+ }
418
+
419
+ return meta
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  import re
3
3
 
4
- NS = {"tool", "graph", "graphfn", "agent"}
4
+ NS = {"tool", "graph", "graphfn", "agent", "app"}
5
5
 
6
6
  # Simple ref regex to detect optional leading 'registry:'
7
7
  _REG_PREFIX = re.compile(r"^registry:(.+)$", re.I)
@@ -37,9 +37,20 @@ class UnifiedRegistry:
37
37
  self._lock = threading.RLock()
38
38
  self._allow_overwrite = allow_overwrite
39
39
 
40
+ # per-version metadata
41
+ self._meta: dict[tuple[str, str, str], dict[str, Any]] = {}
42
+
40
43
  # ---------- registration ----------
41
44
 
42
- def register(self, *, nspace: str, name: str, version: str, obj: RegistryValue) -> None:
45
+ def register(
46
+ self,
47
+ *,
48
+ nspace: str,
49
+ name: str,
50
+ version: str,
51
+ obj: RegistryValue,
52
+ meta: dict[str, Any] | None = None,
53
+ ) -> None:
43
54
  if nspace not in NS:
44
55
  raise ValueError(f"Unknown namespace: {nspace}")
45
56
  key = (nspace, name)
@@ -52,6 +63,10 @@ class UnifiedRegistry:
52
63
  versions[version] = obj
53
64
  self._latest[key] = self._pick_latest(versions.keys())
54
65
 
66
+ # Store metadata
67
+ if meta is not None:
68
+ self._meta[(nspace, name, version)] = meta
69
+
55
70
  def register_latest(
56
71
  self, *, nspace: str, name: str, obj: RegistryValue, version: str = "0.0.0"
57
72
  ) -> None:
@@ -85,11 +100,13 @@ class UnifiedRegistry:
85
100
  raise KeyError(f"Version not found: {key.nspace}:{key.name}@{ver}")
86
101
 
87
102
  val = versions[ver]
88
- # Materialize factories lazily (and cache)
89
- if callable(val):
90
- obj = val()
91
- versions[ver] = obj
92
- return obj
103
+
104
+ ## Materialize if factory -> we handle it when executing the graphs. Here it can cause
105
+ # the graph_fn returns a coroutine inside the GraphFunction object, not the expected function.
106
+ # if callable(val):
107
+ # obj = val()
108
+ # versions[ver] = obj
109
+ # return obj
93
110
  return val
94
111
 
95
112
  # ---------- listing / admin ----------
@@ -123,6 +140,10 @@ class UnifiedRegistry:
123
140
  self._store.pop(k, None)
124
141
  self._latest.pop(k, None)
125
142
  self._aliases.pop(k, None)
143
+ # NEW: drop all meta for this (ns,name)
144
+ for key in list(self._meta.keys()):
145
+ if key[0] == nspace and key[1] == name:
146
+ self._meta.pop(key, None)
126
147
  return
127
148
  vers = self._store[k]
128
149
  vers.pop(version, None)
@@ -131,6 +152,8 @@ class UnifiedRegistry:
131
152
  for tag, v in list(self._aliases[k].items()):
132
153
  if v == version:
133
154
  self._aliases[k].pop(tag, None)
155
+ # drop meta for this version
156
+ self._meta.pop((nspace, name, version), None)
134
157
  # recompute latest
135
158
  if vers:
136
159
  self._latest[k] = self._pick_latest(vers.keys())
@@ -144,6 +167,7 @@ class UnifiedRegistry:
144
167
  self._store.clear()
145
168
  self._latest.clear()
146
169
  self._aliases.clear()
170
+ self._meta.clear()
147
171
 
148
172
  # ---------- typed getters ----------
149
173
 
@@ -159,6 +183,50 @@ class UnifiedRegistry:
159
183
  def get_agent(self, name: str, version: str | None = None) -> Any:
160
184
  return self.get(Key(nspace="agent", name=name, version=version))
161
185
 
186
+ def get_meta(
187
+ self,
188
+ nspace: str,
189
+ name: str,
190
+ version: str | None = None,
191
+ ) -> dict[str, Any] | None:
192
+ """
193
+ Return metadata for a given registered object, or None if not set.
194
+ Follows the same version resolution as `get()`: explicit → alias → latest.
195
+ """
196
+ if nspace not in NS:
197
+ raise ValueError(f"Unknown namespace: {nspace}")
198
+ key = (nspace, name)
199
+ with self._lock:
200
+ versions = self._store.get(key)
201
+ if not versions:
202
+ return None
203
+
204
+ ver = version
205
+ # resolve aliases or default to latest
206
+ ver = self._aliases.get(key, {}).get(ver, ver) if ver else self._latest.get(key)
207
+ if ver is None:
208
+ return None
209
+
210
+ return self._meta.get((nspace, name, ver))
211
+
212
+ # ---------- list typed ----------
213
+ def list_tools(self) -> dict[str, str]:
214
+ return self.list(nspace="tool")
215
+
216
+ def list_graphs(self) -> dict[str, str]:
217
+ return self.list(nspace="graph")
218
+
219
+ def list_graphfns(self) -> dict[str, str]:
220
+ return self.list(nspace="graphfn")
221
+
222
+ def list_agents(self) -> dict[str, str]:
223
+ # Return {'agent:<id>': '<latest_version>'}
224
+ return self.list(nspace="agent")
225
+
226
+ def list_apps(self) -> dict[str, str]:
227
+ # Return {'app:<id>': '<latest_version>'}
228
+ return self.list(nspace="app")
229
+
162
230
  # ---------- helpers ----------
163
231
 
164
232
  @staticmethod