zrb 1.13.1__py3-none-any.whl → 1.21.33__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 (117) hide show
  1. zrb/__init__.py +2 -6
  2. zrb/attr/type.py +10 -7
  3. zrb/builtin/__init__.py +2 -0
  4. zrb/builtin/git.py +12 -1
  5. zrb/builtin/group.py +31 -15
  6. zrb/builtin/http.py +7 -8
  7. zrb/builtin/llm/attachment.py +40 -0
  8. zrb/builtin/llm/chat_completion.py +287 -0
  9. zrb/builtin/llm/chat_session.py +130 -144
  10. zrb/builtin/llm/chat_session_cmd.py +288 -0
  11. zrb/builtin/llm/chat_trigger.py +78 -0
  12. zrb/builtin/llm/history.py +4 -4
  13. zrb/builtin/llm/llm_ask.py +218 -110
  14. zrb/builtin/llm/tool/api.py +74 -62
  15. zrb/builtin/llm/tool/cli.py +56 -21
  16. zrb/builtin/llm/tool/code.py +57 -47
  17. zrb/builtin/llm/tool/file.py +292 -255
  18. zrb/builtin/llm/tool/note.py +84 -0
  19. zrb/builtin/llm/tool/rag.py +25 -18
  20. zrb/builtin/llm/tool/search/__init__.py +1 -0
  21. zrb/builtin/llm/tool/search/brave.py +66 -0
  22. zrb/builtin/llm/tool/search/searxng.py +61 -0
  23. zrb/builtin/llm/tool/search/serpapi.py +61 -0
  24. zrb/builtin/llm/tool/sub_agent.py +53 -26
  25. zrb/builtin/llm/tool/web.py +94 -157
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
  28. zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
  29. zrb/builtin/searxng/config/settings.yml +5671 -0
  30. zrb/builtin/searxng/start.py +21 -0
  31. zrb/builtin/setup/latex/ubuntu.py +1 -0
  32. zrb/builtin/setup/ubuntu.py +1 -1
  33. zrb/builtin/shell/autocomplete/bash.py +4 -3
  34. zrb/builtin/shell/autocomplete/zsh.py +4 -3
  35. zrb/config/config.py +297 -79
  36. zrb/config/default_prompt/file_extractor_system_prompt.md +109 -9
  37. zrb/config/default_prompt/interactive_system_prompt.md +25 -28
  38. zrb/config/default_prompt/persona.md +1 -1
  39. zrb/config/default_prompt/repo_extractor_system_prompt.md +31 -31
  40. zrb/config/default_prompt/repo_summarizer_system_prompt.md +27 -8
  41. zrb/config/default_prompt/summarization_prompt.md +57 -16
  42. zrb/config/default_prompt/system_prompt.md +29 -25
  43. zrb/config/llm_config.py +129 -24
  44. zrb/config/llm_context/config.py +127 -90
  45. zrb/config/llm_context/config_parser.py +1 -7
  46. zrb/config/llm_context/workflow.py +81 -0
  47. zrb/config/llm_rate_limitter.py +100 -47
  48. zrb/context/any_shared_context.py +7 -1
  49. zrb/context/context.py +8 -2
  50. zrb/context/shared_context.py +6 -8
  51. zrb/group/any_group.py +12 -5
  52. zrb/group/group.py +67 -3
  53. zrb/input/any_input.py +5 -1
  54. zrb/input/base_input.py +18 -6
  55. zrb/input/option_input.py +13 -1
  56. zrb/input/text_input.py +7 -24
  57. zrb/runner/cli.py +21 -20
  58. zrb/runner/common_util.py +24 -19
  59. zrb/runner/web_route/task_input_api_route.py +5 -5
  60. zrb/runner/web_route/task_session_api_route.py +1 -4
  61. zrb/runner/web_util/user.py +7 -3
  62. zrb/session/any_session.py +12 -6
  63. zrb/session/session.py +39 -18
  64. zrb/task/any_task.py +24 -3
  65. zrb/task/base/context.py +17 -9
  66. zrb/task/base/execution.py +15 -8
  67. zrb/task/base/lifecycle.py +8 -4
  68. zrb/task/base/monitoring.py +12 -7
  69. zrb/task/base_task.py +69 -5
  70. zrb/task/base_trigger.py +12 -5
  71. zrb/task/llm/agent.py +130 -145
  72. zrb/task/llm/agent_runner.py +152 -0
  73. zrb/task/llm/config.py +45 -13
  74. zrb/task/llm/conversation_history.py +110 -29
  75. zrb/task/llm/conversation_history_model.py +4 -179
  76. zrb/task/llm/default_workflow/coding/workflow.md +41 -0
  77. zrb/task/llm/default_workflow/copywriting/workflow.md +68 -0
  78. zrb/task/llm/default_workflow/git/workflow.md +118 -0
  79. zrb/task/llm/default_workflow/golang/workflow.md +128 -0
  80. zrb/task/llm/default_workflow/html-css/workflow.md +135 -0
  81. zrb/task/llm/default_workflow/java/workflow.md +146 -0
  82. zrb/task/llm/default_workflow/javascript/workflow.md +158 -0
  83. zrb/task/llm/default_workflow/python/workflow.md +160 -0
  84. zrb/task/llm/default_workflow/researching/workflow.md +153 -0
  85. zrb/task/llm/default_workflow/rust/workflow.md +162 -0
  86. zrb/task/llm/default_workflow/shell/workflow.md +299 -0
  87. zrb/task/llm/file_replacement.py +206 -0
  88. zrb/task/llm/file_tool_model.py +57 -0
  89. zrb/task/llm/history_processor.py +206 -0
  90. zrb/task/llm/history_summarization.py +2 -192
  91. zrb/task/llm/print_node.py +192 -64
  92. zrb/task/llm/prompt.py +198 -153
  93. zrb/task/llm/subagent_conversation_history.py +41 -0
  94. zrb/task/llm/tool_confirmation_completer.py +41 -0
  95. zrb/task/llm/tool_wrapper.py +216 -55
  96. zrb/task/llm/workflow.py +76 -0
  97. zrb/task/llm_task.py +122 -70
  98. zrb/task/make_task.py +2 -3
  99. zrb/task/rsync_task.py +25 -10
  100. zrb/task/scheduler.py +4 -4
  101. zrb/util/attr.py +54 -39
  102. zrb/util/cli/markdown.py +12 -0
  103. zrb/util/cli/text.py +30 -0
  104. zrb/util/file.py +27 -11
  105. zrb/util/git.py +2 -2
  106. zrb/util/{llm/prompt.py → markdown.py} +2 -3
  107. zrb/util/string/conversion.py +1 -1
  108. zrb/util/truncate.py +23 -0
  109. zrb/util/yaml.py +204 -0
  110. zrb/xcom/xcom.py +10 -0
  111. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/METADATA +40 -20
  112. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/RECORD +114 -83
  113. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/WHEEL +1 -1
  114. zrb/task/llm/default_workflow/coding.md +0 -24
  115. zrb/task/llm/default_workflow/copywriting.md +0 -17
  116. zrb/task/llm/default_workflow/researching.md +0 -18
  117. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,84 @@
