zrb 1.8.10__py3-none-any.whl → 1.21.29__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.

Potentially problematic release.


This version of zrb might be problematic. Click here for more details.

Files changed (147) hide show
  1. zrb/__init__.py +126 -113
  2. zrb/__main__.py +1 -1
  3. zrb/attr/type.py +10 -7
  4. zrb/builtin/__init__.py +2 -50
  5. zrb/builtin/git.py +12 -1
  6. zrb/builtin/group.py +31 -15
  7. zrb/builtin/http.py +7 -8
  8. zrb/builtin/llm/attachment.py +40 -0
  9. zrb/builtin/llm/chat_completion.py +274 -0
  10. zrb/builtin/llm/chat_session.py +152 -85
  11. zrb/builtin/llm/chat_session_cmd.py +288 -0
  12. zrb/builtin/llm/chat_trigger.py +79 -0
  13. zrb/builtin/llm/history.py +7 -9
  14. zrb/builtin/llm/llm_ask.py +221 -98
  15. zrb/builtin/llm/tool/api.py +74 -52
  16. zrb/builtin/llm/tool/cli.py +46 -17
  17. zrb/builtin/llm/tool/code.py +71 -90
  18. zrb/builtin/llm/tool/file.py +301 -241
  19. zrb/builtin/llm/tool/note.py +84 -0
  20. zrb/builtin/llm/tool/rag.py +38 -8
  21. zrb/builtin/llm/tool/sub_agent.py +67 -50
  22. zrb/builtin/llm/tool/web.py +146 -122
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
  25. zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
  26. zrb/builtin/searxng/config/settings.yml +5671 -0
  27. zrb/builtin/searxng/start.py +21 -0
  28. zrb/builtin/setup/latex/ubuntu.py +1 -0
  29. zrb/builtin/setup/ubuntu.py +1 -1
  30. zrb/builtin/shell/autocomplete/bash.py +4 -3
  31. zrb/builtin/shell/autocomplete/zsh.py +4 -3
  32. zrb/builtin/todo.py +13 -2
  33. zrb/config/config.py +614 -0
  34. zrb/config/default_prompt/file_extractor_system_prompt.md +112 -0
  35. zrb/config/default_prompt/interactive_system_prompt.md +29 -0
  36. zrb/config/default_prompt/persona.md +1 -0
  37. zrb/config/default_prompt/repo_extractor_system_prompt.md +112 -0
  38. zrb/config/default_prompt/repo_summarizer_system_prompt.md +29 -0
  39. zrb/config/default_prompt/summarization_prompt.md +57 -0
  40. zrb/config/default_prompt/system_prompt.md +38 -0
  41. zrb/config/llm_config.py +339 -0
  42. zrb/config/llm_context/config.py +166 -0
  43. zrb/config/llm_context/config_parser.py +40 -0
  44. zrb/config/llm_context/workflow.py +81 -0
  45. zrb/config/llm_rate_limitter.py +190 -0
  46. zrb/{runner → config}/web_auth_config.py +17 -22
  47. zrb/context/any_shared_context.py +17 -1
  48. zrb/context/context.py +16 -2
  49. zrb/context/shared_context.py +18 -8
  50. zrb/group/any_group.py +12 -5
  51. zrb/group/group.py +67 -3
  52. zrb/input/any_input.py +5 -1
  53. zrb/input/base_input.py +18 -6
  54. zrb/input/option_input.py +13 -1
  55. zrb/input/text_input.py +8 -25
  56. zrb/runner/cli.py +25 -23
  57. zrb/runner/common_util.py +24 -19
  58. zrb/runner/web_app.py +3 -3
  59. zrb/runner/web_route/docs_route.py +1 -1
  60. zrb/runner/web_route/error_page/serve_default_404.py +1 -1
  61. zrb/runner/web_route/error_page/show_error_page.py +1 -1
  62. zrb/runner/web_route/home_page/home_page_route.py +2 -2
  63. zrb/runner/web_route/login_api_route.py +1 -1
  64. zrb/runner/web_route/login_page/login_page_route.py +2 -2
  65. zrb/runner/web_route/logout_api_route.py +1 -1
  66. zrb/runner/web_route/logout_page/logout_page_route.py +2 -2
  67. zrb/runner/web_route/node_page/group/show_group_page.py +1 -1
  68. zrb/runner/web_route/node_page/node_page_route.py +1 -1
  69. zrb/runner/web_route/node_page/task/show_task_page.py +1 -1
  70. zrb/runner/web_route/refresh_token_api_route.py +1 -1
  71. zrb/runner/web_route/static/static_route.py +1 -1
  72. zrb/runner/web_route/task_input_api_route.py +6 -6
  73. zrb/runner/web_route/task_session_api_route.py +20 -12
  74. zrb/runner/web_util/cookie.py +1 -1
  75. zrb/runner/web_util/token.py +1 -1
  76. zrb/runner/web_util/user.py +8 -4
  77. zrb/session/any_session.py +24 -17
  78. zrb/session/session.py +50 -25
  79. zrb/session_state_logger/any_session_state_logger.py +9 -4
  80. zrb/session_state_logger/file_session_state_logger.py +16 -6
  81. zrb/session_state_logger/session_state_logger_factory.py +1 -1
  82. zrb/task/any_task.py +30 -9
  83. zrb/task/base/context.py +17 -9
  84. zrb/task/base/execution.py +15 -8
  85. zrb/task/base/lifecycle.py +8 -4
  86. zrb/task/base/monitoring.py +12 -7
  87. zrb/task/base_task.py +69 -5
  88. zrb/task/base_trigger.py +12 -5
  89. zrb/task/cmd_task.py +1 -1
  90. zrb/task/llm/agent.py +154 -161
  91. zrb/task/llm/agent_runner.py +152 -0
  92. zrb/task/llm/config.py +47 -18
  93. zrb/task/llm/conversation_history.py +209 -0
  94. zrb/task/llm/conversation_history_model.py +67 -0
  95. zrb/task/llm/default_workflow/coding/workflow.md +41 -0
  96. zrb/task/llm/default_workflow/copywriting/workflow.md +68 -0
  97. zrb/task/llm/default_workflow/git/workflow.md +118 -0
  98. zrb/task/llm/default_workflow/golang/workflow.md +128 -0
  99. zrb/task/llm/default_workflow/html-css/workflow.md +135 -0
  100. zrb/task/llm/default_workflow/java/workflow.md +146 -0
  101. zrb/task/llm/default_workflow/javascript/workflow.md +158 -0
  102. zrb/task/llm/default_workflow/python/workflow.md +160 -0
  103. zrb/task/llm/default_workflow/researching/workflow.md +153 -0
  104. zrb/task/llm/default_workflow/rust/workflow.md +162 -0
  105. zrb/task/llm/default_workflow/shell/workflow.md +299 -0
  106. zrb/task/llm/error.py +24 -10
  107. zrb/task/llm/file_replacement.py +206 -0
  108. zrb/task/llm/file_tool_model.py +57 -0
  109. zrb/task/llm/history_processor.py +206 -0
  110. zrb/task/llm/history_summarization.py +11 -166
  111. zrb/task/llm/print_node.py +193 -69
  112. zrb/task/llm/prompt.py +242 -45
  113. zrb/task/llm/subagent_conversation_history.py +41 -0
  114. zrb/task/llm/tool_wrapper.py +260 -57
  115. zrb/task/llm/workflow.py +76 -0
  116. zrb/task/llm_task.py +182 -171
  117. zrb/task/make_task.py +2 -3
  118. zrb/task/rsync_task.py +26 -11
  119. zrb/task/scheduler.py +4 -4
  120. zrb/util/attr.py +54 -39
  121. zrb/util/callable.py +23 -0
  122. zrb/util/cli/markdown.py +12 -0
  123. zrb/util/cli/text.py +30 -0
  124. zrb/util/file.py +29 -11
  125. zrb/util/git.py +8 -11
  126. zrb/util/git_diff_model.py +10 -0
  127. zrb/util/git_subtree.py +9 -14
  128. zrb/util/git_subtree_model.py +32 -0
  129. zrb/util/init_path.py +1 -1
  130. zrb/util/markdown.py +62 -0
  131. zrb/util/string/conversion.py +2 -2
  132. zrb/util/todo.py +17 -50
  133. zrb/util/todo_model.py +46 -0
  134. zrb/util/truncate.py +23 -0
  135. zrb/util/yaml.py +204 -0
  136. zrb/xcom/xcom.py +10 -0
  137. zrb-1.21.29.dist-info/METADATA +270 -0
  138. {zrb-1.8.10.dist-info → zrb-1.21.29.dist-info}/RECORD +140 -98
  139. {zrb-1.8.10.dist-info → zrb-1.21.29.dist-info}/WHEEL +1 -1
  140. zrb/config.py +0 -335
  141. zrb/llm_config.py +0 -411
  142. zrb/llm_rate_limitter.py +0 -125
  143. zrb/task/llm/context.py +0 -102
  144. zrb/task/llm/context_enrichment.py +0 -199
  145. zrb/task/llm/history.py +0 -211
  146. zrb-1.8.10.dist-info/METADATA +0 -264
  147. {zrb-1.8.10.dist-info → zrb-1.21.29.dist-info}/entry_points.txt +0 -0
