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.
- zrb/__init__.py +2 -6
- zrb/attr/type.py +10 -7
- zrb/builtin/__init__.py +2 -0
- zrb/builtin/git.py +12 -1
- zrb/builtin/group.py +31 -15
- zrb/builtin/http.py +7 -8
- zrb/builtin/llm/attachment.py +40 -0
- zrb/builtin/llm/chat_completion.py +287 -0
- zrb/builtin/llm/chat_session.py +130 -144
- zrb/builtin/llm/chat_session_cmd.py +288 -0
- zrb/builtin/llm/chat_trigger.py +78 -0
- zrb/builtin/llm/history.py +4 -4
- zrb/builtin/llm/llm_ask.py +218 -110
- zrb/builtin/llm/tool/api.py +74 -62
- zrb/builtin/llm/tool/cli.py +56 -21
- zrb/builtin/llm/tool/code.py +57 -47
- zrb/builtin/llm/tool/file.py +292 -255
- zrb/builtin/llm/tool/note.py +84 -0
- zrb/builtin/llm/tool/rag.py +25 -18
- zrb/builtin/llm/tool/search/__init__.py +1 -0
- zrb/builtin/llm/tool/search/brave.py +66 -0
- zrb/builtin/llm/tool/search/searxng.py +61 -0
- zrb/builtin/llm/tool/search/serpapi.py +61 -0
- zrb/builtin/llm/tool/sub_agent.py +53 -26
- zrb/builtin/llm/tool/web.py +94 -157
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
- zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
- zrb/builtin/searxng/config/settings.yml +5671 -0
- zrb/builtin/searxng/start.py +21 -0
- zrb/builtin/setup/latex/ubuntu.py +1 -0
- zrb/builtin/setup/ubuntu.py +1 -1
- zrb/builtin/shell/autocomplete/bash.py +4 -3
- zrb/builtin/shell/autocomplete/zsh.py +4 -3
- zrb/config/config.py +297 -79
- zrb/config/default_prompt/file_extractor_system_prompt.md +109 -9
- zrb/config/default_prompt/interactive_system_prompt.md +25 -28
- zrb/config/default_prompt/persona.md +1 -1
- zrb/config/default_prompt/repo_extractor_system_prompt.md +31 -31
- zrb/config/default_prompt/repo_summarizer_system_prompt.md +27 -8
- zrb/config/default_prompt/summarization_prompt.md +57 -16
- zrb/config/default_prompt/system_prompt.md +29 -25
- zrb/config/llm_config.py +129 -24
- zrb/config/llm_context/config.py +127 -90
- zrb/config/llm_context/config_parser.py +1 -7
- zrb/config/llm_context/workflow.py +81 -0
- zrb/config/llm_rate_limitter.py +100 -47
- zrb/context/any_shared_context.py +7 -1
- zrb/context/context.py +8 -2
- zrb/context/shared_context.py +6 -8
- zrb/group/any_group.py +12 -5
- zrb/group/group.py +67 -3
- zrb/input/any_input.py +5 -1
- zrb/input/base_input.py +18 -6
- zrb/input/option_input.py +13 -1
- zrb/input/text_input.py +7 -24
- zrb/runner/cli.py +21 -20
- zrb/runner/common_util.py +24 -19
- zrb/runner/web_route/task_input_api_route.py +5 -5
- zrb/runner/web_route/task_session_api_route.py +1 -4
- zrb/runner/web_util/user.py +7 -3
- zrb/session/any_session.py +12 -6
- zrb/session/session.py +39 -18
- zrb/task/any_task.py +24 -3
- zrb/task/base/context.py +17 -9
- zrb/task/base/execution.py +15 -8
- zrb/task/base/lifecycle.py +8 -4
- zrb/task/base/monitoring.py +12 -7
- zrb/task/base_task.py +69 -5
- zrb/task/base_trigger.py +12 -5
- zrb/task/llm/agent.py +130 -145
- zrb/task/llm/agent_runner.py +152 -0
- zrb/task/llm/config.py +45 -13
- zrb/task/llm/conversation_history.py +110 -29
- zrb/task/llm/conversation_history_model.py +4 -179
- zrb/task/llm/default_workflow/coding/workflow.md +41 -0
- zrb/task/llm/default_workflow/copywriting/workflow.md +68 -0
- zrb/task/llm/default_workflow/git/workflow.md +118 -0
- zrb/task/llm/default_workflow/golang/workflow.md +128 -0
- zrb/task/llm/default_workflow/html-css/workflow.md +135 -0
- zrb/task/llm/default_workflow/java/workflow.md +146 -0
- zrb/task/llm/default_workflow/javascript/workflow.md +158 -0
- zrb/task/llm/default_workflow/python/workflow.md +160 -0
- zrb/task/llm/default_workflow/researching/workflow.md +153 -0
- zrb/task/llm/default_workflow/rust/workflow.md +162 -0
- zrb/task/llm/default_workflow/shell/workflow.md +299 -0
- zrb/task/llm/file_replacement.py +206 -0
- zrb/task/llm/file_tool_model.py +57 -0
- zrb/task/llm/history_processor.py +206 -0
- zrb/task/llm/history_summarization.py +2 -192
- zrb/task/llm/print_node.py +192 -64
- zrb/task/llm/prompt.py +198 -153
- zrb/task/llm/subagent_conversation_history.py +41 -0
- zrb/task/llm/tool_confirmation_completer.py +41 -0
- zrb/task/llm/tool_wrapper.py +216 -55
- zrb/task/llm/workflow.py +76 -0
- zrb/task/llm_task.py +122 -70
- zrb/task/make_task.py +2 -3
- zrb/task/rsync_task.py +25 -10
- zrb/task/scheduler.py +4 -4
- zrb/util/attr.py +54 -39
- zrb/util/cli/markdown.py +12 -0
- zrb/util/cli/text.py +30 -0
- zrb/util/file.py +27 -11
- zrb/util/git.py +2 -2
- zrb/util/{llm/prompt.py → markdown.py} +2 -3
- zrb/util/string/conversion.py +1 -1
- zrb/util/truncate.py +23 -0
- zrb/util/yaml.py +204 -0
- zrb/xcom/xcom.py +10 -0
- {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/METADATA +40 -20
- {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/RECORD +114 -83
- {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/WHEEL +1 -1
- zrb/task/llm/default_workflow/coding.md +0 -24
- zrb/task/llm/default_workflow/copywriting.md +0 -17
- zrb/task/llm/default_workflow/researching.md +0 -18
- {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}"
|
zrb/builtin/llm/tool/rag.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
52
|
-
|
|
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
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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):
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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[
|
|
30
|
-
|
|
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
|
-
|
|
42
|
+
Create a tool that is another AI agent, capable of handling complex, multi-step sub-tasks.
|
|
33
43
|
|
|
34
|
-
This
|
|
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
|
|
41
|
-
|
|
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
|
|
45
|
-
|
|
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
|
-
|
|
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) ->
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
146
|
+
Any: The final response or result from the sub-agent.
|
|
120
147
|
"""
|
|
121
148
|
).strip()
|
|
122
149
|
|