zrb 1.15.3__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.
- 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/llm/attachment.py +40 -0
- zrb/builtin/llm/chat_completion.py +274 -0
- zrb/builtin/llm/chat_session.py +126 -167
- zrb/builtin/llm/chat_session_cmd.py +288 -0
- zrb/builtin/llm/chat_trigger.py +79 -0
- zrb/builtin/llm/history.py +4 -4
- zrb/builtin/llm/llm_ask.py +217 -135
- zrb/builtin/llm/tool/api.py +74 -70
- zrb/builtin/llm/tool/cli.py +35 -21
- zrb/builtin/llm/tool/code.py +55 -73
- zrb/builtin/llm/tool/file.py +278 -344
- zrb/builtin/llm/tool/note.py +84 -0
- zrb/builtin/llm/tool/rag.py +27 -34
- zrb/builtin/llm/tool/sub_agent.py +54 -41
- zrb/builtin/llm/tool/web.py +74 -98
- 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/shell/autocomplete/bash.py +4 -3
- zrb/builtin/shell/autocomplete/zsh.py +4 -3
- zrb/config/config.py +202 -27
- zrb/config/default_prompt/file_extractor_system_prompt.md +109 -9
- zrb/config/default_prompt/interactive_system_prompt.md +24 -30
- 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 +36 -30
- zrb/config/llm_config.py +119 -23
- 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 +3 -7
- zrb/group/any_group.py +3 -3
- zrb/group/group.py +3 -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_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 +128 -167
- zrb/task/llm/agent_runner.py +152 -0
- zrb/task/llm/config.py +39 -20
- 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 -193
- zrb/task/llm/print_node.py +184 -64
- zrb/task/llm/prompt.py +175 -179
- zrb/task/llm/subagent_conversation_history.py +41 -0
- zrb/task/llm/tool_wrapper.py +226 -85
- zrb/task/llm/workflow.py +76 -0
- zrb/task/llm_task.py +109 -71
- 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 +12 -3
- 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.15.3.dist-info → zrb-1.21.29.dist-info}/METADATA +38 -18
- {zrb-1.15.3.dist-info → zrb-1.21.29.dist-info}/RECORD +105 -79
- {zrb-1.15.3.dist-info → zrb-1.21.29.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.15.3.dist-info → zrb-1.21.29.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
from asyncio import StreamReader
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Coroutine
|
|
5
|
+
|
|
6
|
+
from zrb.context.any_context import AnyContext
|
|
7
|
+
from zrb.util.run import run_async
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from prompt_toolkit import PromptSession
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
ChatTrigger = Callable[[AnyContext], Coroutine[Any, Any, str] | str]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LLMChatTrigger:
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self._triggers: list[ChatTrigger] = []
|
|
20
|
+
|
|
21
|
+
def add_trigger(self, *trigger: ChatTrigger):
|
|
22
|
+
self.append_trigger(*trigger)
|
|
23
|
+
|
|
24
|
+
def append_trigger(self, *trigger: ChatTrigger):
|
|
25
|
+
for single_trigger in trigger:
|
|
26
|
+
self._triggers.append(single_trigger)
|
|
27
|
+
|
|
28
|
+
async def wait(
|
|
29
|
+
self, reader: "PromptSession[Any] | StreamReader", ctx: AnyContext
|
|
30
|
+
) -> str:
|
|
31
|
+
trigger_tasks = [
|
|
32
|
+
asyncio.create_task(run_async(self._read_next_line(reader, ctx)))
|
|
33
|
+
] + [asyncio.create_task(run_async(trigger(ctx))) for trigger in self._triggers]
|
|
34
|
+
final_result: str = ""
|
|
35
|
+
try:
|
|
36
|
+
done, pending = await asyncio.wait(
|
|
37
|
+
trigger_tasks, return_when=asyncio.FIRST_COMPLETED
|
|
38
|
+
)
|
|
39
|
+
for task in done:
|
|
40
|
+
final_result = await task
|
|
41
|
+
if pending:
|
|
42
|
+
for task in pending:
|
|
43
|
+
task.cancel()
|
|
44
|
+
for task in done:
|
|
45
|
+
break
|
|
46
|
+
except asyncio.CancelledError:
|
|
47
|
+
ctx.print("Task cancelled.", plain=True)
|
|
48
|
+
final_result = "/bye"
|
|
49
|
+
except KeyboardInterrupt:
|
|
50
|
+
ctx.print("KeyboardInterrupt detected. Exiting...", plain=True)
|
|
51
|
+
final_result = "/bye"
|
|
52
|
+
return final_result
|
|
53
|
+
|
|
54
|
+
async def _read_next_line(
|
|
55
|
+
self, reader: "PromptSession[Any] | StreamReader", ctx: AnyContext
|
|
56
|
+
) -> str:
|
|
57
|
+
"""Reads one line of input using the provided reader."""
|
|
58
|
+
from prompt_toolkit import PromptSession
|
|
59
|
+
|
|
60
|
+
from zrb.builtin.llm.chat_completion import ChatCompleter
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
if isinstance(reader, PromptSession):
|
|
64
|
+
bottom_toolbar = f"📁 Current directory: {os.getcwd()}"
|
|
65
|
+
return await reader.prompt_async(
|
|
66
|
+
completer=ChatCompleter(), bottom_toolbar=bottom_toolbar
|
|
67
|
+
)
|
|
68
|
+
line_bytes = await reader.readline()
|
|
69
|
+
if not line_bytes:
|
|
70
|
+
return "/bye" # Signal to exit
|
|
71
|
+
user_input = line_bytes.decode().strip()
|
|
72
|
+
ctx.print(user_input, plain=True)
|
|
73
|
+
return user_input
|
|
74
|
+
except KeyboardInterrupt:
|
|
75
|
+
ctx.print("KeyboardInterrupt detected. Exiting...", plain=True)
|
|
76
|
+
return "/bye"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
llm_chat_trigger = LLMChatTrigger()
|
zrb/builtin/llm/history.py
CHANGED
|
@@ -3,12 +3,12 @@ import os
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
from zrb.config.config import CFG
|
|
6
|
-
from zrb.context.
|
|
6
|
+
from zrb.context.any_context import AnyContext
|
|
7
7
|
from zrb.task.llm.conversation_history_model import ConversationHistory
|
|
8
8
|
from zrb.util.file import read_file, write_file
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def read_chat_conversation(ctx:
|
|
11
|
+
def read_chat_conversation(ctx: AnyContext) -> dict[str, Any] | list | None:
|
|
12
12
|
"""Reads conversation history from the session file.
|
|
13
13
|
Returns the raw dictionary or list loaded from JSON, or None if not found/empty.
|
|
14
14
|
The LLMTask will handle parsing this into ConversationHistory.
|
|
@@ -51,10 +51,10 @@ def read_chat_conversation(ctx: AnySharedContext) -> dict[str, Any] | list | Non
|
|
|
51
51
|
return None
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
def write_chat_conversation(ctx:
|
|
54
|
+
def write_chat_conversation(ctx: AnyContext, history_data: ConversationHistory):
|
|
55
55
|
"""Writes the conversation history data (including context) to a session file."""
|
|
56
56
|
os.makedirs(CFG.LLM_HISTORY_DIR, exist_ok=True)
|
|
57
|
-
current_session_name = ctx.session.name
|
|
57
|
+
current_session_name = ctx.session.name if ctx.session is not None else None
|
|
58
58
|
if not current_session_name:
|
|
59
59
|
ctx.log_warning("Cannot write history: Session name is empty.")
|
|
60
60
|
return
|
zrb/builtin/llm/llm_ask.py
CHANGED
|
@@ -1,137 +1,257 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
1
5
|
from zrb.builtin.group import llm_group
|
|
6
|
+
from zrb.builtin.llm.attachment import get_media_type
|
|
2
7
|
from zrb.builtin.llm.chat_session import get_llm_ask_input_mapping, read_user_prompt
|
|
3
8
|
from zrb.builtin.llm.history import read_chat_conversation, write_chat_conversation
|
|
4
9
|
from zrb.builtin.llm.input import PreviousSessionInput
|
|
5
|
-
from zrb.builtin.llm.tool.api import
|
|
10
|
+
from zrb.builtin.llm.tool.api import (
|
|
11
|
+
create_get_current_location,
|
|
12
|
+
create_get_current_weather,
|
|
13
|
+
)
|
|
6
14
|
from zrb.builtin.llm.tool.cli import run_shell_command
|
|
7
15
|
from zrb.builtin.llm.tool.code import analyze_repo
|
|
8
16
|
from zrb.builtin.llm.tool.file import (
|
|
9
17
|
analyze_file,
|
|
10
18
|
list_files,
|
|
11
19
|
read_from_file,
|
|
12
|
-
read_many_files,
|
|
13
20
|
replace_in_file,
|
|
14
21
|
search_files,
|
|
15
|
-
write_many_files,
|
|
16
22
|
write_to_file,
|
|
17
23
|
)
|
|
24
|
+
from zrb.builtin.llm.tool.note import (
|
|
25
|
+
read_contextual_note,
|
|
26
|
+
read_long_term_note,
|
|
27
|
+
write_contextual_note,
|
|
28
|
+
write_long_term_note,
|
|
29
|
+
)
|
|
18
30
|
from zrb.builtin.llm.tool.web import (
|
|
19
31
|
create_search_internet_tool,
|
|
20
32
|
open_web_page,
|
|
21
|
-
search_arxiv,
|
|
22
|
-
search_wikipedia,
|
|
23
33
|
)
|
|
24
34
|
from zrb.callback.callback import Callback
|
|
25
35
|
from zrb.config.config import CFG
|
|
26
36
|
from zrb.config.llm_config import llm_config
|
|
37
|
+
from zrb.context.any_context import AnyContext
|
|
38
|
+
from zrb.input.any_input import AnyInput
|
|
27
39
|
from zrb.input.bool_input import BoolInput
|
|
28
40
|
from zrb.input.str_input import StrInput
|
|
29
41
|
from zrb.input.text_input import TextInput
|
|
30
42
|
from zrb.task.base_trigger import BaseTrigger
|
|
31
43
|
from zrb.task.llm_task import LLMTask
|
|
44
|
+
from zrb.util.string.conversion import to_boolean
|
|
32
45
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
input
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from pydantic_ai import AbstractToolset, Tool, UserContent
|
|
48
|
+
|
|
49
|
+
ToolOrCallable = Tool | Callable
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _get_toolset(ctx: AnyContext) -> list["AbstractToolset[None] | str"]:
|
|
53
|
+
cwd = os.getcwd()
|
|
54
|
+
toolsets = []
|
|
55
|
+
for config_path in [
|
|
56
|
+
os.path.join(cwd, "mcp_config.json"),
|
|
57
|
+
os.path.join(cwd, "mcp-config.json"),
|
|
58
|
+
]:
|
|
59
|
+
if os.path.isfile(config_path):
|
|
60
|
+
toolsets.append(config_path)
|
|
61
|
+
return toolsets
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _get_tool(ctx: AnyContext) -> list["ToolOrCallable"]:
|
|
65
|
+
tools = [
|
|
66
|
+
read_long_term_note,
|
|
67
|
+
write_long_term_note,
|
|
68
|
+
read_contextual_note,
|
|
69
|
+
write_contextual_note,
|
|
70
|
+
]
|
|
71
|
+
if CFG.LLM_ALLOW_ANALYZE_REPO:
|
|
72
|
+
tools.append(analyze_repo)
|
|
73
|
+
if CFG.LLM_ALLOW_ANALYZE_FILE:
|
|
74
|
+
tools.append(analyze_file)
|
|
75
|
+
if CFG.LLM_ALLOW_ACCESS_LOCAL_FILE:
|
|
76
|
+
tools.append(search_files)
|
|
77
|
+
tools.append(list_files)
|
|
78
|
+
tools.append(read_from_file)
|
|
79
|
+
tools.append(replace_in_file)
|
|
80
|
+
tools.append(write_to_file)
|
|
81
|
+
if CFG.LLM_ALLOW_ACCESS_SHELL:
|
|
82
|
+
tools.append(run_shell_command)
|
|
83
|
+
if CFG.LLM_ALLOW_OPEN_WEB_PAGE:
|
|
84
|
+
tools.append(open_web_page)
|
|
85
|
+
if CFG.LLM_ALLOW_GET_CURRENT_LOCATION:
|
|
86
|
+
tools.append(create_get_current_location())
|
|
87
|
+
if CFG.LLM_ALLOW_GET_CURRENT_WEATHER:
|
|
88
|
+
tools.append(create_get_current_weather())
|
|
89
|
+
if CFG.SERPAPI_KEY != "" and CFG.LLM_ALLOW_SEARCH_INTERNET:
|
|
90
|
+
tools.append(create_search_internet_tool())
|
|
91
|
+
return tools
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _get_default_yolo_mode(ctx: AnyContext) -> str:
|
|
95
|
+
default_value = llm_config.default_yolo_mode
|
|
96
|
+
if isinstance(default_value, list):
|
|
97
|
+
return ",".join(default_value)
|
|
98
|
+
return f"{default_value}"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _render_yolo_mode_input(ctx: AnyContext) -> list[str] | bool:
|
|
102
|
+
if ctx.input.yolo.strip() == "":
|
|
103
|
+
return []
|
|
104
|
+
elements = [element.strip() for element in ctx.input.yolo.split(",")]
|
|
105
|
+
if len(elements) == 1:
|
|
106
|
+
try:
|
|
107
|
+
return to_boolean(elements[0])
|
|
108
|
+
except Exception:
|
|
109
|
+
pass
|
|
110
|
+
return elements
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _render_attach_input(ctx: AnyContext) -> "list[UserContent]":
|
|
114
|
+
from pathlib import Path
|
|
115
|
+
|
|
116
|
+
from pydantic_ai import BinaryContent
|
|
117
|
+
|
|
118
|
+
attachment_paths: list[str] = [
|
|
119
|
+
attachment_path.strip()
|
|
120
|
+
for attachment_path in ctx.input.attach.split(",")
|
|
121
|
+
if attachment_path.strip() != ""
|
|
122
|
+
]
|
|
123
|
+
if len(attachment_paths) == 0:
|
|
124
|
+
return []
|
|
125
|
+
attachments = []
|
|
126
|
+
for attachment_path in attachment_paths:
|
|
127
|
+
attachment_path = os.path.abspath(os.path.expanduser(attachment_path))
|
|
128
|
+
media_type = get_media_type(attachment_path)
|
|
129
|
+
if media_type is None:
|
|
130
|
+
ctx.log_error(f"Cannot get media type of {attachment_path}")
|
|
131
|
+
continue
|
|
132
|
+
data = Path(attachment_path).read_bytes()
|
|
133
|
+
attachments.append(BinaryContent(data, media_type=media_type))
|
|
134
|
+
return attachments
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _get_inputs(require_message: bool = True) -> list[AnyInput | None]:
|
|
138
|
+
return [
|
|
139
|
+
StrInput(
|
|
140
|
+
"model",
|
|
141
|
+
description="LLM Model",
|
|
142
|
+
prompt="LLM Model",
|
|
143
|
+
default="",
|
|
144
|
+
allow_positional_parsing=False,
|
|
145
|
+
always_prompt=False,
|
|
146
|
+
allow_empty=True,
|
|
112
147
|
),
|
|
113
|
-
|
|
114
|
-
|
|
148
|
+
StrInput(
|
|
149
|
+
"base-url",
|
|
150
|
+
description="LLM API Base URL",
|
|
151
|
+
prompt="LLM API Base URL",
|
|
152
|
+
default="",
|
|
153
|
+
allow_positional_parsing=False,
|
|
154
|
+
always_prompt=False,
|
|
155
|
+
allow_empty=True,
|
|
115
156
|
),
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
157
|
+
StrInput(
|
|
158
|
+
"api-key",
|
|
159
|
+
description="LLM API Key",
|
|
160
|
+
prompt="LLM API Key",
|
|
161
|
+
default="",
|
|
162
|
+
allow_positional_parsing=False,
|
|
163
|
+
always_prompt=False,
|
|
164
|
+
allow_empty=True,
|
|
120
165
|
),
|
|
121
|
-
|
|
122
|
-
|
|
166
|
+
TextInput(
|
|
167
|
+
"system-prompt",
|
|
168
|
+
description="System prompt",
|
|
169
|
+
prompt="System prompt",
|
|
170
|
+
default="",
|
|
171
|
+
allow_positional_parsing=False,
|
|
172
|
+
always_prompt=False,
|
|
123
173
|
),
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
174
|
+
TextInput(
|
|
175
|
+
"workflows",
|
|
176
|
+
description="Workflows",
|
|
177
|
+
prompt="Workflows",
|
|
178
|
+
default=lambda ctx: ",".join(llm_config.default_workflows),
|
|
179
|
+
allow_positional_parsing=False,
|
|
180
|
+
always_prompt=False,
|
|
181
|
+
),
|
|
182
|
+
BoolInput(
|
|
183
|
+
"start-new",
|
|
184
|
+
description="Start new session (LLM Agent will forget past conversation)",
|
|
185
|
+
prompt="Start new session (LLM Agent will forget past conversation)",
|
|
186
|
+
default=False,
|
|
187
|
+
allow_positional_parsing=False,
|
|
188
|
+
always_prompt=False,
|
|
189
|
+
),
|
|
190
|
+
StrInput(
|
|
191
|
+
"yolo",
|
|
192
|
+
description="YOLO mode (LLM Agent will start in YOLO Mode)",
|
|
193
|
+
prompt="YOLO mode (LLM Agent will start in YOLO Mode)",
|
|
194
|
+
default=_get_default_yolo_mode,
|
|
195
|
+
allow_positional_parsing=False,
|
|
196
|
+
always_prompt=False,
|
|
197
|
+
),
|
|
198
|
+
TextInput(
|
|
199
|
+
"message",
|
|
200
|
+
description="User message",
|
|
201
|
+
prompt="Your message",
|
|
202
|
+
always_prompt=require_message,
|
|
203
|
+
allow_empty=not require_message,
|
|
204
|
+
),
|
|
205
|
+
StrInput(
|
|
206
|
+
name="attach",
|
|
207
|
+
description="Comma separated attachments",
|
|
208
|
+
default="",
|
|
209
|
+
allow_positional_parsing=False,
|
|
210
|
+
always_prompt=False,
|
|
211
|
+
),
|
|
212
|
+
PreviousSessionInput(
|
|
213
|
+
"previous-session",
|
|
214
|
+
description="Previous conversation session",
|
|
215
|
+
prompt="Previous conversation session (can be empty)",
|
|
216
|
+
allow_positional_parsing=False,
|
|
217
|
+
allow_empty=True,
|
|
218
|
+
always_prompt=False,
|
|
219
|
+
),
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
llm_ask = LLMTask(
|
|
224
|
+
name="llm-ask",
|
|
225
|
+
input=_get_inputs(True),
|
|
226
|
+
description="❓ Ask LLM",
|
|
227
|
+
model=lambda ctx: None if ctx.input.model.strip() == "" else ctx.input.model,
|
|
228
|
+
model_base_url=lambda ctx: (
|
|
229
|
+
None if ctx.input.base_url.strip() == "" else ctx.input.base_url
|
|
230
|
+
),
|
|
231
|
+
model_api_key=lambda ctx: (
|
|
232
|
+
None if ctx.input.api_key.strip() == "" else ctx.input.api_key
|
|
127
233
|
),
|
|
128
|
-
|
|
234
|
+
conversation_history_reader=read_chat_conversation,
|
|
235
|
+
conversation_history_writer=write_chat_conversation,
|
|
236
|
+
system_prompt=lambda ctx: (
|
|
237
|
+
None if ctx.input.system_prompt.strip() == "" else ctx.input.system_prompt
|
|
238
|
+
),
|
|
239
|
+
workflows=lambda ctx: (
|
|
240
|
+
None if ctx.input.workflows.strip() == "" else ctx.input.workflows.split(",")
|
|
241
|
+
),
|
|
242
|
+
attachment=_render_attach_input,
|
|
243
|
+
message="{ctx.input.message}",
|
|
244
|
+
tools=_get_tool,
|
|
245
|
+
toolsets=_get_toolset,
|
|
246
|
+
yolo_mode=_render_yolo_mode_input,
|
|
247
|
+
retries=0,
|
|
129
248
|
)
|
|
249
|
+
llm_group.add_task(llm_ask, alias="ask")
|
|
130
250
|
|
|
131
251
|
llm_group.add_task(
|
|
132
252
|
BaseTrigger(
|
|
133
253
|
name="llm-chat",
|
|
134
|
-
input=
|
|
254
|
+
input=_get_inputs(False),
|
|
135
255
|
description="💬 Chat with LLM",
|
|
136
256
|
queue_name="ask_trigger",
|
|
137
257
|
action=read_user_prompt,
|
|
@@ -147,41 +267,3 @@ llm_group.add_task(
|
|
|
147
267
|
),
|
|
148
268
|
alias="chat",
|
|
149
269
|
)
|
|
150
|
-
|
|
151
|
-
if CFG.LLM_ALLOW_ANALYZE_REPO:
|
|
152
|
-
llm_ask.append_tool(analyze_repo)
|
|
153
|
-
|
|
154
|
-
if CFG.LLM_ALLOW_ANALYZE_FILE:
|
|
155
|
-
llm_ask.append_tool(analyze_file)
|
|
156
|
-
|
|
157
|
-
if CFG.LLM_ALLOW_ACCESS_LOCAL_FILE:
|
|
158
|
-
llm_ask.append_tool(
|
|
159
|
-
search_files,
|
|
160
|
-
list_files,
|
|
161
|
-
read_from_file,
|
|
162
|
-
read_many_files,
|
|
163
|
-
replace_in_file,
|
|
164
|
-
write_to_file,
|
|
165
|
-
write_many_files,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
if CFG.LLM_ALLOW_ACCESS_SHELL:
|
|
169
|
-
llm_ask.append_tool(run_shell_command)
|
|
170
|
-
|
|
171
|
-
if CFG.LLM_ALLOW_OPEN_WEB_PAGE:
|
|
172
|
-
llm_ask.append_tool(open_web_page)
|
|
173
|
-
|
|
174
|
-
if CFG.LLM_ALLOW_SEARCH_WIKIPEDIA:
|
|
175
|
-
llm_ask.append_tool(search_wikipedia)
|
|
176
|
-
|
|
177
|
-
if CFG.LLM_ALLOW_SEARCH_ARXIV:
|
|
178
|
-
llm_ask.append_tool(search_arxiv)
|
|
179
|
-
|
|
180
|
-
if CFG.LLM_ALLOW_GET_CURRENT_LOCATION:
|
|
181
|
-
llm_ask.append_tool(get_current_location)
|
|
182
|
-
|
|
183
|
-
if CFG.LLM_ALLOW_GET_CURRENT_WEATHER:
|
|
184
|
-
llm_ask.append_tool(get_current_weather)
|
|
185
|
-
|
|
186
|
-
if CFG.SERPAPI_KEY != "" and CFG.LLM_ALLOW_SEARCH_INTERNET:
|
|
187
|
-
llm_ask.append_tool(create_search_internet_tool(CFG.SERPAPI_KEY))
|
zrb/builtin/llm/tool/api.py
CHANGED
|
@@ -1,71 +1,75 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
str: A JSON string containing detailed weather data, including
|
|
50
|
-
temperature, wind speed, and weather code.
|
|
51
|
-
Raises:
|
|
52
|
-
requests.RequestException: If the API request to the weather service
|
|
53
|
-
fails.
|
|
54
|
-
"""
|
|
55
|
-
import requests
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
response = requests.get(
|
|
59
|
-
"https://api.open-meteo.com/v1/forecast",
|
|
60
|
-
params={
|
|
61
|
-
"latitude": latitude,
|
|
62
|
-
"longitude": longitude,
|
|
63
|
-
"temperature_unit": temperature_unit,
|
|
64
|
-
"current_weather": True,
|
|
65
|
-
},
|
|
66
|
-
timeout=5,
|
|
1
|
+
from typing import Any, Callable, Literal
|
|
2
|
+
|
|
3
|
+
from zrb.config.llm_config import llm_config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def create_get_current_location() -> Callable:
|
|
7
|
+
if llm_config.default_current_location_tool is not None:
|
|
8
|
+
return llm_config.default_current_location_tool
|
|
9
|
+
|
|
10
|
+
def get_current_location() -> dict[str, float]:
|
|
11
|
+
"""
|
|
12
|
+
Gets the user's current geographical location (latitude and longitude).
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
get_current_location() # Returns {'lat': 48.8584, 'lon': 2.2945}
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
dict: Dictionary with 'lat' and 'lon' keys.
|
|
19
|
+
"""
|
|
20
|
+
import requests
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
response = requests.get("http://ip-api.com/json?fields=lat,lon", timeout=5)
|
|
24
|
+
response.raise_for_status()
|
|
25
|
+
return dict(response.json())
|
|
26
|
+
except requests.RequestException as e:
|
|
27
|
+
raise requests.RequestException(f"Failed to get location: {e}")
|
|
28
|
+
|
|
29
|
+
return get_current_location
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def create_get_current_weather() -> Callable:
|
|
33
|
+
if llm_config.default_current_weather_tool is not None:
|
|
34
|
+
return llm_config.default_current_weather_tool
|
|
35
|
+
|
|
36
|
+
def get_current_weather(
|
|
37
|
+
latitude: float,
|
|
38
|
+
longitude: float,
|
|
39
|
+
temperature_unit: Literal["celsius", "fahrenheit"],
|
|
40
|
+
) -> dict[str, Any]:
|
|
41
|
+
"""
|
|
42
|
+
Gets current weather conditions for a given location.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
get_current_weather(
|
|
46
|
+
latitude=34.0522, longitude=-118.2437, temperature_unit='fahrenheit'
|
|
67
47
|
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
latitude (float): Latitude of the location.
|
|
51
|
+
longitude (float): Longitude of the location.
|
|
52
|
+
temperature_unit (Literal): 'celsius' or 'fahrenheit'.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
dict: Detailed weather data.
|
|
56
|
+
"""
|
|
57
|
+
import requests
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
response = requests.get(
|
|
61
|
+
"https://api.open-meteo.com/v1/forecast",
|
|
62
|
+
params={
|
|
63
|
+
"latitude": latitude,
|
|
64
|
+
"longitude": longitude,
|
|
65
|
+
"temperature_unit": temperature_unit,
|
|
66
|
+
"current_weather": True,
|
|
67
|
+
},
|
|
68
|
+
timeout=5,
|
|
69
|
+
)
|
|
70
|
+
response.raise_for_status()
|
|
71
|
+
return dict(response.json())
|
|
72
|
+
except requests.RequestException as e:
|
|
73
|
+
raise requests.RequestException(f"Failed to get weather data: {e}")
|
|
74
|
+
|
|
75
|
+
return get_current_weather
|