zrb/task/llm/prompt.py CHANGED
@@ -1,10 +1,139 @@
1
- from zrb.attr.type import StrAttr
1
+ import os
2
+ import platform
3
+ import re
4
+ from datetime import datetime, timezone
5
+ from typing import TYPE_CHECKING, Callable
6
+
7
+ from zrb.attr.type import StrAttr, StrListAttr
8
+ from zrb.config.llm_config import llm_config
2
9
  from zrb.context.any_context import AnyContext
3
- from zrb.llm_config import llm_config as llm_config
4
- from zrb.util.attr import get_attr, get_str_attr
10
+ from zrb.task.llm.conversation_history_model import ConversationHistory
11
+ from zrb.task.llm.workflow import LLMWorkflow, get_available_workflows
12
+ from zrb.util.attr import get_attr, get_str_attr, get_str_list_attr
13
+ from zrb.util.file import read_dir, read_file_with_line_numbers
14
+ from zrb.util.markdown import make_markdown_section
15
+
16
+ if TYPE_CHECKING:
17
+ from pydantic_ai.messages import UserContent
18
+
19
+
20
+ def get_system_and_user_prompt(
21
+ ctx: AnyContext,
22
+ user_message: str,
23
+ persona_attr: StrAttr | None = None,
24
+ render_persona: bool = False,
25
+ system_prompt_attr: StrAttr | None = None,
26
+ render_system_prompt: bool = False,
27
+ special_instruction_prompt_attr: StrAttr | None = None,
28
+ render_special_instruction_prompt: bool = False,
29
+ workflows_attr: StrListAttr | None = None,
30
+ render_workflows: bool = False,
31
+ conversation_history: ConversationHistory | None = None,
32
+ ) -> tuple[str, str]:
33
+ if conversation_history is None:
34
+ conversation_history = ConversationHistory()
35
+ new_user_message_prompt, apendixes = _get_user_message_prompt(user_message)
36
+ new_system_prompt = _construct_system_prompt(
37
+ ctx=ctx,
38
+ user_message=user_message,
39
+ apendixes=apendixes,
40
+ persona_attr=persona_attr,
41
+ render_persona=render_persona,
42
+ system_prompt_attr=system_prompt_attr,
43
+ render_system_prompt=render_system_prompt,
44
+ special_instruction_prompt_attr=special_instruction_prompt_attr,
45
+ render_special_instruction_prompt=render_special_instruction_prompt,
46
+ workflows_attr=workflows_attr,
47
+ render_workflows=render_workflows,
48
+ conversation_history=conversation_history,
49
+ )
50
+ return new_system_prompt, new_user_message_prompt
51
+
52
+
53
+ def _construct_system_prompt(
54
+ ctx: AnyContext,
55
+ user_message: str,
56
+ apendixes: str,
57
+ persona_attr: StrAttr | None = None,
58
+ render_persona: bool = False,
59
+ system_prompt_attr: StrAttr | None = None,
60
+ render_system_prompt: bool = False,
61
+ special_instruction_prompt_attr: StrAttr | None = None,
62
+ render_special_instruction_prompt: bool = False,
63
+ workflows_attr: StrListAttr | None = None,
64
+ render_workflows: bool = False,
65
+ conversation_history: ConversationHistory | None = None,
66
+ ) -> str:
67
+ persona = _get_persona(ctx, persona_attr, render_persona)
68
+ base_system_prompt = _get_base_system_prompt(
69
+ ctx, system_prompt_attr, render_system_prompt
70
+ )
71
+ special_instruction_prompt = _get_special_instruction_prompt(
72
+ ctx, special_instruction_prompt_attr, render_special_instruction_prompt
73
+ )
74
+ available_workflows = get_available_workflows()
75
+ active_workflow_names = set(
76
+ _get_active_workflow_names(ctx, workflows_attr, render_workflows)
77
+ )
78
+ active_workflow_prompt = _get_workflow_prompt(
79
+ available_workflows, active_workflow_names, True
80
+ )
81
+ inactive_workflow_prompt = _get_workflow_prompt(
82
+ available_workflows, active_workflow_names, False
83
+ )
84
+ if conversation_history is None:
85
+ conversation_history = ConversationHistory()
86
+ current_directory = os.getcwd()
87
+ iso_date = datetime.now(timezone.utc).astimezone().isoformat()
88
+ return "\n".join(
89
+ [
90
+ persona,
91
+ base_system_prompt,
92
+ make_markdown_section(
93
+ "📝 SPECIAL INSTRUCTION",
94
+ "\n".join(
95
+ [
96
+ special_instruction_prompt,
97
+ active_workflow_prompt,
98
+ ]
99
+ ),
100
+ ),
101
+ make_markdown_section("🛠️ AVAILABLE WORKFLOWS", inactive_workflow_prompt),
102
+ make_markdown_section(
103
+ "📚 CONTEXT",
104
+ "\n".join(
105
+ [
106
+ make_markdown_section(
107
+ "ℹ️ System Information",
108
+ "\n".join(
109
+ [
110
+ f"- OS: {platform.system()} {platform.version()}",
111
+ f"- Python Version: {platform.python_version()}",
112
+ f"- Current Directory: {current_directory}",
113
+ f"- Current Time: {iso_date}",
114
+ ]
115
+ ),
116
+ ),
117
+ make_markdown_section(
118
+ "🧠 Long Term Note Content",
119
+ conversation_history.long_term_note,
120
+ ),
121
+ make_markdown_section(
122
+ "📝 Contextual Note Content",
123
+ conversation_history.contextual_note,
124
+ ),
125
+ make_markdown_section(
126
+ "📄 Apendixes",
127
+ apendixes,
128
+ ),
129
+ ]
130
+ ),
131
+ ),
132
+ ]
133
+ )
5
134
 
