zrb 1.15.3__py3-none-any.whl → 2.0.0a4__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 +118 -133
- zrb/attr/type.py +10 -7
- zrb/builtin/__init__.py +55 -1
- zrb/builtin/git.py +12 -1
- zrb/builtin/group.py +31 -15
- zrb/builtin/llm/chat.py +147 -0
- 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/callback/callback.py +8 -1
- zrb/cmd/cmd_result.py +2 -1
- zrb/config/config.py +555 -169
- zrb/config/helper.py +84 -0
- zrb/config/web_auth_config.py +50 -35
- zrb/context/any_shared_context.py +20 -3
- zrb/context/context.py +39 -5
- zrb/context/print_fn.py +13 -0
- zrb/context/shared_context.py +17 -8
- 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 +41 -1
- zrb/input/text_input.py +7 -24
- zrb/llm/agent/__init__.py +9 -0
- zrb/llm/agent/agent.py +215 -0
- zrb/llm/agent/summarizer.py +20 -0
- zrb/llm/app/__init__.py +10 -0
- zrb/llm/app/completion.py +281 -0
- zrb/llm/app/confirmation/allow_tool.py +66 -0
- zrb/llm/app/confirmation/handler.py +178 -0
- zrb/llm/app/confirmation/replace_confirmation.py +77 -0
- zrb/llm/app/keybinding.py +34 -0
- zrb/llm/app/layout.py +117 -0
- zrb/llm/app/lexer.py +155 -0
- zrb/llm/app/redirection.py +28 -0
- zrb/llm/app/style.py +16 -0
- zrb/llm/app/ui.py +733 -0
- zrb/llm/config/__init__.py +4 -0
- zrb/llm/config/config.py +122 -0
- zrb/llm/config/limiter.py +247 -0
- zrb/llm/history_manager/__init__.py +4 -0
- zrb/llm/history_manager/any_history_manager.py +23 -0
- zrb/llm/history_manager/file_history_manager.py +91 -0
- zrb/llm/history_processor/summarizer.py +108 -0
- zrb/llm/note/__init__.py +3 -0
- zrb/llm/note/manager.py +122 -0
- zrb/llm/prompt/__init__.py +29 -0
- zrb/llm/prompt/claude_compatibility.py +92 -0
- zrb/llm/prompt/compose.py +55 -0
- zrb/llm/prompt/default.py +51 -0
- zrb/llm/prompt/markdown/file_extractor.md +112 -0
- zrb/llm/prompt/markdown/mandate.md +23 -0
- zrb/llm/prompt/markdown/persona.md +3 -0
- zrb/llm/prompt/markdown/repo_extractor.md +112 -0
- zrb/llm/prompt/markdown/repo_summarizer.md +29 -0
- zrb/llm/prompt/markdown/summarizer.md +21 -0
- zrb/llm/prompt/note.py +41 -0
- zrb/llm/prompt/system_context.py +46 -0
- zrb/llm/prompt/zrb.py +41 -0
- zrb/llm/skill/__init__.py +3 -0
- zrb/llm/skill/manager.py +86 -0
- zrb/llm/task/__init__.py +4 -0
- zrb/llm/task/llm_chat_task.py +316 -0
- zrb/llm/task/llm_task.py +245 -0
- zrb/llm/tool/__init__.py +39 -0
- zrb/llm/tool/bash.py +75 -0
- zrb/llm/tool/code.py +266 -0
- zrb/llm/tool/file.py +419 -0
- zrb/llm/tool/note.py +70 -0
- zrb/{builtin/llm → llm}/tool/rag.py +33 -37
- zrb/llm/tool/search/brave.py +53 -0
- zrb/llm/tool/search/searxng.py +47 -0
- zrb/llm/tool/search/serpapi.py +47 -0
- zrb/llm/tool/skill.py +19 -0
- zrb/llm/tool/sub_agent.py +70 -0
- zrb/llm/tool/web.py +97 -0
- zrb/llm/tool/zrb_task.py +66 -0
- zrb/llm/util/attachment.py +101 -0
- zrb/llm/util/prompt.py +104 -0
- zrb/llm/util/stream_response.py +178 -0
- 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 -9
- zrb/session/session.py +38 -17
- zrb/task/any_task.py +24 -3
- zrb/task/base/context.py +42 -22
- zrb/task/base/execution.py +67 -55
- zrb/task/base/lifecycle.py +14 -7
- zrb/task/base/monitoring.py +12 -7
- zrb/task/base_task.py +113 -50
- zrb/task/base_trigger.py +16 -6
- zrb/task/cmd_task.py +6 -0
- zrb/task/http_check.py +11 -5
- zrb/task/make_task.py +5 -3
- zrb/task/rsync_task.py +30 -10
- zrb/task/scaffolder.py +7 -4
- zrb/task/scheduler.py +7 -4
- zrb/task/tcp_check.py +6 -4
- zrb/util/ascii_art/art/bee.txt +17 -0
- zrb/util/ascii_art/art/cat.txt +9 -0
- zrb/util/ascii_art/art/ghost.txt +16 -0
- zrb/util/ascii_art/art/panda.txt +17 -0
- zrb/util/ascii_art/art/rose.txt +14 -0
- zrb/util/ascii_art/art/unicorn.txt +15 -0
- zrb/util/ascii_art/banner.py +92 -0
- zrb/util/attr.py +54 -39
- zrb/util/cli/markdown.py +32 -0
- zrb/util/cli/text.py +30 -0
- zrb/util/cmd/command.py +33 -10
- zrb/util/file.py +61 -33
- zrb/util/git.py +2 -2
- zrb/util/{llm/prompt.py → markdown.py} +2 -3
- zrb/util/match.py +78 -0
- zrb/util/run.py +3 -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-2.0.0a4.dist-info}/METADATA +41 -27
- {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/RECORD +129 -131
- {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/WHEEL +1 -1
- zrb/attr/__init__.py +0 -0
- zrb/builtin/llm/chat_session.py +0 -311
- zrb/builtin/llm/history.py +0 -71
- zrb/builtin/llm/input.py +0 -27
- zrb/builtin/llm/llm_ask.py +0 -187
- zrb/builtin/llm/previous-session.js +0 -21
- zrb/builtin/llm/tool/__init__.py +0 -0
- zrb/builtin/llm/tool/api.py +0 -71
- zrb/builtin/llm/tool/cli.py +0 -38
- zrb/builtin/llm/tool/code.py +0 -254
- zrb/builtin/llm/tool/file.py +0 -626
- zrb/builtin/llm/tool/sub_agent.py +0 -137
- zrb/builtin/llm/tool/web.py +0 -195
- zrb/builtin/project/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
- zrb/builtin/project/create/__init__.py +0 -0
- zrb/builtin/shell/__init__.py +0 -0
- zrb/builtin/shell/autocomplete/__init__.py +0 -0
- zrb/callback/__init__.py +0 -0
- zrb/cmd/__init__.py +0 -0
- zrb/config/default_prompt/file_extractor_system_prompt.md +0 -12
- zrb/config/default_prompt/interactive_system_prompt.md +0 -35
- zrb/config/default_prompt/persona.md +0 -1
- zrb/config/default_prompt/repo_extractor_system_prompt.md +0 -112
- zrb/config/default_prompt/repo_summarizer_system_prompt.md +0 -10
- zrb/config/default_prompt/summarization_prompt.md +0 -16
- zrb/config/default_prompt/system_prompt.md +0 -32
- zrb/config/llm_config.py +0 -243
- zrb/config/llm_context/config.py +0 -129
- zrb/config/llm_context/config_parser.py +0 -46
- zrb/config/llm_rate_limitter.py +0 -137
- zrb/content_transformer/__init__.py +0 -0
- zrb/context/__init__.py +0 -0
- zrb/dot_dict/__init__.py +0 -0
- zrb/env/__init__.py +0 -0
- zrb/group/__init__.py +0 -0
- zrb/input/__init__.py +0 -0
- zrb/runner/__init__.py +0 -0
- zrb/runner/web_route/__init__.py +0 -0
- zrb/runner/web_route/home_page/__init__.py +0 -0
- zrb/session/__init__.py +0 -0
- zrb/session_state_log/__init__.py +0 -0
- zrb/session_state_logger/__init__.py +0 -0
- zrb/task/__init__.py +0 -0
- zrb/task/base/__init__.py +0 -0
- zrb/task/llm/__init__.py +0 -0
- zrb/task/llm/agent.py +0 -243
- zrb/task/llm/config.py +0 -103
- zrb/task/llm/conversation_history.py +0 -128
- zrb/task/llm/conversation_history_model.py +0 -242
- 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/task/llm/error.py +0 -95
- zrb/task/llm/history_summarization.py +0 -216
- zrb/task/llm/print_node.py +0 -101
- zrb/task/llm/prompt.py +0 -325
- zrb/task/llm/tool_wrapper.py +0 -220
- zrb/task/llm/typing.py +0 -3
- zrb/task/llm_task.py +0 -341
- zrb/task_status/__init__.py +0 -0
- zrb/util/__init__.py +0 -0
- zrb/util/cli/__init__.py +0 -0
- zrb/util/cmd/__init__.py +0 -0
- zrb/util/codemod/__init__.py +0 -0
- zrb/util/string/__init__.py +0 -0
- zrb/xcom/__init__.py +0 -0
- {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import os
|
|
3
|
-
from collections.abc import Callable
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from zrb.config.llm_context.config import llm_context_config
|
|
7
|
-
from zrb.context.any_context import AnyContext
|
|
8
|
-
from zrb.task.llm.typing import ListOfDict
|
|
9
|
-
from zrb.util.file import read_file
|
|
10
|
-
from zrb.util.llm.prompt import make_prompt_section
|
|
11
|
-
from zrb.util.run import run_async
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class ConversationHistory:
|
|
15
|
-
|
|
16
|
-
def __init__(
|
|
17
|
-
self,
|
|
18
|
-
past_conversation_summary: str = "",
|
|
19
|
-
past_conversation_transcript: str = "",
|
|
20
|
-
history: ListOfDict | None = None,
|
|
21
|
-
contextual_note: str | None = None,
|
|
22
|
-
long_term_note: str | None = None,
|
|
23
|
-
project_path: str | None = None,
|
|
24
|
-
):
|
|
25
|
-
self.past_conversation_transcript = past_conversation_transcript
|
|
26
|
-
self.past_conversation_summary = past_conversation_summary
|
|
27
|
-
self.history = history if history is not None else []
|
|
28
|
-
self.contextual_note = contextual_note if contextual_note is not None else ""
|
|
29
|
-
self.long_term_note = long_term_note if long_term_note is not None else ""
|
|
30
|
-
self.project_path = project_path if project_path is not None else os.getcwd()
|
|
31
|
-
|
|
32
|
-
def to_dict(self) -> dict[str, Any]:
|
|
33
|
-
return {
|
|
34
|
-
"past_conversation_summary": self.past_conversation_summary,
|
|
35
|
-
"past_conversation_transcript": self.past_conversation_transcript,
|
|
36
|
-
"history": self.history,
|
|
37
|
-
"contextual_note": self.contextual_note,
|
|
38
|
-
"long_term_note": self.long_term_note,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
def model_dump_json(self, indent: int = 2) -> str:
|
|
42
|
-
return json.dumps(self.to_dict(), indent=indent)
|
|
43
|
-
|
|
44
|
-
@classmethod
|
|
45
|
-
async def read_from_source(
|
|
46
|
-
cls,
|
|
47
|
-
ctx: AnyContext,
|
|
48
|
-
reader: Callable[[AnyContext], dict[str, Any] | list | None] | None,
|
|
49
|
-
file_path: str | None,
|
|
50
|
-
) -> "ConversationHistory | None":
|
|
51
|
-
# Priority 1: Reader function
|
|
52
|
-
if reader:
|
|
53
|
-
try:
|
|
54
|
-
raw_data = await run_async(reader(ctx))
|
|
55
|
-
if raw_data:
|
|
56
|
-
instance = cls.parse_and_validate(ctx, raw_data, "reader")
|
|
57
|
-
if instance:
|
|
58
|
-
return instance
|
|
59
|
-
except Exception as e:
|
|
60
|
-
ctx.log_warning(
|
|
61
|
-
f"Error executing conversation history reader: {e}. Ignoring."
|
|
62
|
-
)
|
|
63
|
-
# Priority 2: History file
|
|
64
|
-
if file_path and os.path.isfile(file_path):
|
|
65
|
-
try:
|
|
66
|
-
content = read_file(file_path)
|
|
67
|
-
raw_data = json.loads(content)
|
|
68
|
-
instance = cls.parse_and_validate(ctx, raw_data, f"file '{file_path}'")
|
|
69
|
-
if instance:
|
|
70
|
-
return instance
|
|
71
|
-
except json.JSONDecodeError:
|
|
72
|
-
ctx.log_warning(
|
|
73
|
-
f"Could not decode JSON from history file '{file_path}'. "
|
|
74
|
-
"Ignoring file content."
|
|
75
|
-
)
|
|
76
|
-
except Exception as e:
|
|
77
|
-
ctx.log_warning(
|
|
78
|
-
f"Error reading history file '{file_path}': {e}. "
|
|
79
|
-
"Ignoring file content."
|
|
80
|
-
)
|
|
81
|
-
# Fallback: Return default value
|
|
82
|
-
return None
|
|
83
|
-
|
|
84
|
-
def fetch_newest_notes(self):
|
|
85
|
-
self._fetch_long_term_note()
|
|
86
|
-
self._fetch_contextual_note()
|
|
87
|
-
|
|
88
|
-
@classmethod
|
|
89
|
-
def parse_and_validate(
|
|
90
|
-
cls, ctx: AnyContext, data: Any, source: str
|
|
91
|
-
) -> "ConversationHistory":
|
|
92
|
-
try:
|
|
93
|
-
if isinstance(data, cls):
|
|
94
|
-
return data # Already a valid instance
|
|
95
|
-
if isinstance(data, dict):
|
|
96
|
-
# This handles both the new format and the old {'context': ..., 'history': ...}
|
|
97
|
-
return cls(
|
|
98
|
-
past_conversation_summary=data.get("past_conversation_summary", ""),
|
|
99
|
-
past_conversation_transcript=data.get(
|
|
100
|
-
"past_conversation_transcript", ""
|
|
101
|
-
),
|
|
102
|
-
history=data.get("history", data.get("messages", [])),
|
|
103
|
-
contextual_note=data.get("contextual_note", ""),
|
|
104
|
-
long_term_note=data.get("long_term_note", ""),
|
|
105
|
-
)
|
|
106
|
-
elif isinstance(data, list):
|
|
107
|
-
# Handle very old format (just a list) - wrap it
|
|
108
|
-
ctx.log_warning(
|
|
109
|
-
f"History from {source} contains legacy list format. "
|
|
110
|
-
"Wrapping it into the new structure. "
|
|
111
|
-
"Consider updating the source format."
|
|
112
|
-
)
|
|
113
|
-
return cls(history=data)
|
|
114
|
-
else:
|
|
115
|
-
ctx.log_warning(
|
|
116
|
-
f"History data from {source} has unexpected format "
|
|
117
|
-
f"(type: {type(data)}). Ignoring."
|
|
118
|
-
)
|
|
119
|
-
except Exception as e: # Catch validation errors too
|
|
120
|
-
ctx.log_warning(
|
|
121
|
-
f"Error validating/parsing history data from {source}: {e}. Ignoring."
|
|
122
|
-
)
|
|
123
|
-
return cls()
|
|
124
|
-
|
|
125
|
-
def write_past_conversation_summary(self, past_conversation_summary: str):
|
|
126
|
-
"""
|
|
127
|
-
Write or update the past conversation summary.
|
|
128
|
-
|
|
129
|
-
Use this tool to store or update a summary of previous conversations for
|
|
130
|
-
future reference. This is useful for providing context to LLMs or other tools
|
|
131
|
-
that need a concise history.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
past_conversation_summary (str): The summary text to store.
|
|
135
|
-
|
|
136
|
-
Returns:
|
|
137
|
-
str: A JSON object indicating the success or failure of the operation.
|
|
138
|
-
|
|
139
|
-
Raises:
|
|
140
|
-
Exception: If the summary cannot be written.
|
|
141
|
-
"""
|
|
142
|
-
self.past_conversation_summary = past_conversation_summary
|
|
143
|
-
return json.dumps({"success": True})
|
|
144
|
-
|
|
145
|
-
def write_past_conversation_transcript(self, past_conversation_transcript: str):
|
|
146
|
-
"""
|
|
147
|
-
Write or update the past conversation transcript.
|
|
148
|
-
|
|
149
|
-
Use this tool to store or update the full transcript of previous conversations.
|
|
150
|
-
This is useful for providing detailed context to LLMs or for record-keeping.
|
|
151
|
-
|
|
152
|
-
Args:
|
|
153
|
-
past_conversation_transcript (str): The transcript text to store.
|
|
154
|
-
|
|
155
|
-
Returns:
|
|
156
|
-
str: A JSON object indicating the success or failure of the operation.
|
|
157
|
-
|
|
158
|
-
Raises:
|
|
159
|
-
Exception: If the transcript cannot be written.
|
|
160
|
-
"""
|
|
161
|
-
self.past_conversation_transcript = past_conversation_transcript
|
|
162
|
-
return json.dumps({"success": True})
|
|
163
|
-
|
|
164
|
-
def read_long_term_note(self) -> str:
|
|
165
|
-
"""
|
|
166
|
-
Read the content of the long-term references.
|
|
167
|
-
|
|
168
|
-
This tool helps you retrieve knowledge or notes stored for long-term reference.
|
|
169
|
-
If the note does not exist, you may want to create it using the write tool.
|
|
170
|
-
|
|
171
|
-
Returns:
|
|
172
|
-
str: JSON with content of the notes.
|
|
173
|
-
|
|
174
|
-
Raises:
|
|
175
|
-
Exception: If the note cannot be read.
|
|
176
|
-
"""
|
|
177
|
-
return json.dumps({"content": self._fetch_long_term_note()})
|
|
178
|
-
|
|
179
|
-
def write_long_term_note(self, content: str) -> str:
|
|
180
|
-
"""
|
|
181
|
-
Write the entire content of the long-term references.
|
|
182
|
-
This will overwrite any existing long-term notes.
|
|
183
|
-
|
|
184
|
-
Args:
|
|
185
|
-
content (str): The full content of the long-term notes.
|
|
186
|
-
|
|
187
|
-
Returns:
|
|
188
|
-
str: JSON indicating success.
|
|
189
|
-
"""
|
|
190
|
-
llm_context_config.write_context(content, context_path="/")
|
|
191
|
-
return json.dumps({"success": True})
|
|
192
|
-
|
|
193
|
-
def read_contextual_note(self) -> str:
|
|
194
|
-
"""
|
|
195
|
-
Read the content of the contextual references for the current project.
|
|
196
|
-
|
|
197
|
-
This tool helps you retrieve knowledge or notes stored for contextual reference.
|
|
198
|
-
If the note does not exist, you may want to create it using the write tool.
|
|
199
|
-
|
|
200
|
-
Returns:
|
|
201
|
-
str: JSON with content of the notes.
|
|
202
|
-
|
|
203
|
-
Raises:
|
|
204
|
-
Exception: If the note cannot be read.
|
|
205
|
-
"""
|
|
206
|
-
return json.dumps({"content": self._fetch_contextual_note()})
|
|
207
|
-
|
|
208
|
-
def write_contextual_note(
|
|
209
|
-
self, content: str, context_path: str | None = None
|
|
210
|
-
) -> str:
|
|
211
|
-
"""
|
|
212
|
-
Write the entire content of the contextual references for a specific path.
|
|
213
|
-
This will overwrite any existing contextual notes for that path.
|
|
214
|
-
|
|
215
|
-
Args:
|
|
216
|
-
content (str): The full content of the contextual notes.
|
|
217
|
-
context_path (str, optional): The directory path for the context.
|
|
218
|
-
Defaults to the current project path.
|
|
219
|
-
|
|
220
|
-
Returns:
|
|
221
|
-
str: JSON indicating success.
|
|
222
|
-
"""
|
|
223
|
-
if context_path is None:
|
|
224
|
-
context_path = self.project_path
|
|
225
|
-
llm_context_config.write_context(content, context_path=context_path)
|
|
226
|
-
return json.dumps({"success": True})
|
|
227
|
-
|
|
228
|
-
def _fetch_long_term_note(self):
|
|
229
|
-
contexts = llm_context_config.get_contexts(cwd=self.project_path)
|
|
230
|
-
self.long_term_note = contexts.get("/", "")
|
|
231
|
-
return self.long_term_note
|
|
232
|
-
|
|
233
|
-
def _fetch_contextual_note(self):
|
|
234
|
-
contexts = llm_context_config.get_contexts(cwd=self.project_path)
|
|
235
|
-
self.contextual_note = "\n".join(
|
|
236
|
-
[
|
|
237
|
-
make_prompt_section(header, content)
|
|
238
|
-
for header, content in contexts.items()
|
|
239
|
-
if header != "/"
|
|
240
|
-
]
|
|
241
|
-
)
|
|
242
|
-
return self.contextual_note
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
When the user's request involves writing or modifying code, you MUST follow these domain-specific rules in addition to your core workflow.
|
|
2
|
-
|
|
3
|
-
## 1. Critical Prohibitions
|
|
4
|
-
- **NEVER Assume Dependencies:** You MUST NOT use a library, framework, or package unless you have first verified it is an existing project dependency (e.g., in `package.json`, `requirements.txt`, `pyproject.toml`, etc.).
|
|
5
|
-
- **NEVER Commit Without Verification:** You MUST NOT use `git commit` until you have staged the changes and run the project's own verification steps (tests, linter, build).
|
|
6
|
-
|
|
7
|
-
## 2. Code Development Workflow
|
|
8
|
-
This expands on your core "Execute and Verify" loop with steps specific to coding.
|
|
9
|
-
|
|
10
|
-
1. **CRITICAL: Gather Context First:** Before writing or modifying any code, you MUST gather context to ensure your changes are idiomatic and correct.
|
|
11
|
-
* **Project Structure & Dependencies:** Check for `README.md`, `package.json`, etc., to understand the project's scripts (lint, test, build).
|
|
12
|
-
* **Code Style & Conventions:** Look for `.eslintrc`, `.prettierrc`, `ruff.toml`, etc. Analyze surrounding source files to determine naming conventions, typing style, error handling, and architectural patterns.
|
|
13
|
-
* **For new tests:** You MUST read the full source code of the module(s) you are testing.
|
|
14
|
-
* **For new features:** You MUST look for existing tests and related modules to understand conventions.
|
|
15
|
-
|
|
16
|
-
2. **Implement Idiomatically:** Make the changes, strictly adhering to the patterns and conventions discovered in the context-gathering phase.
|
|
17
|
-
|
|
18
|
-
3. **CRITICAL: Design for Testability:** Your primary goal is to produce code that is easy to test automatically.
|
|
19
|
-
* **Prefer `return` over `print`:** Core logic functions MUST `return` values. I/O operations like `print()` should be separated into different functions.
|
|
20
|
-
* **Embrace Modularity:** Decompose complex tasks into smaller, single-responsibility functions or classes.
|
|
21
|
-
* **Use Function Arguments:** Avoid relying on global state. Pass necessary data into functions as arguments.
|
|
22
|
-
|
|
23
|
-
4. **Verify with Project Tooling:** After implementation, run all relevant project-specific commands (e.g., `npm run test`, `pytest`, `npm run lint`). This is the verification step for code.
|
|
24
|
-
* **CRITICAL:** If any verification step fails, you MUST enter your standard Debugging Loop. You are responsible for fixing the code until all project-specific verifications pass. Do not stop until the code is working correctly.
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
When the user's request involves creating, refining, or organizing textual content, you MUST follow these domain-specific rules in addition to your core workflow.
|
|
2
|
-
|
|
3
|
-
## 1. Core Principles
|
|
4
|
-
- **Audience and Tone:** Before writing, always consider the intended audience and the desired tone (e.g., formal, casual, technical, persuasive). If it's not specified, default to a professional and helpful tone.
|
|
5
|
-
- **Structure and Clarity:** Organize content logically. Use headings, bullet points, and bold text to improve readability. Start with a clear topic sentence and build on it.
|
|
6
|
-
- **Originality and Idiom:** Do not plagiarize. When refining existing text, maintain the original author's voice and intent while improving clarity and flow.
|
|
7
|
-
|
|
8
|
-
## 2. Content Creation Workflow
|
|
9
|
-
1. **Clarify the Goal:** If the user's request is vague (e.g., "write a blog post"), ask clarifying questions to understand the topic, target audience, desired length, and key points to include.
|
|
10
|
-
2. **Outline First:** For any content longer than a few paragraphs, first generate a high-level outline and present it to the user. This ensures you are on the right track before generating the full text.
|
|
11
|
-
3. **Draft and Refine:** Write the full content based on the approved outline. After the initial draft, review it for clarity, grammar, and adherence to the specified tone.
|
|
12
|
-
4. **Verification:** Read back the final content to the user. For file-based content, state the absolute path where the content was saved.
|
|
13
|
-
|
|
14
|
-
## 3. Specific Task Guidelines
|
|
15
|
-
- **Summarization:** Identify the main arguments, key findings, and conclusions. Do not inject your own opinions. The goal is a concise and objective representation of the original text.
|
|
16
|
-
- **Translation:** Perform a direct translation, then review it to ensure the phrasing is natural and idiomatic in the target language.
|
|
17
|
-
- **Proofreading:** Correct grammar, spelling, and punctuation errors. Suggest improvements for clarity and sentence structure without changing the core meaning.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
When the user's request involves finding, synthesizing, or analyzing information, you MUST follow these domain-specific rules in addition to your core workflow.
|
|
2
|
-
|
|
3
|
-
## 1. Core Principles
|
|
4
|
-
- **Objectivity:** Your primary goal is to be an unbiased synthesizer of information. Report the facts as you find them. Do not inject personal opinions or unverified claims.
|
|
5
|
-
- **Source Reliability:** Prioritize reputable sources (e.g., official documentation, academic papers, established news organizations). Be cautious with user-generated content like forums or blogs, and if you must use them, qualify the information (e.g., "According to a user on...").
|
|
6
|
-
- **Synthesis over Recitation:** Do not simply copy-paste large blocks of text. Your value is in synthesizing information from multiple sources to provide a concise, coherent answer.
|
|
7
|
-
|
|
8
|
-
## 2. Research Workflow
|
|
9
|
-
1. **Deconstruct the Request:** Break down the user's query into key questions and search terms.
|
|
10
|
-
2. **Execute Searches:** Use your web search tools to find relevant information. If initial searches fail, try alternative keywords and phrasing.
|
|
11
|
-
3. **Synthesize Findings:** Read and analyze the search results. Identify the most relevant facts, key arguments, and differing viewpoints.
|
|
12
|
-
4. **Formulate the Answer:** Structure the answer logically. Start with a direct answer to the user's primary question, then provide supporting details, context, and sources.
|
|
13
|
-
5. **Cite Your Sources:** For every key fact or claim, you MUST cite the source (e.g., "According to [Source Name](URL), ...").
|
|
14
|
-
|
|
15
|
-
## 3. Specific Task Guidelines
|
|
16
|
-
- **Summarization:** Extract the main thesis, key arguments, and conclusions from the provided text or URL. The summary must be a concise and accurate representation of the original content.
|
|
17
|
-
- **Comparison:** When asked to compare two or more things, create a structured comparison (e.g., using a table or bullet points) that clearly outlines the key similarities and differences.
|
|
18
|
-
- **Data Analysis:** When working with data (e.g., from a CSV file), state your findings clearly and concisely. If you generate a chart or graph, explain what the visualization shows.
|
zrb/task/llm/error.py
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import TYPE_CHECKING, Any, Optional
|
|
3
|
-
|
|
4
|
-
if TYPE_CHECKING:
|
|
5
|
-
from openai import APIError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# Define a structured error model for tool execution failures
|
|
9
|
-
class ToolExecutionError:
|
|
10
|
-
def __init__(
|
|
11
|
-
self,
|
|
12
|
-
tool_name: str,
|
|
13
|
-
error_type: str,
|
|
14
|
-
message: str,
|
|
15
|
-
details: Optional[str] = None,
|
|
16
|
-
):
|
|
17
|
-
self.tool_name = tool_name
|
|
18
|
-
self.error_type = error_type
|
|
19
|
-
self.message = message
|
|
20
|
-
self.details = details
|
|
21
|
-
|
|
22
|
-
def to_dict(self) -> dict[str, Any]:
|
|
23
|
-
return {
|
|
24
|
-
"tool_name": self.tool_name,
|
|
25
|
-
"error_type": self.error_type,
|
|
26
|
-
"message": self.message,
|
|
27
|
-
"details": self.details,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
def model_dump_json(self, indent: int = 2) -> str:
|
|
31
|
-
return json.dumps(self.to_dict(), indent=indent)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def extract_api_error_details(error: "APIError") -> str:
|
|
35
|
-
"""Extract detailed error information from an APIError."""
|
|
36
|
-
details = f"{error.message}"
|
|
37
|
-
# Try to parse the error body as JSON
|
|
38
|
-
if error.body:
|
|
39
|
-
try:
|
|
40
|
-
if isinstance(error.body, str):
|
|
41
|
-
body_json = json.loads(error.body)
|
|
42
|
-
elif isinstance(error.body, bytes):
|
|
43
|
-
body_json = json.loads(error.body.decode("utf-8"))
|
|
44
|
-
else:
|
|
45
|
-
body_json = error.body
|
|
46
|
-
# Extract error details from the JSON structure
|
|
47
|
-
if isinstance(body_json, dict):
|
|
48
|
-
if "error" in body_json:
|
|
49
|
-
error_obj = body_json["error"]
|
|
50
|
-
if isinstance(error_obj, dict):
|
|
51
|
-
if "message" in error_obj:
|
|
52
|
-
details += f"\nProvider message: {error_obj['message']}"
|
|
53
|
-
if "code" in error_obj:
|
|
54
|
-
details += f"\nError code: {error_obj['code']}"
|
|
55
|
-
if "status" in error_obj:
|
|
56
|
-
details += f"\nStatus: {error_obj['status']}"
|
|
57
|
-
# Check for metadata that might contain provider-specific information
|
|
58
|
-
if "metadata" in body_json and isinstance(body_json["metadata"], dict):
|
|
59
|
-
metadata = body_json["metadata"]
|
|
60
|
-
if "provider_name" in metadata:
|
|
61
|
-
details += f"\nProvider: {metadata['provider_name']}"
|
|
62
|
-
if "raw" in metadata:
|
|
63
|
-
try:
|
|
64
|
-
raw_json = json.loads(metadata["raw"])
|
|
65
|
-
if "error" in raw_json and isinstance(
|
|
66
|
-
raw_json["error"], dict
|
|
67
|
-
):
|
|
68
|
-
raw_error = raw_json["error"]
|
|
69
|
-
if "message" in raw_error:
|
|
70
|
-
details += (
|
|
71
|
-
f"\nRaw error message: {raw_error['message']}"
|
|
72
|
-
)
|
|
73
|
-
except (KeyError, TypeError, ValueError):
|
|
74
|
-
# If we can't parse the raw JSON, just include it as is
|
|
75
|
-
details += f"\nRaw error data: {metadata['raw']}"
|
|
76
|
-
except json.JSONDecodeError:
|
|
77
|
-
# If we can't parse the JSON, include the raw body
|
|
78
|
-
details += f"\nRaw error body: {error.body}"
|
|
79
|
-
except Exception as e:
|
|
80
|
-
# Catch any other exceptions during parsing
|
|
81
|
-
details += f"\nError parsing error body: {str(e)}"
|
|
82
|
-
# Include request information if available
|
|
83
|
-
if hasattr(error, "request") and error.request:
|
|
84
|
-
if hasattr(error.request, "method") and hasattr(error.request, "url"):
|
|
85
|
-
details += f"\nRequest: {error.request.method} {error.request.url}"
|
|
86
|
-
# Include a truncated version of the request content if available
|
|
87
|
-
if hasattr(error.request, "content") and error.request.content:
|
|
88
|
-
content = error.request.content
|
|
89
|
-
if isinstance(content, bytes):
|
|
90
|
-
try:
|
|
91
|
-
content = content.decode("utf-8")
|
|
92
|
-
except UnicodeDecodeError:
|
|
93
|
-
content = str(content)
|
|
94
|
-
details += f"\nRequest content: {content}"
|
|
95
|
-
return details
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import traceback
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from zrb.attr.type import BoolAttr, IntAttr
|
|
6
|
-
from zrb.config.llm_config import llm_config
|
|
7
|
-
from zrb.config.llm_rate_limitter import LLMRateLimiter, llm_rate_limitter
|
|
8
|
-
from zrb.context.any_context import AnyContext
|
|
9
|
-
from zrb.task.llm.agent import run_agent_iteration
|
|
10
|
-
from zrb.task.llm.conversation_history import (
|
|
11
|
-
count_part_in_history_list,
|
|
12
|
-
replace_system_prompt_in_history,
|
|
13
|
-
)
|
|
14
|
-
from zrb.task.llm.conversation_history_model import ConversationHistory
|
|
15
|
-
from zrb.task.llm.typing import ListOfDict
|
|
16
|
-
from zrb.util.attr import get_bool_attr, get_int_attr
|
|
17
|
-
from zrb.util.cli.style import stylize_faint
|
|
18
|
-
from zrb.util.llm.prompt import make_prompt_section
|
|
19
|
-
|
|
20
|
-
if TYPE_CHECKING:
|
|
21
|
-
from pydantic_ai.models import Model
|
|
22
|
-
from pydantic_ai.settings import ModelSettings
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _count_token_in_history(history_list: ListOfDict) -> int:
|
|
26
|
-
"""Counts the total number of tokens in a conversation history list."""
|
|
27
|
-
text_to_count = json.dumps(history_list)
|
|
28
|
-
return llm_rate_limitter.count_token(text_to_count)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def get_history_summarization_token_threshold(
|
|
32
|
-
ctx: AnyContext,
|
|
33
|
-
history_summarization_token_threshold_attr: IntAttr | None,
|
|
34
|
-
render_history_summarization_token_threshold: bool,
|
|
35
|
-
) -> int:
|
|
36
|
-
"""Gets the history summarization token threshold, handling defaults and errors."""
|
|
37
|
-
try:
|
|
38
|
-
return get_int_attr(
|
|
39
|
-
ctx,
|
|
40
|
-
history_summarization_token_threshold_attr,
|
|
41
|
-
llm_config.default_history_summarization_token_threshold,
|
|
42
|
-
auto_render=render_history_summarization_token_threshold,
|
|
43
|
-
)
|
|
44
|
-
except ValueError as e:
|
|
45
|
-
ctx.log_warning(
|
|
46
|
-
f"Could not convert history_summarization_token_threshold to int: {e}. "
|
|
47
|
-
"Defaulting to -1 (no threshold)."
|
|
48
|
-
)
|
|
49
|
-
return -1
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def should_summarize_history(
|
|
53
|
-
ctx: AnyContext,
|
|
54
|
-
history_list: ListOfDict,
|
|
55
|
-
should_summarize_history_attr: BoolAttr | None,
|
|
56
|
-
render_summarize_history: bool,
|
|
57
|
-
history_summarization_token_threshold_attr: IntAttr | None,
|
|
58
|
-
render_history_summarization_token_threshold: bool,
|
|
59
|
-
) -> bool:
|
|
60
|
-
"""Determines if history summarization should occur based on token length and config."""
|
|
61
|
-
history_part_count = count_part_in_history_list(history_list)
|
|
62
|
-
if history_part_count == 0:
|
|
63
|
-
return False
|
|
64
|
-
summarization_token_threshold = get_history_summarization_token_threshold(
|
|
65
|
-
ctx,
|
|
66
|
-
history_summarization_token_threshold_attr,
|
|
67
|
-
render_history_summarization_token_threshold,
|
|
68
|
-
)
|
|
69
|
-
history_token_count = _count_token_in_history(history_list)
|
|
70
|
-
if (
|
|
71
|
-
summarization_token_threshold == -1
|
|
72
|
-
or summarization_token_threshold > history_token_count
|
|
73
|
-
):
|
|
74
|
-
return False
|
|
75
|
-
return get_bool_attr(
|
|
76
|
-
ctx,
|
|
77
|
-
should_summarize_history_attr,
|
|
78
|
-
llm_config.default_summarize_history,
|
|
79
|
-
auto_render=render_summarize_history,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
async def summarize_history(
|
|
84
|
-
ctx: AnyContext,
|
|
85
|
-
model: "Model | str | None",
|
|
86
|
-
settings: "ModelSettings | None",
|
|
87
|
-
system_prompt: str,
|
|
88
|
-
conversation_history: ConversationHistory,
|
|
89
|
-
rate_limitter: LLMRateLimiter | None = None,
|
|
90
|
-
retries: int = 3,
|
|
91
|
-
) -> str:
|
|
92
|
-
"""Runs an LLM call to update the conversation summary."""
|
|
93
|
-
from pydantic_ai import Agent
|
|
94
|
-
|
|
95
|
-
ctx.log_info("Attempting to summarize conversation history...")
|
|
96
|
-
# Construct the user prompt for the summarization agent
|
|
97
|
-
user_prompt = "\n".join(
|
|
98
|
-
[
|
|
99
|
-
make_prompt_section(
|
|
100
|
-
"Past Conversation",
|
|
101
|
-
"\n".join(
|
|
102
|
-
[
|
|
103
|
-
make_prompt_section(
|
|
104
|
-
"Summary",
|
|
105
|
-
conversation_history.past_conversation_summary,
|
|
106
|
-
as_code=True,
|
|
107
|
-
),
|
|
108
|
-
make_prompt_section(
|
|
109
|
-
"Last Transcript",
|
|
110
|
-
conversation_history.past_conversation_transcript,
|
|
111
|
-
as_code=True,
|
|
112
|
-
),
|
|
113
|
-
]
|
|
114
|
-
),
|
|
115
|
-
),
|
|
116
|
-
make_prompt_section(
|
|
117
|
-
"Recent Conversation (JSON)",
|
|
118
|
-
json.dumps(conversation_history.history),
|
|
119
|
-
as_code=True,
|
|
120
|
-
),
|
|
121
|
-
make_prompt_section(
|
|
122
|
-
"Notes",
|
|
123
|
-
"\n".join(
|
|
124
|
-
[
|
|
125
|
-
make_prompt_section(
|
|
126
|
-
"Long Term",
|
|
127
|
-
conversation_history.long_term_note,
|
|
128
|
-
as_code=True,
|
|
129
|
-
),
|
|
130
|
-
make_prompt_section(
|
|
131
|
-
"Contextual",
|
|
132
|
-
conversation_history.contextual_note,
|
|
133
|
-
as_code=True,
|
|
134
|
-
),
|
|
135
|
-
]
|
|
136
|
-
),
|
|
137
|
-
),
|
|
138
|
-
]
|
|
139
|
-
)
|
|
140
|
-
summarization_agent = Agent(
|
|
141
|
-
model=model,
|
|
142
|
-
system_prompt=system_prompt,
|
|
143
|
-
model_settings=settings,
|
|
144
|
-
retries=retries,
|
|
145
|
-
tools=[
|
|
146
|
-
conversation_history.write_past_conversation_summary,
|
|
147
|
-
conversation_history.write_past_conversation_transcript,
|
|
148
|
-
conversation_history.read_long_term_note,
|
|
149
|
-
conversation_history.write_long_term_note,
|
|
150
|
-
conversation_history.read_contextual_note,
|
|
151
|
-
conversation_history.write_contextual_note,
|
|
152
|
-
],
|
|
153
|
-
)
|
|
154
|
-
try:
|
|
155
|
-
ctx.print(stylize_faint("📝 Summarize Conversation >>>"), plain=True)
|
|
156
|
-
summary_run = await run_agent_iteration(
|
|
157
|
-
ctx=ctx,
|
|
158
|
-
agent=summarization_agent,
|
|
159
|
-
user_prompt=user_prompt,
|
|
160
|
-
attachments=[],
|
|
161
|
-
history_list=[],
|
|
162
|
-
rate_limitter=rate_limitter,
|
|
163
|
-
)
|
|
164
|
-
if summary_run and summary_run.result and summary_run.result.output:
|
|
165
|
-
usage = summary_run.result.usage()
|
|
166
|
-
ctx.print(stylize_faint(f"📝 Summarization Token: {usage}"), plain=True)
|
|
167
|
-
ctx.print(plain=True)
|
|
168
|
-
ctx.log_info("History summarized and updated.")
|
|
169
|
-
else:
|
|
170
|
-
ctx.log_warning("History summarization failed or returned no data.")
|
|
171
|
-
except BaseException as e:
|
|
172
|
-
ctx.log_warning(f"Error during history summarization: {e}")
|
|
173
|
-
traceback.print_exc()
|
|
174
|
-
# Return the original summary if summarization fails
|
|
175
|
-
return conversation_history
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
async def maybe_summarize_history(
|
|
179
|
-
ctx: AnyContext,
|
|
180
|
-
conversation_history: ConversationHistory,
|
|
181
|
-
should_summarize_history_attr: BoolAttr | None,
|
|
182
|
-
render_summarize_history: bool,
|
|
183
|
-
history_summarization_token_threshold_attr: IntAttr | None,
|
|
184
|
-
render_history_summarization_token_threshold: bool,
|
|
185
|
-
model: "str | Model | None",
|
|
186
|
-
model_settings: "ModelSettings | None",
|
|
187
|
-
summarization_prompt: str,
|
|
188
|
-
rate_limitter: LLMRateLimiter | None = None,
|
|
189
|
-
) -> ConversationHistory:
|
|
190
|
-
"""Summarizes history and updates context if enabled and threshold met."""
|
|
191
|
-
shorten_history = replace_system_prompt_in_history(conversation_history.history)
|
|
192
|
-
if should_summarize_history(
|
|
193
|
-
ctx,
|
|
194
|
-
shorten_history,
|
|
195
|
-
should_summarize_history_attr,
|
|
196
|
-
render_summarize_history,
|
|
197
|
-
history_summarization_token_threshold_attr,
|
|
198
|
-
render_history_summarization_token_threshold,
|
|
199
|
-
):
|
|
200
|
-
original_history = conversation_history.history
|
|
201
|
-
conversation_history.history = shorten_history
|
|
202
|
-
conversation_history = await summarize_history(
|
|
203
|
-
ctx=ctx,
|
|
204
|
-
model=model,
|
|
205
|
-
settings=model_settings,
|
|
206
|
-
system_prompt=summarization_prompt,
|
|
207
|
-
conversation_history=conversation_history,
|
|
208
|
-
rate_limitter=rate_limitter,
|
|
209
|
-
)
|
|
210
|
-
conversation_history.history = original_history
|
|
211
|
-
if (
|
|
212
|
-
conversation_history.past_conversation_summary != ""
|
|
213
|
-
and conversation_history.past_conversation_transcript != ""
|
|
214
|
-
):
|
|
215
|
-
conversation_history.history = []
|
|
216
|
-
return conversation_history
|