1
+ import os
2
+
3
+ from zrb.config.llm_context.config import llm_context_config
4
+
5
+
6
+ def read_long_term_note() -> str:
7
+ """
8
+ Retrieves the GLOBAL 🧠 Long Term Note shared across ALL sessions and projects.
9
+
10
+ Use this to recall user preferences, facts, and cross-project context.
11
+
12
+ Returns:
13
+ str: The current global note content.
14
+ """
15
+ contexts = llm_context_config.get_notes()
16
+ return contexts.get("/", "")
17
+
18
+
19
+ def write_long_term_note(content: str) -> str:
20
+ """
21
+ Persists CRITICAL facts to the GLOBAL 🧠 Long Term Note.
22
+
23
+ USE EAGERLY to save or update:
24
+ - User preferences (e.g., "I prefer Python", "No unit tests").
25
+ - User information (e.g., user name, user email address).
26
+ - Important facts (e.g., "My API key is in .env").
27
+ - Cross-project goals.
28
+ - Anything that will be useful for future interaction across projects.
29
+
30
+ WARNING: This OVERWRITES the entire Long Term Note.
31
+
32
+ Args:
33
+ content (str): The text to strictly memorize.
34
+
35
+ Returns:
36
+ str: Confirmation message.
37
+ """
38
+ llm_context_config.write_note(content, "/")
39
+ return "Global long-term note saved."
40
+
41
+
42
+ def read_contextual_note(path: str | None = None) -> str:
43
+ """
44
+ Retrieves LOCAL 📝 Contextual Note specific to a directory path.
45
+
46
+ Use to recall project-specific architecture, code summaries, or past decisions
47
+ relevant to the current working location.
48
+
49
+ Args:
50
+ path (str | None): Target file/dir. Defaults to current working directory (CWD).
51
+
52
+ Returns:
53
+ str: The local note content for the path.
54
+ """
55
+ if path is None:
56
+ path = os.getcwd()
57
+ abs_path = os.path.abspath(path)
58
+ contexts = llm_context_config.get_notes(cwd=abs_path)
59
+ return contexts.get(abs_path, "")
60
+
61
+
62
+ def write_contextual_note(content: str, path: str | None = None) -> str:
63
+ """
64
+ Persists LOCAL facts specific to a directory into 📝 Contextual Note.
65
+
66
+ USE EAGERLY to save or update:
67
+ - Architectural patterns for this project/directory.
68
+ - Summaries of large files or directories.
69
+ - Specific guidelines for this project.
70
+ - Anything related to this directory that will be useful for future interaction.
71
+
72
+ WARNING: This OVERWRITES the entire Contextual Note for a directory.
73
+
74
+ Args:
75
+ content (str): The text to memorize for this location.
76
+ path (str | None): Target file/dir. Defaults to CWD.
77
+
78
+ Returns:
79
+ str: Confirmation message.
80
+ """
81
+ if path is None:
82
+ path = os.getcwd()
83
+ llm_context_config.write_note(content, path)
84
+ return f"Contextual note saved for: {path}"
@@ -5,6 +5,7 @@ import os
5
5
  import sys
