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.

Files changed (204) hide show
  1. zrb/__init__.py +118 -133
  2. zrb/attr/type.py +10 -7
  3. zrb/builtin/__init__.py +55 -1
  4. zrb/builtin/git.py +12 -1
  5. zrb/builtin/group.py +31 -15
  6. zrb/builtin/llm/chat.py +147 -0
  7. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
  9. zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
  10. zrb/builtin/searxng/config/settings.yml +5671 -0
  11. zrb/builtin/searxng/start.py +21 -0
  12. zrb/builtin/shell/autocomplete/bash.py +4 -3
  13. zrb/builtin/shell/autocomplete/zsh.py +4 -3
  14. zrb/callback/callback.py +8 -1
  15. zrb/cmd/cmd_result.py +2 -1
  16. zrb/config/config.py +555 -169
  17. zrb/config/helper.py +84 -0
  18. zrb/config/web_auth_config.py +50 -35
  19. zrb/context/any_shared_context.py +20 -3
  20. zrb/context/context.py +39 -5
  21. zrb/context/print_fn.py +13 -0
  22. zrb/context/shared_context.py +17 -8
  23. zrb/group/any_group.py +3 -3
  24. zrb/group/group.py +3 -3
  25. zrb/input/any_input.py +5 -1
  26. zrb/input/base_input.py +18 -6
  27. zrb/input/option_input.py +41 -1
  28. zrb/input/text_input.py +7 -24
  29. zrb/llm/agent/__init__.py +9 -0
  30. zrb/llm/agent/agent.py +215 -0
  31. zrb/llm/agent/summarizer.py +20 -0
  32. zrb/llm/app/__init__.py +10 -0
  33. zrb/llm/app/completion.py +281 -0
  34. zrb/llm/app/confirmation/allow_tool.py +66 -0
  35. zrb/llm/app/confirmation/handler.py +178 -0
  36. zrb/llm/app/confirmation/replace_confirmation.py +77 -0
  37. zrb/llm/app/keybinding.py +34 -0
  38. zrb/llm/app/layout.py +117 -0
  39. zrb/llm/app/lexer.py +155 -0
  40. zrb/llm/app/redirection.py +28 -0
  41. zrb/llm/app/style.py +16 -0
  42. zrb/llm/app/ui.py +733 -0
  43. zrb/llm/config/__init__.py +4 -0
  44. zrb/llm/config/config.py +122 -0
  45. zrb/llm/config/limiter.py +247 -0
  46. zrb/llm/history_manager/__init__.py +4 -0
  47. zrb/llm/history_manager/any_history_manager.py +23 -0
  48. zrb/llm/history_manager/file_history_manager.py +91 -0
  49. zrb/llm/history_processor/summarizer.py +108 -0
  50. zrb/llm/note/__init__.py +3 -0
  51. zrb/llm/note/manager.py +122 -0
  52. zrb/llm/prompt/__init__.py +29 -0
  53. zrb/llm/prompt/claude_compatibility.py +92 -0
  54. zrb/llm/prompt/compose.py +55 -0
  55. zrb/llm/prompt/default.py +51 -0
  56. zrb/llm/prompt/markdown/file_extractor.md +112 -0
  57. zrb/llm/prompt/markdown/mandate.md +23 -0
  58. zrb/llm/prompt/markdown/persona.md +3 -0
  59. zrb/llm/prompt/markdown/repo_extractor.md +112 -0
  60. zrb/llm/prompt/markdown/repo_summarizer.md +29 -0
  61. zrb/llm/prompt/markdown/summarizer.md +21 -0
  62. zrb/llm/prompt/note.py +41 -0
  63. zrb/llm/prompt/system_context.py +46 -0
  64. zrb/llm/prompt/zrb.py +41 -0
  65. zrb/llm/skill/__init__.py +3 -0
  66. zrb/llm/skill/manager.py +86 -0
  67. zrb/llm/task/__init__.py +4 -0
  68. zrb/llm/task/llm_chat_task.py +316 -0
  69. zrb/llm/task/llm_task.py +245 -0
  70. zrb/llm/tool/__init__.py +39 -0
  71. zrb/llm/tool/bash.py +75 -0
  72. zrb/llm/tool/code.py +266 -0
  73. zrb/llm/tool/file.py +419 -0
  74. zrb/llm/tool/note.py +70 -0
  75. zrb/{builtin/llm → llm}/tool/rag.py +33 -37
  76. zrb/llm/tool/search/brave.py +53 -0
  77. zrb/llm/tool/search/searxng.py +47 -0
  78. zrb/llm/tool/search/serpapi.py +47 -0
  79. zrb/llm/tool/skill.py +19 -0
  80. zrb/llm/tool/sub_agent.py +70 -0
  81. zrb/llm/tool/web.py +97 -0
  82. zrb/llm/tool/zrb_task.py +66 -0
  83. zrb/llm/util/attachment.py +101 -0
  84. zrb/llm/util/prompt.py +104 -0
  85. zrb/llm/util/stream_response.py +178 -0
  86. zrb/runner/cli.py +21 -20
  87. zrb/runner/common_util.py +24 -19
  88. zrb/runner/web_route/task_input_api_route.py +5 -5
  89. zrb/runner/web_util/user.py +7 -3
  90. zrb/session/any_session.py +12 -9
  91. zrb/session/session.py +38 -17
  92. zrb/task/any_task.py +24 -3
  93. zrb/task/base/context.py +42 -22
  94. zrb/task/base/execution.py +67 -55
  95. zrb/task/base/lifecycle.py +14 -7
  96. zrb/task/base/monitoring.py +12 -7
  97. zrb/task/base_task.py +113 -50
  98. zrb/task/base_trigger.py +16 -6
  99. zrb/task/cmd_task.py +6 -0
  100. zrb/task/http_check.py +11 -5
  101. zrb/task/make_task.py +5 -3
  102. zrb/task/rsync_task.py +30 -10
  103. zrb/task/scaffolder.py +7 -4
  104. zrb/task/scheduler.py +7 -4
  105. zrb/task/tcp_check.py +6 -4
  106. zrb/util/ascii_art/art/bee.txt +17 -0
  107. zrb/util/ascii_art/art/cat.txt +9 -0
  108. zrb/util/ascii_art/art/ghost.txt +16 -0
  109. zrb/util/ascii_art/art/panda.txt +17 -0
  110. zrb/util/ascii_art/art/rose.txt +14 -0
  111. zrb/util/ascii_art/art/unicorn.txt +15 -0
  112. zrb/util/ascii_art/banner.py +92 -0
  113. zrb/util/attr.py +54 -39
  114. zrb/util/cli/markdown.py +32 -0
  115. zrb/util/cli/text.py +30 -0
  116. zrb/util/cmd/command.py +33 -10
  117. zrb/util/file.py +61 -33
  118. zrb/util/git.py +2 -2
  119. zrb/util/{llm/prompt.py → markdown.py} +2 -3
  120. zrb/util/match.py +78 -0
  121. zrb/util/run.py +3 -3
  122. zrb/util/string/conversion.py +1 -1
  123. zrb/util/truncate.py +23 -0
  124. zrb/util/yaml.py +204 -0
  125. zrb/xcom/xcom.py +10 -0
  126. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/METADATA +41 -27
  127. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/RECORD +129 -131
  128. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/WHEEL +1 -1
  129. zrb/attr/__init__.py +0 -0
  130. zrb/builtin/llm/chat_session.py +0 -311
  131. zrb/builtin/llm/history.py +0 -71
  132. zrb/builtin/llm/input.py +0 -27
  133. zrb/builtin/llm/llm_ask.py +0 -187
  134. zrb/builtin/llm/previous-session.js +0 -21
  135. zrb/builtin/llm/tool/__init__.py +0 -0
  136. zrb/builtin/llm/tool/api.py +0 -71
  137. zrb/builtin/llm/tool/cli.py +0 -38
  138. zrb/builtin/llm/tool/code.py +0 -254
  139. zrb/builtin/llm/tool/file.py +0 -626
  140. zrb/builtin/llm/tool/sub_agent.py +0 -137
  141. zrb/builtin/llm/tool/web.py +0 -195
  142. zrb/builtin/project/__init__.py +0 -0
  143. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
  144. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
  145. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
  146. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
  147. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
  148. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
  149. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
  150. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
  151. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
  152. zrb/builtin/project/create/__init__.py +0 -0
  153. zrb/builtin/shell/__init__.py +0 -0
  154. zrb/builtin/shell/autocomplete/__init__.py +0 -0
  155. zrb/callback/__init__.py +0 -0
  156. zrb/cmd/__init__.py +0 -0
  157. zrb/config/default_prompt/file_extractor_system_prompt.md +0 -12
  158. zrb/config/default_prompt/interactive_system_prompt.md +0 -35
  159. zrb/config/default_prompt/persona.md +0 -1
  160. zrb/config/default_prompt/repo_extractor_system_prompt.md +0 -112
  161. zrb/config/default_prompt/repo_summarizer_system_prompt.md +0 -10
  162. zrb/config/default_prompt/summarization_prompt.md +0 -16
  163. zrb/config/default_prompt/system_prompt.md +0 -32
  164. zrb/config/llm_config.py +0 -243
  165. zrb/config/llm_context/config.py +0 -129
  166. zrb/config/llm_context/config_parser.py +0 -46
  167. zrb/config/llm_rate_limitter.py +0 -137
  168. zrb/content_transformer/__init__.py +0 -0
  169. zrb/context/__init__.py +0 -0
  170. zrb/dot_dict/__init__.py +0 -0
  171. zrb/env/__init__.py +0 -0
  172. zrb/group/__init__.py +0 -0
  173. zrb/input/__init__.py +0 -0
  174. zrb/runner/__init__.py +0 -0
  175. zrb/runner/web_route/__init__.py +0 -0
  176. zrb/runner/web_route/home_page/__init__.py +0 -0
  177. zrb/session/__init__.py +0 -0
  178. zrb/session_state_log/__init__.py +0 -0
  179. zrb/session_state_logger/__init__.py +0 -0
  180. zrb/task/__init__.py +0 -0
  181. zrb/task/base/__init__.py +0 -0
  182. zrb/task/llm/__init__.py +0 -0
  183. zrb/task/llm/agent.py +0 -243
  184. zrb/task/llm/config.py +0 -103
  185. zrb/task/llm/conversation_history.py +0 -128
  186. zrb/task/llm/conversation_history_model.py +0 -242
  187. zrb/task/llm/default_workflow/coding.md +0 -24
  188. zrb/task/llm/default_workflow/copywriting.md +0 -17
  189. zrb/task/llm/default_workflow/researching.md +0 -18
  190. zrb/task/llm/error.py +0 -95
  191. zrb/task/llm/history_summarization.py +0 -216
  192. zrb/task/llm/print_node.py +0 -101
  193. zrb/task/llm/prompt.py +0 -325
  194. zrb/task/llm/tool_wrapper.py +0 -220
  195. zrb/task/llm/typing.py +0 -3
  196. zrb/task/llm_task.py +0 -341
  197. zrb/task_status/__init__.py +0 -0
  198. zrb/util/__init__.py +0 -0
  199. zrb/util/cli/__init__.py +0 -0
  200. zrb/util/cmd/__init__.py +0 -0
  201. zrb/util/codemod/__init__.py +0 -0
  202. zrb/util/string/__init__.py +0 -0
  203. zrb/xcom/__init__.py +0 -0
  204. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