6
135
 
7
- def get_persona(
136
+ def _get_persona(
8
137
  ctx: AnyContext,
9
138
  persona_attr: StrAttr | None,
10
139
  render_persona: bool,
@@ -21,7 +150,7 @@ def get_persona(
21
150
  return llm_config.default_persona or ""
22
151
 
23
152
 
24
- def get_base_system_prompt(
153
+ def _get_base_system_prompt(
25
154
  ctx: AnyContext,
26
155
  system_prompt_attr: StrAttr | None,
27
156
  render_system_prompt: bool,
@@ -38,59 +167,126 @@ def get_base_system_prompt(
38
167
  return llm_config.default_system_prompt or ""
39
168
 
40
169
 
41
- def get_special_instruction_prompt(
170
+ def _get_special_instruction_prompt(
42
171
  ctx: AnyContext,
43
172
  special_instruction_prompt_attr: StrAttr | None,
44
- render_special_instruction_prompt: bool,
173
+ render_spcecial_instruction_prompt: bool,
45
174
  ) -> str:
46
175
  """Gets the special instruction prompt, prioritizing task-specific, then default."""
47
176
  special_instruction = get_attr(
48
177
  ctx,
49
178
  special_instruction_prompt_attr,
50
179
  None,
51
- auto_render=render_special_instruction_prompt,
180
+ auto_render=render_spcecial_instruction_prompt,
52
181
  )
53
182
  if special_instruction is not None:
54
183
  return special_instruction
55
184
  return llm_config.default_special_instruction_prompt
56
185
 
57
186
 
58
- def get_combined_system_prompt(
187
+ def _get_active_workflow_names(
59
188
  ctx: AnyContext,
60
- persona_attr: StrAttr | None,
61
- render_persona: bool,
62
- system_prompt_attr: StrAttr | None,
63
- render_system_prompt: bool,
64
- special_instruction_prompt_attr: StrAttr | None,
65
- render_special_instruction_prompt: bool,
189
+ workflows_attr: StrListAttr | None,
190
+ render_workflows: bool,
191
+ ) -> list[str]:
192
+ """Gets the workflows, prioritizing task-specific, then default."""
193
+ raw_workflows = get_str_list_attr(
194
+ ctx,
195
+ [] if workflows_attr is None else workflows_attr,
196
+ auto_render=render_workflows,
197
+ )
198
+ if raw_workflows is not None and len(raw_workflows) > 0:
199
+ return [w.strip().lower() for w in raw_workflows if w.strip() != ""]
200
+ return []
201
+
202
+
203
+ def _get_workflow_prompt(
204
+ available_workflows: dict[str, LLMWorkflow],
205
+ active_workflow_names: list[str] | set[str],
206
+ select_active_workflow: bool,
66
207
  ) -> str:
67
- """Combines persona, base system prompt, and special instructions."""
68
- persona = get_persona(ctx, persona_attr, render_persona)
69
- base_system_prompt = get_base_system_prompt(
70
- ctx, system_prompt_attr, render_system_prompt
208
+ selected_workflows = {
209
+ workflow_name: available_workflows[workflow_name]
210
+ for workflow_name in available_workflows
211
+ if (workflow_name in active_workflow_names) == select_active_workflow
212
+ }
213
+ return "\n".join(
214
+ [
215
+ make_markdown_section(
216
+ workflow_name.capitalize(),
217
+ (
218
+ (
219
+ "> Workflow status: Automatically Loaded/Activated.\n"
220
+ f"> Workflow location: `{workflow.path}`\n"
221
+ "{workflow.content}"
222
+ )
223
+ if select_active_workflow
224
+ else f"Workflow name: {workflow_name}\n{workflow.description}"
225
+ ),
226
+ )
227
+ for workflow_name, workflow in selected_workflows.items()
228
+ ]
71
229
  )
72
- special_instruction = get_special_instruction_prompt(
73
- ctx, special_instruction_prompt_attr, render_special_instruction_prompt
230
+
231
+
232
+ def _get_user_message_prompt(user_message: str) -> tuple[str, str]:
233
+ processed_user_message = user_message
234
+ # Match “@” + any non-space/comma sequence that contains at least one “/”
235
+ pattern = r"(?<!\w)@(?=[^,\s]*\/)([^,\?\!\s]+)"
236
+ potential_resource_path = re.findall(pattern, user_message)
237
+ apendix_list = []
238
+ for i, ref in enumerate(potential_resource_path):
239
+ resource_path = os.path.abspath(os.path.expanduser(ref))
240
+ content = ""
241
+ ref_type = ""
242
+ if os.path.isfile(resource_path):
243
+ content = read_file_with_line_numbers(resource_path)
244
+ ref_type = "file"
245
+ elif os.path.isdir(resource_path):
246
+ content = read_dir(resource_path)
247
+ ref_type = "directory"
248
+ if content != "":
249
+ # Replace the @-reference in the user message with the placeholder
250
+ placeholder = f"[Reference {i+1}: `{os.path.basename(ref)}`]"
251
+ processed_user_message = processed_user_message.replace(
252
+ f"@{ref}", placeholder, 1
253
+ )
254
+ apendix_list.append(
255
+ make_markdown_section(
256
+ f"Content of {placeholder} ({ref_type} path: `{resource_path}`)",
257
+ "\n".join(content) if isinstance(content, list) else content,
258
+ as_code=True,
259
+ )
260
+ )
261
+ apendixes = "\n".join(apendix_list)
262
+ current_directory = os.getcwd()
263
+ iso_date = datetime.now(timezone.utc).astimezone().isoformat()
264
+ modified_user_message = make_markdown_section(
265
+ "User Request",
266
+ "\n".join(
267
+ [
268
+ f"- Current Directory: {current_directory}",
269
+ f"- Current Time: {iso_date}",
270
+ "---",
271
+ processed_user_message,
272
+ ]
273
+ ),
74
274
  )
75
- parts = []
76
- if persona:
77
- parts.append(persona)
78
- if base_system_prompt:
79
- parts.append(base_system_prompt)
80
- if special_instruction:
81
- parts.append(special_instruction)
82
- return "\n\n".join(parts).strip()
275
+ return modified_user_message, apendixes
83
276
 
84
277
 
85
278
  def get_user_message(
86
279
  ctx: AnyContext,
87
280
  message_attr: StrAttr | None,
281
+ render_user_message: bool,
88
282
  ) -> str:
89
283
  """Gets the user message, rendering and providing a default."""
90
- return get_str_attr(ctx, message_attr, "How are you?", auto_render=True)
284
+ return get_str_attr(
285
+ ctx, message_attr, "How are you?", auto_render=render_user_message
286
+ )
91
287
 
92
288
 
93
- def get_summarization_prompt(
289
+ def get_summarization_system_prompt(
94
290
  ctx: AnyContext,
95
291
  summarization_prompt_attr: StrAttr | None,
96
292
  render_summarization_prompt: bool,
@@ -107,18 +303,19 @@ def get_summarization_prompt(
107
303
  return llm_config.default_summarization_prompt
108
304
 
109
305
 
110
- def get_context_enrichment_prompt(
306
+ def get_attachments(
111
307
  ctx: AnyContext,
112
- context_enrichment_prompt_attr: StrAttr | None,
113
- render_context_enrichment_prompt: bool,
114
- ) -> str:
115
- """Gets the context enrichment prompt, rendering if configured and handling defaults."""
116
- context_enrichment_prompt = get_attr(
117
- ctx,
118
- context_enrichment_prompt_attr,
119
- None,
120
- auto_render=render_context_enrichment_prompt,
121
- )
122
- if context_enrichment_prompt is not None:
123
- return context_enrichment_prompt
124
- return llm_config.default_context_enrichment_prompt
308
+ attachment: "UserContent | list[UserContent] | Callable[[AnyContext], UserContent | list[UserContent]] | None" = None, # noqa
309
+ ) -> "list[UserContent]":
310
+ if attachment is None:
311
+ return []
312
+ if callable(attachment):
313
+ result = attachment(ctx)
314
+ if result is None:
315
+ return []
316
+ if isinstance(result, list):
317
+ return result
318
+ return [result]
319
+ if isinstance(attachment, list):
320
+ return attachment
321
+ return [attachment]
@@ -0,0 +1,41 @@
1
+ from zrb.context.any_context import AnyContext
2
+ from zrb.task.llm.conversation_history_model import ConversationHistory
3
+ from zrb.task.llm.typing import ListOfDict
4
+ from zrb.xcom.xcom import Xcom
5
+
6
+
7
+ def inject_subagent_conversation_history_into_ctx(
8
+ ctx: AnyContext, conversation_history: ConversationHistory
9
+ ):
10
+ subagent_messages_xcom = _get_global_subagent_history_xcom(ctx)
11
+ existing_subagent_history = subagent_messages_xcom.get({})
12
+ subagent_messages_xcom.set(
13
+ {**existing_subagent_history, **conversation_history.subagent_history}
14
+ )
15
+
16
+
17
+ def extract_subagent_conversation_history_from_ctx(
18
+ ctx: AnyContext,
19
+ ) -> dict[str, ListOfDict]:
20
+ subagent_messsages_xcom = _get_global_subagent_history_xcom(ctx)
21
+ return subagent_messsages_xcom.get({})
22
+
23
+
24
+ def get_ctx_subagent_history(ctx: AnyContext, subagent_name: str) -> ListOfDict:
25
+ subagent_history = extract_subagent_conversation_history_from_ctx(ctx)
26
+ return subagent_history.get(subagent_name, [])
27
+
28
+
29
+ def set_ctx_subagent_history(ctx: AnyContext, subagent_name: str, messages: ListOfDict):
30
+ subagent_history = extract_subagent_conversation_history_from_ctx(ctx)
31
+ subagent_history[subagent_name] = messages
32
+ subagent_messages_xcom = _get_global_subagent_history_xcom(ctx)
33
+ subagent_messages_xcom.set(subagent_history)
34
+
35
+
36
+ def _get_global_subagent_history_xcom(ctx: AnyContext) -> Xcom:
37
+ if "_global_subagents" not in ctx.xcom:
38
+ ctx.xcom["_global_subagents"] = Xcom([{}])
39
+ if not isinstance(ctx.xcom["_global_subagents"], Xcom):
40
+ raise ValueError("ctx.xcom._global_subagents must be an Xcom")
41
+ return ctx.xcom["_global_subagents"]