6
6
  from collections.abc import Callable
7
7
  from textwrap import dedent
8
+ from typing import Any
8
9
 
9
10
  import ulid
10
11
 
@@ -44,35 +45,40 @@ def create_rag_from_directory(
44
45
  openai_embedding_model: str | None = None,
45
46
  ):
46
47
  """
47
- Creates a powerful Retrieval-Augmented Generation (RAG) tool for querying a local knowledge base.
48
+ Create a powerful RAG (Retrieval-Augmented Generation) tool for querying a local
49
+ knowledge base.
48
50
 
49
- This factory function generates a tool that can perform semantic searches over a directory of documents. It automatically indexes the documents into a vector database, keeping it updated as files change. The generated tool is ideal for answering questions based on a specific set of documents, such as project documentation, research papers, or internal wikis.
51
+ This factory function generates a tool that performs semantic search over a directory of
52
+ documents. It automatically indexes the documents into a vector database (ChromaDB) and
53
+ keeps it updated as files change.
50
54
 
51
- The created tool will:
52
- 1. Monitor a specified directory for file changes.
53
- 2. Automatically update a vector database (ChromaDB) with the latest content.
54
- 3. Accept a user query, embed it, and perform a similarity search against the document vectors.
55
- 4. Return the most relevant document chunks that match the query.
55
+ The generated tool is ideal for answering questions based on a specific set of documents,
56
+ such as project documentation or internal wikis.
56
57
 
57
58
  Args:
58
59
  tool_name (str): The name for the generated RAG tool (e.g., "search_project_docs").
59
- tool_description (str): A clear description of what the generated tool does and when to use it (e.g., "Searches the project's technical documentation to answer questions.").
60
- document_dir_path (str, optional): The path to the directory containing the documents to be indexed.
61
- vector_db_path (str, optional): The path to store the ChromaDB vector database.
62
- vector_db_collection (str, optional): The name of the collection within the vector database.
60
+ tool_description (str): A clear description of what the tool does and when to use it.
61
+ This is what the LLM will see.
62
+ document_dir_path (str, optional): The path to the directory containing the documents
63
+ to be indexed.
64
+ vector_db_path (str, optional): The path where the ChromaDB vector database will be
65
+ stored.
66
+ vector_db_collection (str, optional): The name of the collection within the vector
67
+ database.
63
68
  chunk_size (int, optional): The size of text chunks for embedding.
64
69
  overlap (int, optional): The overlap between text chunks.
65
70
  max_result_count (int, optional): The maximum number of search results to return.
66
- file_reader (list[RAGFileReader], optional): Custom file readers for specific file types.
67
- openai_api_key (str, optional): OpenAI API key for embeddings.
68
- openai_base_url (str, optional): OpenAI base URL for embeddings.
71
+ file_reader (list[RAGFileReader], optional): A list of custom file readers for
72
+ specific file types.
73
+ openai_api_key (str, optional): Your OpenAI API key for generating embeddings.
74
+ openai_base_url (str, optional): An optional base URL for the OpenAI API.
69
75
  openai_embedding_model (str, optional): The embedding model to use.
70
76
 
71
77
  Returns:
72
- Callable: An asynchronous function that serves as the RAG tool.
78
+ An asynchronous function that serves as the RAG tool.
73
79
  """