zrb/task/llm/agent.py DELETED
@@ -1,243 +0,0 @@
1
- import json
2
- from collections.abc import Callable
3
- from typing import TYPE_CHECKING, Any
4
-
5
- from zrb.config.llm_rate_limitter import LLMRateLimiter, llm_rate_limitter
6
- from zrb.context.any_context import AnyContext
7
- from zrb.context.any_shared_context import AnySharedContext
8
- from zrb.task.llm.error import extract_api_error_details
9
- from zrb.task.llm.print_node import print_node
10
- from zrb.task.llm.tool_wrapper import wrap_func, wrap_tool
11
- from zrb.task.llm.typing import ListOfDict
12
-
13
- if TYPE_CHECKING:
14
- from pydantic_ai import Agent, Tool
15
- from pydantic_ai.agent import AgentRun
16
- from pydantic_ai.messages import UserContent
17
- from pydantic_ai.models import Model
18
- from pydantic_ai.settings import ModelSettings
19
- from pydantic_ai.toolsets import AbstractToolset
20
-
21
- ToolOrCallable = Tool | Callable
22
-
23
-
24
- def create_agent_instance(
25
- ctx: AnyContext,
26
- model: "str | Model",
27
- system_prompt: str = "",
28
- model_settings: "ModelSettings | None" = None,
29
- tools: "list[ToolOrCallable]" = [],
30
- toolsets: list["AbstractToolset[Agent]"] = [],
31
- retries: int = 3,
32
- is_yolo_mode: bool | None = None,
33
- ) -> "Agent":
34
- """Creates a new Agent instance with configured tools and servers."""
35
- from pydantic_ai import Agent, Tool
36
- from pydantic_ai.tools import GenerateToolJsonSchema
37
-
38
- if is_yolo_mode is None:
39
- is_yolo_mode = False
40
- # Normalize tools
41
- tool_list = []
42
- for tool_or_callable in tools:
43
- if isinstance(tool_or_callable, Tool):
44
- tool_list.append(tool_or_callable)
45
- # Update tool's function
46
- tool = tool_or_callable
47
- tool_list.append(
48
- Tool(
49
- function=wrap_func(tool.function, ctx, is_yolo_mode),
50
- takes_ctx=tool.takes_ctx,
51
- max_retries=tool.max_retries,
52
- name=tool.name,
53
- description=tool.description,
54
- prepare=tool.prepare,
55
- docstring_format=tool.docstring_format,
56
- require_parameter_descriptions=tool.require_parameter_descriptions,
57
- schema_generator=GenerateToolJsonSchema,
58
- strict=tool.strict,
59
- )
60
- )
61
- else:
62
- # Turn function into tool
63
- tool_list.append(wrap_tool(tool_or_callable, ctx, is_yolo_mode))
64
- # Return Agent
65
- return Agent(
66
- model=model,
67
- system_prompt=system_prompt,
68
- tools=tool_list,
69
- toolsets=toolsets,
70
- model_settings=model_settings,
71
- retries=retries,
72
- )
73
-
74
-
75
- def get_agent(
76
- ctx: AnyContext,
77
- agent_attr: "Agent | Callable[[AnySharedContext], Agent] | None",
78
- model: "str | Model",
79
- system_prompt: str,
80
- model_settings: "ModelSettings | None",
81
- tools_attr: (
82
- "list[ToolOrCallable] | Callable[[AnySharedContext], list[ToolOrCallable]]"
83
- ),
84
- additional_tools: "list[ToolOrCallable]",
85
- toolsets_attr: "list[AbstractToolset[Agent]] | Callable[[AnySharedContext], list[AbstractToolset[Agent]]]", # noqa
86
- additional_toolsets: "list[AbstractToolset[Agent]]",
87
- retries: int = 3,
88
- is_yolo_mode: bool | None = None,
89
- ) -> "Agent":
90
- """Retrieves the configured Agent instance or creates one if necessary."""
91
- from pydantic_ai import Agent
92
-
93
- # Render agent instance and return if agent_attr is already an agent
94
- if isinstance(agent_attr, Agent):
95
- return agent_attr
96
- if callable(agent_attr):
97
- agent_instance = agent_attr(ctx)
98
- if not isinstance(agent_instance, Agent):
99
- err_msg = (
100
- "Callable agent factory did not return an Agent instance, "
101
- f"got: {type(agent_instance)}"
102
- )
103
- raise TypeError(err_msg)
104
- return agent_instance
105
- # Get tools for agent
106
- tools = list(tools_attr(ctx) if callable(tools_attr) else tools_attr)
107
- tools.extend(additional_tools)
108
- # Get Toolsets for agent
109
- tool_sets = list(toolsets_attr(ctx) if callable(toolsets_attr) else toolsets_attr)
110
- tool_sets.extend(additional_toolsets)
111
- # If no agent provided, create one using the configuration
112
- return create_agent_instance(
113
- ctx=ctx,
114
- model=model,
115
- system_prompt=system_prompt,
116
- tools=tools,
117
- toolsets=tool_sets,
118
- model_settings=model_settings,
119
- retries=retries,
120
- is_yolo_mode=is_yolo_mode,
121
- )
122
-
123
-
124
- async def run_agent_iteration(
125
- ctx: AnyContext,
126
- agent: "Agent",
127
- user_prompt: str,
128
- attachments: "list[UserContent] | None" = None,
129
- history_list: ListOfDict | None = None,
130
- rate_limitter: LLMRateLimiter | None = None,
131
- max_retry: int = 2,
132
- ) -> "AgentRun":
133
- """
134
- Runs a single iteration of the agent execution loop.
135
-
136
- Args:
137
- ctx: The task context.
138
- agent: The Pydantic AI agent instance.
139
- user_prompt: The user's input prompt.
140
- history_list: The current conversation history.
141
-
142
- Returns:
143
- The agent run result object.
144
-
145
- Raises:
146
- Exception: If any error occurs during agent execution.
147
- """
148
- if max_retry < 0:
149
- raise ValueError("Max retry cannot be less than 0")
150
- attempt = 0
151
- while attempt < max_retry:
152
- try:
153
- return await _run_single_agent_iteration(
154
- ctx=ctx,
155
- agent=agent,
156
- user_prompt=user_prompt,
157
- attachments=[] if attachments is None else attachments,
158
- history_list=[] if history_list is None else history_list,
159
- rate_limitter=(
160
- llm_rate_limitter if rate_limitter is None else rate_limitter
161
- ),
162
- )
163
- except BaseException:
164
- attempt += 1
165
- if attempt == max_retry:
166
- raise
167
- raise Exception("Max retry exceeded")
168
-
169
-
170
- async def _run_single_agent_iteration(
171
- ctx: AnyContext,
172
- agent: "Agent",
173
- user_prompt: str,
174
- attachments: "list[UserContent]",
175
- history_list: ListOfDict,
176
- rate_limitter: LLMRateLimiter,
177
- ) -> "AgentRun":
178
- from openai import APIError
179
- from pydantic_ai.messages import ModelMessagesTypeAdapter
180
-
181
- agent_payload = _estimate_request_payload(
182
- agent, user_prompt, attachments, history_list
183
- )
184
- if rate_limitter:
185
- await rate_limitter.throttle(agent_payload)
186
- else:
187
- await llm_rate_limitter.throttle(agent_payload)
188
-
189
- user_prompt_with_attachments = [user_prompt] + attachments
190
- async with agent:
191
- async with agent.iter(
192
- user_prompt=user_prompt_with_attachments,
193
- message_history=ModelMessagesTypeAdapter.validate_python(history_list),
194
- ) as agent_run:
195
- async for node in agent_run:
196
- # Each node represents a step in the agent's execution
197
- # Reference: https://ai.pydantic.dev/agents/#streaming
198
- try:
199
- await print_node(_get_plain_printer(ctx), agent_run, node)
200
- except APIError as e:
201
- # Extract detailed error information from the response
202
- error_details = extract_api_error_details(e)
203
- ctx.log_error(f"API Error: {error_details}")
204
- raise
205
- except Exception as e:
206
- ctx.log_error(f"Error processing node: {str(e)}")
207
- ctx.log_error(f"Error type: {type(e).__name__}")
208
- raise
209
- return agent_run
210
-
211
-
212
- def _estimate_request_payload(
213
- agent: "Agent",
214
- user_prompt: str,
215
- attachments: "list[UserContent]",
216
- history_list: ListOfDict,
217
- ) -> str:
218
- system_prompts = agent._system_prompts if hasattr(agent, "_system_prompts") else ()
219
- return json.dumps(
220
- [
221
- {"role": "system", "content": "\n".join(system_prompts)},
222
- *history_list,
223
- {"role": "user", "content": user_prompt},
224
- *[_estimate_attachment_payload(attachment) for attachment in attachments],
225
- ]
226
- )
227
-
228
-
229
- def _estimate_attachment_payload(attachment: "UserContent") -> Any:
230
- if hasattr(attachment, "url"):
231
- return {"role": "user", "content": attachment.url}
232
- if hasattr(attachment, "data"):
233
- return {"role": "user", "content": "x" * len(attachment.data)}
234
- return ""
235
-
236
-
237
- def _get_plain_printer(ctx: AnyContext):
238
- def printer(*args, **kwargs):
239
- if "plain" not in kwargs:
240
- kwargs["plain"] = True
241
- return ctx.print(*args, **kwargs)
242
-
243
- return printer
zrb/task/llm/config.py DELETED
@@ -1,103 +0,0 @@
1
- from typing import TYPE_CHECKING, Callable
2
-
3
- if TYPE_CHECKING:
4
- from pydantic_ai.models import Model
5
- from pydantic_ai.settings import ModelSettings
6
-
7
- from zrb.attr.type import BoolAttr, StrAttr, fstring
8
- from zrb.config.llm_config import LLMConfig, llm_config
9
- from zrb.context.any_context import AnyContext
10
- from zrb.context.any_shared_context import AnySharedContext
11
- from zrb.util.attr import get_attr, get_bool_attr
12
-
13
-
14
- def get_is_yolo_mode(
15
- ctx: AnyContext,
16
- is_yolo_mode_attr: BoolAttr | None = None,
17
- render_yolo_mode: bool = True,
18
- ):
19
- return get_bool_attr(
20
- ctx,
21
- is_yolo_mode_attr,
22
- llm_config.default_yolo_mode,
23
- auto_render=render_yolo_mode,
24
- )
25
-
26
-
27
- def get_model_settings(
28
- ctx: AnyContext,
29
- model_settings_attr: (
30
- "ModelSettings | Callable[[AnySharedContext], ModelSettings] | None"
31
- ) = None,
32
- ) -> "ModelSettings | None":
33
- """Gets the model settings, resolving callables if necessary."""
34
- model_settings = get_attr(ctx, model_settings_attr, None, auto_render=False)
35
- if model_settings is None:
36
- return llm_config.default_model_settings
37
- return model_settings
38
-
39
-
40
- def get_model_base_url(
41
- ctx: AnyContext,
42
- model_base_url_attr: StrAttr | None = None,
43
- render_model_base_url: bool = True,
44
- ) -> str | None:
45
- """Gets the model base URL, rendering if configured."""
46
- base_url = get_attr(
47
- ctx, model_base_url_attr, None, auto_render=render_model_base_url
48
- )
49
- if base_url is None and llm_config.default_model_base_url is not None:
50
- return llm_config.default_model_base_url
51
- if isinstance(base_url, str) or base_url is None:
52
- return base_url
53
- raise ValueError(f"Invalid model base URL: {base_url}")
54
-
55
-
56
- def get_model_api_key(
57
- ctx: AnyContext,
58
- model_api_key_attr: StrAttr | None = None,
59
- render_model_api_key: bool = True,
60
- ) -> str | None:
61
- """Gets the model API key, rendering if configured."""
62
- api_key = get_attr(ctx, model_api_key_attr, None, auto_render=render_model_api_key)
63
- if api_key is None and llm_config.default_api_key is not None:
64
- return llm_config.default_model_api_key
65
- if isinstance(api_key, str) or api_key is None:
66
- return api_key
67
- raise ValueError(f"Invalid model API key: {api_key}")
68
-
69
-
70
- def get_model(
71
- ctx: AnyContext,
72
- model_attr: "Callable[[AnySharedContext], Model | str | fstring] | Model | None",
73
- render_model: bool,
74
- model_base_url_attr: StrAttr | None = None,
75
- render_model_base_url: bool = True,
76
- model_api_key_attr: StrAttr | None = None,
77
- render_model_api_key: bool = True,
78
- ) -> "str | Model":
79
- """Gets the model instance or name, handling defaults and configuration."""
80
- from pydantic_ai.models import Model
81
-
82
- model = get_attr(ctx, model_attr, None, auto_render=render_model)
83
- if model is None:
84
- return llm_config.default_model
85
- if isinstance(model, str):
86
- model_base_url = get_model_base_url(
87
- ctx, model_base_url_attr, render_model_base_url
88
- )
89
- model_api_key = get_model_api_key(ctx, model_api_key_attr, render_model_api_key)
90
- new_llm_config = LLMConfig(
91
- default_model_name=model,
92
- default_base_url=model_base_url,
93
- default_api_key=model_api_key,
94
- )
95
- if model_base_url is None and model_api_key is None:
96
- default_model_provider = llm_config.default_model_provider
97
- if default_model_provider is not None:
98
- new_llm_config.set_default_model_provider(default_model_provider)
99
- return new_llm_config.default_model
100
- # If it's already a Model instance, return it directly
101
- if isinstance(model, Model):
102
- return model
103
- raise ValueError(f"Invalid model type resolved: {type(model)}, value: {model}")
@@ -1,128 +0,0 @@
1
- import json
2
- from collections.abc import Callable
3
- from copy import deepcopy
4
- from typing import Any
5
-
6
- from zrb.attr.type import StrAttr
7
- from zrb.context.any_context import AnyContext
8
- from zrb.context.any_shared_context import AnySharedContext
9
- from zrb.task.llm.conversation_history_model import ConversationHistory
10
- from zrb.task.llm.typing import ListOfDict
11
- from zrb.util.attr import get_str_attr
12
- from zrb.util.file import write_file
13
- from zrb.util.run import run_async
14
-
15
-
16
- def get_history_file(
17
- ctx: AnyContext,
18
- conversation_history_file_attr: StrAttr | None,
19
- render_history_file: bool,
20
- ) -> str:
21
- """Gets the path to the conversation history file, rendering if configured."""
22
- return get_str_attr(
23
- ctx,
24
- conversation_history_file_attr,
25
- "",
26
- auto_render=render_history_file,
27
- )
28
-
29
-
30
- async def read_conversation_history(
31
- ctx: AnyContext,
32
- conversation_history_reader: (
33
- Callable[[AnySharedContext], ConversationHistory | dict | list | None] | None
34
- ),
35
- conversation_history_file_attr: StrAttr | None,
36
- render_history_file: bool,
37
- conversation_history_attr: (
38
- ConversationHistory
39
- | Callable[[AnySharedContext], ConversationHistory | dict | list]
40
- | dict
41
- | list
42
- ),
43
- ) -> ConversationHistory:
44
- """Reads conversation history from reader, file, or attribute, with validation."""
45
- history_file = get_history_file(
46
- ctx, conversation_history_file_attr, render_history_file
47
- )
48
- # Use the class method defined above
49
- history_data = await ConversationHistory.read_from_source(
50
- ctx=ctx,
51
- reader=conversation_history_reader,
52
- file_path=history_file,
53
- )
54
- if history_data:
55
- return history_data
56
- # Priority 3: Callable or direct conversation_history attribute
57
- raw_data_attr: Any = None
58
- if callable(conversation_history_attr):
59
- try:
60
- raw_data_attr = await run_async(conversation_history_attr(ctx))
61
- except Exception as e:
62
- ctx.log_warning(
63
- f"Error executing callable conversation_history attribute: {e}. "
64
- "Ignoring."
65
- )
66
- if raw_data_attr is None:
67
- raw_data_attr = conversation_history_attr
68
- if raw_data_attr:
69
- # Use the class method defined above
70
- history_data = ConversationHistory.parse_and_validate(
71
- ctx, raw_data_attr, "attribute"
72
- )
73
- if history_data:
74
- return history_data
75
- # Fallback: Return default value
76
- return ConversationHistory()
77
-
78
-
79
- async def write_conversation_history(
80
- ctx: AnyContext,
81
- history_data: ConversationHistory,
82
- conversation_history_writer: (
83
- Callable[[AnySharedContext, ConversationHistory], None] | None
84
- ),
85
- conversation_history_file_attr: StrAttr | None,
86
- render_history_file: bool,
87
- ):
88
- """Writes conversation history using the writer or to a file."""
89
- if conversation_history_writer is not None:
90
- await run_async(conversation_history_writer(ctx, history_data))
91
- history_file = get_history_file(
92
- ctx, conversation_history_file_attr, render_history_file
93
- )
94
- if history_file != "":
95
- write_file(history_file, json.dumps(history_data.to_dict(), indent=2))
96
-
97
-
98
- def replace_system_prompt_in_history(
99
- history_list: ListOfDict, replacement: str = "<main LLM system prompt>"
100
- ) -> ListOfDict:
101
- """
102
- Returns a new history list where any part with part_kind 'system-prompt'
103
- has its 'content' replaced with the given replacement string.
104
- Args:
105
- history: List of history items (each item is a dict with a 'parts' list).
106
- replacement: The string to use in place of system-prompt content.
107
-
108
- Returns:
109
- A deep-copied list of history items with system-prompt content replaced.
110
- """
111
- new_history = deepcopy(history_list)
112
- for item in new_history:
113
- parts = item.get("parts", [])
114
- for part in parts:
115
- if part.get("part_kind") == "system-prompt":
116
- part["content"] = replacement
117
- return new_history
118
-
119
-
120
- def count_part_in_history_list(history_list: ListOfDict) -> int:
121
- """Calculates the total number of 'parts' in a history list."""
122
- history_part_len = 0
123
- for history in history_list:
124
- if "parts" in history:
125
- history_part_len += len(history["parts"])
126
- else:
127
- history_part_len += 1
128
- return history_part_len