74
80
 
75
- async def retrieve(query: str) -> str:
81
+ async def retrieve(query: str) -> dict[str, Any]:
76
82
  # Docstring will be set dynamically below
77
83
  from chromadb import PersistentClient
78
84
  from chromadb.config import Settings
@@ -187,7 +193,7 @@ def create_rag_from_directory(
187
193
  query_embeddings=query_vector,
188
194
  n_results=max_result_count_val,
189
195
  )
190
- return json.dumps(results)
196
+ return dict(results)
191
197
 
192
198
  retrieve.__name__ = tool_name
193
199
  retrieve.__doc__ = dedent(
@@ -196,7 +202,8 @@ def create_rag_from_directory(
196
202
  Args:
197
203
  query (str): The user query to search for in documents.
198
204
  Returns:
199
- str: JSON string with search results: {{"ids": [...], "documents": [...], ...}}
205
+ dict[str, Any]: dictionary with search results:
206
+ {{"ids": [...], "documents": [...], ...}}
200
207
  """
201
208
  ).strip()
202
209
  return retrieve
@@ -0,0 +1 @@
1
+ # This file makes the directory a Python package
@@ -0,0 +1,66 @@
1
+ from typing import Any
2
+
3
+ import requests
4
+
5
+ from zrb.config.config import CFG
6
+
7
+
8
+ def search_internet(
9
+ query: str,
10
+ page: int = 1,
11
+ safe_search: str | None = None,
12
+ language: str | None = None,
13
+ ) -> dict[str, Any]:
14
+ """
15
+ Performs an internet search using Brave Search.
16
+
17
+ Use this tool to find up-to-date information, answer questions about current events,
18
+ or research topics using a search engine.
19
+
20
+ **EFFICIENCY TIP:**
21
+ Make your `query` specific and keyword-rich to get the best results in a single call.
22
+ Avoid vague queries that require follow-up searches.
23
+ Bad: "new python features"
24
+ Good: "python 3.12 new features list release date"
25
+
26
+ Args:
27
+ query (str): The natural language search query (e.g., 'Soto Madura').
28
+ Do NOT include instructions, meta-talk, or internal reasoning.
29
+ Use concise terms as a human would in a search engine.
30
+ page (int): Search result page number. Defaults to 1.
31
+ safe_search (str | None): Safety setting. 'strict', 'moderate', or 'off'.
32
+ If None, uses the system default configuration.
33
+ language (str | None): Language code (e.g., 'en').
34
+ If None, uses the system default configuration.
35
+
36
+ Returns:
37
+ dict: Summary of search results (titles, links, snippets).
38
+ """
39
+ if safe_search is None:
40
+ safe_search = CFG.BRAVE_API_SAFE
41
+ if language is None:
42
+ language = CFG.BRAVE_API_LANG
43
+
44
+ user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
45
+
46
+ response = requests.get(
47
+ "https://api.search.brave.com/res/v1/web/search",
48
+ headers={
49
+ "User-Agent": user_agent,
50
+ "Accept": "application/json",
51
+ "x-subscription-token": CFG.BRAVE_API_KEY,
52
+ },
53
+ params={
54
+ "q": query,
55
+ "count": "10",
56
+ "offset": (page - 1) * 10,
57
+ "safesearch": safe_search,
58
+ "search_lang": language,
59
+ "summary": "true",
60
+ },
61
+ )
62
+ if response.status_code != 200:
63
+ raise Exception(
64
+ f"Error: Unable to retrieve search results (status code: {response.status_code})"
65
+ )
66
+ return response.json()
@@ -0,0 +1,61 @@
1
+ from typing import Any
2
+
3
+ import requests
4
+
5
+ from zrb.config.config import CFG
6
+
7
+
8
+ def search_internet(
9
+ query: str,
10
+ page: int = 1,
11
+ safe_search: int | None = None,
12
+ language: str | None = None,
13
+ ) -> dict[str, Any]:
14
+ """
15
+ Performs an internet search using SearXNG.
16
+
17
+ Use this tool to find up-to-date information, answer questions about current events,
18
+ or research topics using a search engine.
19
+
20
+ **EFFICIENCY TIP:**
21
+ Make your `query` specific and keyword-rich to get the best results in a single call.
22
+ Avoid vague queries that require follow-up searches.
23
+ Bad: "new python features"
24
+ Good: "python 3.12 new features list release date"
25
+
26
+ Args:
27
+ query (str): The natural language search query (e.g., 'Soto Madura').
28
+ Do NOT include instructions, meta-talk, or internal reasoning.
29
+ Use concise terms as a human would in a search engine.
30
+ page (int): Search result page number. Defaults to 1.
31
+ safe_search (int | None): Safety setting. 0 (None), 1 (Moderate), 2 (Strict).
32
+ If None, uses the system default configuration.
33
+ language (str | None): Language code (e.g., 'en').
34
+ If None, uses the system default configuration.
35
+
36
+ Returns:
37
+ dict: Summary of search results (titles, links, snippets).
38
+ """
39
+ if safe_search is None:
40
+ safe_search = CFG.SEARXNG_SAFE
41
+ if language is None:
42
+ language = CFG.SEARXNG_LANG
43
+
44
+ user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
45
+
46
+ response = requests.get(
47
+ url=f"{CFG.SEARXNG_BASE_URL}/search",
48
+ headers={"User-Agent": user_agent},
49
+ params={
50
+ "q": query,
51
+ "format": "json",
52
+ "pageno": page,
53
+ "safesearch": safe_search,
54
+ "language": language,
55
+ },
56
+ )
57
+ if response.status_code != 200:
58
+ raise Exception(
59
+ f"Error: Unable to retrieve search results (status code: {response.status_code})"
60
+ )
61
+ return response.json()
@@ -0,0 +1,61 @@
1
+ from typing import Any
2
+
3
+ import requests
4
+
5
+ from zrb.config.config import CFG
6
+
7
+
8
+ def search_internet(
9
+ query: str,
10
+ page: int = 1,
11
+ safe_search: str | None = None,
12
+ language: str | None = None,
13
+ ) -> dict[str, Any]:
14
+ """
15
+ Performs an internet search using SerpApi (Google).
16
+
17
+ Use this tool to find up-to-date information, answer questions about current events,
18
+ or research topics using a search engine.
19
+
20
+ **EFFICIENCY TIP:**
21
+ Make your `query` specific and keyword-rich to get the best results in a single call.
22
+ Avoid vague queries that require follow-up searches.
23
+ Bad: "new python features"
24
+ Good: "python 3.12 new features list release date"
25
+
26
+ Args:
27
+ query (str): The natural language search query (e.g., 'Soto Madura').
28
+ Do NOT include instructions, meta-talk, or internal reasoning.
29
+ Use concise terms as a human would in a search engine.
30
+ page (int): Search result page number. Defaults to 1.
31
+ safe_search (str | None): Safety setting. 'active' or 'off'.
32
+ If None, uses the system default configuration.
33
+ language (str | None): Two-letter language code (e.g., 'en', 'id').
34
+ If None, uses the system default configuration.
35
+
36
+ Returns:
37
+ dict: Summary of search results (titles, links, snippets).
38
+ """
39
+ if safe_search is None:
40
+ safe_search = CFG.SERPAPI_SAFE
41
+ if language is None:
42
+ language = CFG.SERPAPI_LANG
43
+
44
+ user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
45
+
46
+ response = requests.get(
47
+ "https://serpapi.com/search",
48
+ headers={"User-Agent": user_agent},
49
+ params={
50
+ "q": query,
51
+ "start": (page - 1) * 10,
52
+ "hl": language,
53
+ "safe": safe_search,
54
+ "api_key": CFG.SERPAPI_KEY,
55
+ },
56
+ )
57
+ if response.status_code != 200:
58
+ raise Exception(
59
+ f"Error: Unable to retrieve search results (status code: {response.status_code})"
60
+ )
61
+ return response.json()
@@ -4,19 +4,23 @@ from textwrap import dedent
4
4
  from typing import TYPE_CHECKING, Any, Coroutine
5
5
 
6
6
  from zrb.context.any_context import AnyContext
7
- from zrb.task.llm.agent import create_agent_instance, run_agent_iteration
7
+ from zrb.task.llm.agent import create_agent_instance
8
+ from zrb.task.llm.agent_runner import run_agent_iteration
8
9
  from zrb.task.llm.config import get_model, get_model_settings
9
10
  from zrb.task.llm.prompt import get_system_and_user_prompt
11
+ from zrb.task.llm.subagent_conversation_history import (
12
+ get_ctx_subagent_history,
13
+ set_ctx_subagent_history,
14
+ )
10
15
 
11
16
  if TYPE_CHECKING:
12
- from pydantic_ai import Agent, Tool
17
+ from pydantic_ai import Tool
18
+ from pydantic_ai._agent_graph import HistoryProcessor
13
19
  from pydantic_ai.models import Model
14
20
  from pydantic_ai.settings import ModelSettings
15
21
  from pydantic_ai.toolsets import AbstractToolset
16
22
 
17
23
  ToolOrCallable = Tool | Callable
18
- else:
19
- ToolOrCallable = Any
20
24
 
21
25
 
22
26
  def create_sub_agent_tool(
@@ -25,30 +29,44 @@ def create_sub_agent_tool(
25
29
  system_prompt: str | None = None,
26
30
  model: "str | Model | None" = None,
27
31
  model_settings: "ModelSettings | None" = None,
28
- tools: list[ToolOrCallable] = [],
29
- toolsets: list["AbstractToolset[Agent]"] = [],
30
- ) -> Callable[[AnyContext, str], Coroutine[Any, Any, str]]:
32
+ tools: "list[ToolOrCallable]" = [],
33
+ toolsets: list["AbstractToolset[None]"] = [],
34
+ yolo_mode: bool | list[str] | None = None,
35
+ history_processors: list["HistoryProcessor"] | None = None,
36
+ log_indent_level: int = 2,
37
+ agent_name: str | None = None,
38
+ auto_summarize: bool = True,
39
+ remember_history: bool = True,
40
+ ) -> Callable[[AnyContext, str], Coroutine[Any, Any, Any]]:
31
41
  """
32
- Creates a "tool that is another AI agent," capable of handling complex, multi-step sub-tasks.
42
+ Create a tool that is another AI agent, capable of handling complex, multi-step sub-tasks.
33
43
 
34
- This powerful factory function generates a tool that, when used, spins up a temporary, specialized AI agent. This "sub-agent" has its own system prompt, tools, and context, allowing it to focus exclusively on accomplishing the task it's given without being distracted by the main conversation.
44
+ This factory function generates a tool that, when used, spins up a temporary, specialized
45
+ AI agent. This "sub-agent" has its own system prompt, tools, and context, allowing it to
46
+ focus on accomplishing a specific task without being distracted by the main conversation.
35
47
 
36
48
  This is ideal for delegating complex tasks like analyzing a file or a repository.
37
49
 
38
50
  Args:
39
51
  tool_name (str): The name for the generated sub-agent tool.
40
- tool_description (str): A clear description of the sub-agent's purpose and when to use it.
41
- system_prompt (str, optional): The system prompt that will guide the sub-agent's behavior.
52
+ tool_description (str): A clear description of the sub-agent's purpose and when to
53
+ use it. This is what the LLM will see.
54
+ system_prompt (str, optional): The system prompt that will guide the sub-agent's
55
+ behavior.
42
56
  model (str | Model, optional): The language model the sub-agent will use.
43
57
  model_settings (ModelSettings, optional): Specific settings for the sub-agent's model.
44
- tools (list, optional): A list of tools that will be exclusively available to the sub-agent.
45
- toolsets (list, optional): A list of Toolset for the sub-agent.
58
+ tools (list, optional): A list of tools that will be exclusively available to the
59
+ sub-agent.
60
+ toolsets (list, optional): A list of Toolsets for the sub-agent.
46
61
 
47
62
  Returns:
48
- Callable: An asynchronous function that serves as the sub-agent tool. When called, it runs the sub-agent with a given query and returns its final result.
63
+ An asynchronous function that serves as the sub-agent tool. When called, it runs the
64
+ sub-agent with a given query and returns its final result.
49
65
  """
66
+ if agent_name is None:
67
+ agent_name = f"{tool_name}_agent"
50
68
 
51
- async def run_sub_agent(ctx: AnyContext, query: str) -> str:
69
+ async def run_sub_agent(ctx: AnyContext, query: str) -> Any:
52
70
  """
53
71
  Runs the sub-agent with the given query.
54
72
  """
@@ -67,7 +85,6 @@ def create_sub_agent_tool(
67
85
  ctx=ctx,
68
86
  model_settings_attr=model_settings,
69
87
  )
70
-
71
88
  if system_prompt is None:
72
89
  resolved_system_prompt, query = get_system_and_user_prompt(
73
90
  ctx=ctx,
@@ -86,25 +103,35 @@ def create_sub_agent_tool(
86
103
  model_settings=resolved_model_settings,
87
104
  tools=tools,
88
105
  toolsets=toolsets,
106
+ yolo_mode=yolo_mode,
107
+ history_processors=history_processors,
108
+ auto_summarize=auto_summarize,
89
109
  )
90
-
91
110
  sub_agent_run = None
92
111
  # Run the sub-agent iteration
93
- # Start with an empty history for the sub-agent
112
+ history_list = (
113
+ get_ctx_subagent_history(ctx, agent_name) if remember_history else []
114
+ )
94
115
  sub_agent_run = await run_agent_iteration(
95
116
  ctx=ctx,
96
117
  agent=sub_agent_agent,
97
118
  user_prompt=query,
98
- history_list=[], # Start with empty history for the sub-agent
119
+ attachments=[],
120
+ history_list=history_list,
121
+ log_indent_level=log_indent_level,
99
122
  )
100
-
101
123
  # Return the sub-agent's final message content
102
124
  if sub_agent_run and sub_agent_run.result:
103
- # Return the final message content as a string
104
- return json.dumps({"result": sub_agent_run.result.output})
105
- else:
106
- ctx.log_warning("Sub-agent run did not produce a result.")
107
- return "Sub-agent failed to produce a result."
125
+ # Return the final message content
126
+ if remember_history:
127
+ set_ctx_subagent_history(
128
+ ctx,
129
+ agent_name,
130
+ json.loads(sub_agent_run.result.all_messages_json()),
131
+ )
132
+ return sub_agent_run.result.output
133
+ ctx.log_warning("Sub-agent run did not produce a result.")
134
+ raise ValueError(f"{tool_name} not returning any result")
108
135
 
109
136
  # Set the name and docstring for the callable function
110
137
  run_sub_agent.__name__ = tool_name
@@ -116,7 +143,7 @@ def create_sub_agent_tool(
116
143
  query (str): The query or task for the sub-agent.
117
144
 
118
145
  Returns:
119
- str: The final response or result from the sub-agent.
146
+ Any: The final response or result from the sub-agent.
120
147
  """
121
148
  ).strip()
122
149