zrb 1.3.1__py3-none-any.whl → 1.4.1__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 CHANGED
@@ -33,6 +33,7 @@ from zrb.input.option_input import OptionInput
33
33
  from zrb.input.password_input import PasswordInput
34
34
  from zrb.input.str_input import StrInput
35
35
  from zrb.input.text_input import TextInput
36
+ from zrb.llm_config import llm_config
36
37
  from zrb.runner.cli import cli
37
38
  from zrb.runner.web_config.config_factory import web_config
38
39
  from zrb.runner.web_schema.user import User
@@ -101,6 +102,7 @@ assert ContentTransformer
101
102
  assert Scaffolder
102
103
  assert Scheduler
103
104
  assert cli
105
+ assert llm_config
104
106
  assert Xcom
105
107
  assert web_config
106
108
  assert User
zrb/__main__.py CHANGED
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import sys
2
3
 
3
4
  from zrb.config import INIT_MODULES, INIT_SCRIPTS
@@ -9,10 +10,19 @@ from zrb.util.load import load_file, load_module
9
10
 
10
11
  def serve_cli():
11
12
  try:
13
+ # load init modules
12
14
  for init_module in INIT_MODULES:
13
15
  load_module(init_module)
16
+ zrb_init_path_list = _get_zrb_init_path_list()
17
+ # load init scripts
14
18
  for init_script in INIT_SCRIPTS:
15
- load_file(init_script, -1)
19
+ abs_init_script = os.path.abspath(init_script)
20
+ if abs_init_script not in zrb_init_path_list:
21
+ load_file(abs_init_script, -1)
22
+ # load zrb init
23
+ for zrb_init_path in zrb_init_path_list:
24
+ load_file(zrb_init_path)
25
+ # run the CLI
16
26
  cli.run(sys.argv[1:])
17
27
  except KeyboardInterrupt:
18
28
  print(stylize_warning("\nStopped"), file=sys.stderr)
@@ -24,3 +34,17 @@ def serve_cli():
24
34
  except NodeNotFoundError as e:
25
35
  print(stylize_error(f"{e}"), file=sys.stderr)
26
36
  sys.exit(1)
37
+
38
+
39
+ def _get_zrb_init_path_list() -> list[str]:
40
+ current_path = os.path.abspath(os.getcwd())
41
+ dir_path_list = [current_path]
42
+ while current_path != os.path.dirname(current_path): # Stop at root
43
+ current_path = os.path.dirname(current_path)
44
+ dir_path_list.append(current_path)
45
+ zrb_init_path_list = []
46
+ for current_path in dir_path_list[::-1]:
47
+ zrb_init_path = os.path.join(current_path, "zrb_init.py")
48
+ if os.path.isfile(zrb_init_path):
49
+ zrb_init_path_list.append(zrb_init_path)
50
+ return zrb_init_path_list
@@ -2,14 +2,12 @@ import json
2
2
  import os
3
3
  from typing import Any
4
4
 
5
- from pydantic_ai.models import Model
6
-
7
5
  from zrb.builtin.group import llm_group
8
6
  from zrb.builtin.llm.tool.api import get_current_location, get_current_weather
9
7
  from zrb.builtin.llm.tool.cli import run_shell_command
10
8
  from zrb.builtin.llm.tool.file import (
11
- list_file,
12
- read_source_code,
9
+ list_files,
10
+ read_all_files,
13
11
  read_text_file,
14
12
  write_text_file,
15
13
  )
@@ -24,18 +22,14 @@ from zrb.config import (
24
22
  LLM_ALLOW_ACCESS_LOCAL_FILE,
25
23
  LLM_ALLOW_ACCESS_SHELL,
26
24
  LLM_HISTORY_DIR,
27
- LLM_MODEL,
28
25
  LLM_SYSTEM_PROMPT,
29
26
  SERP_API_KEY,
30
27
  )
31
- from zrb.context.any_context import AnyContext
32
28
  from zrb.context.any_shared_context import AnySharedContext
33
- from zrb.input.any_input import AnyInput
34
29
  from zrb.input.bool_input import BoolInput
35
30
  from zrb.input.str_input import StrInput
36
31
  from zrb.input.text_input import TextInput
37
32
  from zrb.task.llm_task import LLMTask
38
- from zrb.util.attr import get_attr
39
33
  from zrb.util.file import read_file, write_file
40
34
  from zrb.util.string.conversion import to_pascal_case
41
35
 
@@ -90,46 +84,37 @@ def _write_chat_conversation(
90
84
  write_file(last_session_file_path, current_session_name)
91
85
 
92
86
 
93
- class _LLMChat(LLMTask):
94
-
95
- _default_model: Model | str | None = None
96
-
97
- def set_default_model(self, model: Model | str):
98
- self._default_model = model
99
-
100
- @property
101
- def inputs(self) -> list[AnyInput]:
102
- task_inputs = super().inputs
103
- model_input_default = LLM_MODEL if self._default_model is None else "default"
104
- return [
87
+ llm_chat: LLMTask = llm_group.add_task(
88
+ LLMTask(
89
+ name="llm-chat",
90
+ input=[
105
91
  StrInput(
106
92
  "model",
107
93
  description="LLM Model",
108
94
  prompt="LLM Model",
109
- default=model_input_default,
95
+ default="",
110
96
  allow_positional_parsing=False,
111
97
  always_prompt=False,
98
+ allow_empty=True,
99
+ ),
100
+ StrInput(
101
+ "base-url",
102
+ description="LLM API Base URL",
103
+ prompt="LLM API Base URL",
104
+ default="",
105
+ allow_positional_parsing=False,
106
+ always_prompt=False,
107
+ allow_empty=True,
108
+ ),
109
+ StrInput(
110
+ "api-key",
111
+ description="LLM API Key",
112
+ prompt="LLM API Key",
113
+ default="",
114
+ allow_positional_parsing=False,
115
+ always_prompt=False,
116
+ allow_empty=True,
112
117
  ),
113
- *task_inputs,
114
- ]
115
-
116
- def _get_model(self, ctx: AnyContext) -> str | Model | None:
117
- if ctx.input.model == "default":
118
- if self._default_model is not None:
119
- return self._default_model
120
- return super()._get_model(ctx)
121
- model = get_attr(
122
- ctx, ctx.input.model, "ollama_chat/llama3.1", auto_render=self._render_model
123
- )
124
- if isinstance(model, (Model, str)) or model is None:
125
- return model
126
- raise ValueError("Invalid model")
127
-
128
-
129
- llm_chat: LLMTask = llm_group.add_task(
130
- _LLMChat(
131
- name="llm-chat",
132
- input=[
133
118
  TextInput(
134
119
  "system-prompt",
135
120
  description="System prompt",
@@ -156,10 +141,17 @@ llm_chat: LLMTask = llm_group.add_task(
156
141
  always_prompt=False,
157
142
  ),
158
143
  ],
144
+ model=lambda ctx: None if ctx.input.model == "" else ctx.input.model,
145
+ model_base_url=lambda ctx: (
146
+ None if ctx.input.base_url == "" else ctx.input.base_url
147
+ ),
148
+ model_api_key=lambda ctx: (
149
+ None if ctx.input.api_key == "" else ctx.input.api_key
150
+ ),
159
151
  conversation_history_reader=_read_chat_conversation,
160
152
  conversation_history_writer=_write_chat_conversation,
161
153
  description="Chat with LLM",
162
- system_prompt="{ctx.input['system-prompt']}",
154
+ system_prompt="{ctx.input.system_prompt}",
163
155
  message="{ctx.input.message}",
164
156
  retries=0,
165
157
  ),
@@ -168,8 +160,8 @@ llm_chat: LLMTask = llm_group.add_task(
168
160
 
169
161
 
170
162
  if LLM_ALLOW_ACCESS_LOCAL_FILE:
171
- llm_chat.add_tool(read_source_code)
172
- llm_chat.add_tool(list_file)
163
+ llm_chat.add_tool(read_all_files)
164
+ llm_chat.add_tool(list_files)
173
165
  llm_chat.add_tool(read_text_file)
174
166
  llm_chat.add_tool(write_text_file)
175
167
 
@@ -1,22 +1,131 @@
1
+ import fnmatch
1
2
  import os
2
3
 
3
4
  from zrb.util.file import read_file, write_file
4
5
 
6
+ _INCLUDED_PATTERNS: list[str] = [
7
+ "*.py", # Python
8
+ "*.go", # Go
9
+ "*.rs", # Rust
10
+ "*.js", # JavaScript
11
+ "*.ts", # TypeScript
12
+ "*.java", # Java
13
+ "*.c", # C
14
+ "*.cpp", # C++
15
+ "*.cc", # Alternative C++ extension
16
+ "*.cxx", # Alternative C++ extension
17
+ "*.rb", # Ruby
18
+ "*.swift", # Swift
19
+ "*.kt", # Kotlin
20
+ "*.php", # PHP
21
+ "*.pl", # Perl / Prolog
22
+ "*.pm", # Perl module
23
+ "*.sh", # Shell
24
+ "*.bat", # Batch
25
+ "*.ps1", # PowerShell
26
+ "*.R", # R (capital)
27
+ "*.r", # R (lowercase)
28
+ "*.scala", # Scala
29
+ "*.hs", # Haskell
30
+ "*.cs", # C#
31
+ "*.fs", # F#
32
+ "*.ex", # Elixir
33
+ "*.exs", # Elixir script
34
+ "*.erl", # Erlang
35
+ "*.hrl", # Erlang header
36
+ "*.dart", # Dart
37
+ "*.m", # Objective-C / Matlab (note: conflicts may arise)
38
+ "*.mm", # Objective-C++
39
+ "*.lua", # Lua
40
+ "*.jl", # Julia
41
+ "*.groovy", # Groovy
42
+ "*.clj", # Clojure
43
+ "*.cljs", # ClojureScript
44
+ "*.cljc", # Clojure common
45
+ "*.vb", # Visual Basic
46
+ "*.f90", # Fortran
47
+ "*.f95", # Fortran
48
+ "*.adb", # Ada
49
+ "*.ads", # Ada specification
50
+ "*.pas", # Pascal
51
+ "*.pp", # Pascal
52
+ "*.ml", # OCaml
53
+ "*.mli", # OCaml interface
54
+ "*.nim", # Nim
55
+ "*.rkt", # Racket
56
+ "*.d", # D
57
+ "*.lisp", # Common Lisp
58
+ "*.lsp", # Lisp variant
59
+ "*.cl", # Common Lisp
60
+ "*.scm", # Scheme
61
+ "*.st", # Smalltalk
62
+ "*.vhd", # VHDL
63
+ "*.vhdl", # VHDL
64
+ "*.v", # Verilog
65
+ "*.asm", # Assembly
66
+ "*.s", # Assembly (alternative)
67
+ "*.sql", # SQL (if desired)
68
+ ]
5
69
 
6
- def list_file(
70
+ # Extended list of directories and patterns to exclude.
71
+ _EXCLUDED_PATTERNS: list[str] = [
72
+ "venv", # Python virtual environments
73
+ ".venv",
74
+ "node_modules", # Node.js dependencies
75
+ ".git", # Git repositories
76
+ "__pycache__", # Python cache directories
77
+ "build", # Build directories
78
+ "dist", # Distribution directories
79
+ "target", # Build output directories (Java, Rust, etc.)
80
+ "bin", # Binary directories
81
+ "obj", # Object files directories
82
+ ".idea", # JetBrains IDEs
83
+ ".vscode", # VS Code settings
84
+ ".eggs", # Python eggs
85
+ ]
86
+
87
+
88
+ def list_files(
7
89
  directory: str = ".",
8
- extensions: list[str] = [".py", ".go", ".js", ".ts", ".java", ".c", ".cpp"],
90
+ included_patterns: list[str] = _INCLUDED_PATTERNS,
91
+ excluded_patterns: list[str] = _EXCLUDED_PATTERNS,
9
92
  ) -> list[str]:
10
- """List all files in a directory"""
93
+ """List all files in a directory that match any of the included glob patterns
94
+ and do not reside in any directory matching an excluded pattern.
95
+ Patterns are evaluated using glob-style matching.
96
+ """
11
97
  all_files: list[str] = []
12
- for root, _, files in os.walk(directory):
98
+ for root, dirs, files in os.walk(directory):
13
99
  for filename in files:
14
- for extension in extensions:
15
- if filename.lower().endswith(extension):
16
- all_files.append(os.path.join(root, filename))
100
+ if any(fnmatch.fnmatch(filename, pat) for pat in included_patterns):
101
+ full_path = os.path.join(root, filename)
102
+ if _should_exclude(full_path, excluded_patterns):
103
+ continue
104
+ all_files.append(full_path)
17
105
  return all_files
18
106
 
19
107
 
108
+ def _should_exclude(full_path: str, excluded_patterns: list[str]) -> bool:
109
+ """
110
+ Return True if the file at full_path should be excluded based on
111
+ the list of excluded_patterns. Patterns that include a path separator
112
+ are applied to the full normalized path; otherwise they are matched
113
+ against each individual component of the path.
114
+ """
115
+ norm_path = os.path.normpath(full_path)
116
+ path_parts = norm_path.split(os.sep)
117
+ for pat in excluded_patterns:
118
+ # If the pattern seems intended for full path matching (contains a separator)
119
+ if os.sep in pat or "/" in pat:
120
+ if fnmatch.fnmatch(norm_path, pat):
121
+ return True
122
+ else:
123
+ # Otherwise check each part of the path
124
+ if any(fnmatch.fnmatch(part, pat) for part in path_parts):
125
+ return True
126
+ return False
127
+
128
+
20
129
  def read_text_file(file: str) -> str:
21
130
  """Read a text file"""
22
131
  return read_file(os.path.abspath(file))
@@ -27,12 +136,16 @@ def write_text_file(file: str, content: str):
27
136
  return write_file(os.path.abspath(file), content)
28
137
 
29
138
 
30
- def read_source_code(
139
+ def read_all_files(
31
140
  directory: str = ".",
32
- extensions: list[str] = [".py", ".go", ".js", ".ts", ".java", ".c", ".cpp"],
141
+ included_patterns: list[str] = _INCLUDED_PATTERNS,
142
+ excluded_patterns: list[str] = _EXCLUDED_PATTERNS,
33
143
  ) -> list[str]:
34
- """Read source code in a directory"""
35
- files = list_file(directory, extensions)
144
+ """Read all files in a directory that match any of the included glob patterns
145
+ and do not match any of the excluded glob patterns.
146
+ Patterns are evaluated using glob-style matching.
147
+ """
148
+ files = list_files(directory, included_patterns, excluded_patterns)
36
149
  for index, file in enumerate(files):
37
150
  content = read_text_file(file)
38
151
  files[index] = f"# {file}\n```\n{content}\n```"
zrb/config.py CHANGED
@@ -75,7 +75,6 @@ WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES = int(
75
75
  WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES = int(
76
76
  os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60")
77
77
  )
78
- LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
79
78
 
80
79
  _DEFAULT_PROMPT = (
81
80
  "You are a helpful AI assistant capable of using various tools to answer user queries. When solving a problem:\n"
zrb/llm_config.py ADDED
@@ -0,0 +1,50 @@
1
+ import os
2
+
3
+ from pydantic_ai.models import Model
4
+ from pydantic_ai.models.openai import OpenAIModel
5
+ from pydantic_ai.providers.openai import OpenAIProvider
6
+
7
+
8
+ class LLMConfig:
9
+
10
+ def __init__(
11
+ self,
12
+ model_name: str | None = None,
13
+ base_url: str | None = None,
14
+ api_key: str | None = None,
15
+ ):
16
+ self._model_name = (
17
+ model_name if model_name is not None else os.getenv("ZRB_LLM_MODEL", None)
18
+ )
19
+ self._base_url = (
20
+ base_url if base_url is not None else os.getenv("ZRB_LLM_BASE_URL", None)
21
+ )
22
+ self._api_key = (
23
+ api_key if api_key is not None else os.getenv("ZRB_LLM_API_KEY", None)
24
+ )
25
+ self._default_model = None
26
+
27
+ def _get_model_name(self) -> str | None:
28
+ return self._model_name if self._model_name is not None else None
29
+
30
+ def _get_model_provider(self) -> OpenAIProvider:
31
+ if self._base_url is None and self._api_key is None:
32
+ return "openai"
33
+ return OpenAIProvider(base_url=self._base_url, api_key=self._api_key)
34
+
35
+ def get_default_model(self) -> Model | str | None:
36
+ if self._default_model is not None:
37
+ return self._default_model
38
+ model_name = self._get_model_name()
39
+ if model_name is None:
40
+ return None
41
+ return OpenAIModel(
42
+ model_name=model_name,
43
+ provider=self._get_model_provider(),
44
+ )
45
+
46
+ def set_default_model(self, model: Model | str | None):
47
+ self._default_model = model
48
+
49
+
50
+ llm_config = LLMConfig()
zrb/runner/cli.py CHANGED
@@ -18,13 +18,11 @@ from zrb.util.cli.style import (
18
18
  stylize_section_header,
19
19
  )
20
20
  from zrb.util.group import extract_node_from_args, get_non_empty_subgroups, get_subtasks
21
- from zrb.util.load import load_zrb_init
22
21
  from zrb.util.string.conversion import double_quote
23
22
 
24
23
 
25
24
  class Cli(Group):
26
25
  def run(self, args: list[str] = []):
27
- load_zrb_init()
28
26
  kwargs, args = self._extract_kwargs_from_args(args)
29
27
  node, node_path, args = extract_node_from_args(self, args)
30
28
  if isinstance(node, Group):
zrb/task/llm_task.py CHANGED
@@ -18,11 +18,13 @@ from pydantic_ai.models import Model
18
18
  from pydantic_ai.settings import ModelSettings
19
19
 
20
20
  from zrb.attr.type import StrAttr, fstring
21
- from zrb.config import LLM_MODEL, LLM_SYSTEM_PROMPT
21
+ from zrb.config import LLM_SYSTEM_PROMPT
22
22
  from zrb.context.any_context import AnyContext
23
23
  from zrb.context.any_shared_context import AnySharedContext
24
24
  from zrb.env.any_env import AnyEnv
25
25
  from zrb.input.any_input import AnyInput
26
+ from zrb.llm_config import LLMConfig
27
+ from zrb.llm_config import llm_config as default_llm_config
26
28
  from zrb.task.any_task import AnyTask
27
29
  from zrb.task.base_task import BaseTask
28
30
  from zrb.util.attr import get_attr, get_str_attr
@@ -46,11 +48,15 @@ class LLMTask(BaseTask):
46
48
  env: list[AnyEnv | None] | AnyEnv | None = None,
47
49
  model: (
48
50
  Callable[[AnySharedContext], Model | str | fstring] | Model | None
49
- ) = LLM_MODEL,
51
+ ) = None,
52
+ render_model: bool = True,
53
+ model_base_url: StrAttr = None,
54
+ render_model_base_url: bool = True,
55
+ model_api_key: StrAttr = None,
56
+ render_model_api_key: bool = True,
50
57
  model_settings: (
51
58
  ModelSettings | Callable[[AnySharedContext], ModelSettings] | None
52
59
  ) = None,
53
- render_model: bool = True,
54
60
  agent: Agent | Callable[[AnySharedContext], Agent] | None = None,
55
61
  system_prompt: StrAttr | None = LLM_SYSTEM_PROMPT,
56
62
  render_system_prompt: bool = True,
@@ -105,9 +111,13 @@ class LLMTask(BaseTask):
105
111
  successor=successor,
106
112
  )
107
113
  self._model = model
114
+ self._render_model = render_model
115
+ self._model_base_url = model_base_url
116
+ self._render_model_base_url = render_model_base_url
117
+ self._model_api_key = model_api_key
118
+ self._render_model_api_key = render_model_api_key
108
119
  self._model_settings = model_settings
109
120
  self._agent = agent
110
- self._render_model = render_model
111
121
  self._system_prompt = system_prompt
112
122
  self._render_system_prompt = render_system_prompt
113
123
  self._message = message
@@ -120,9 +130,6 @@ class LLMTask(BaseTask):
120
130
  self._render_history_file = render_history_file
121
131
  self._max_call_iteration = max_call_iteration
122
132
 
123
- def set_model(self, model: Model | str):
124
- self._model = model
125
-
126
133
  def add_tool(self, tool: ToolOrCallable):
127
134
  self._additional_tools.append(tool)
128
135
 
@@ -242,15 +249,47 @@ class LLMTask(BaseTask):
242
249
  system_prompt=self._get_system_prompt(ctx),
243
250
  tools=tools,
244
251
  model_settings=self._get_model_settings(ctx),
252
+ retries=3,
245
253
  )
246
254
 
247
255
  def _get_model(self, ctx: AnyContext) -> str | Model | None:
248
- model = get_attr(
249
- ctx, self._model, "ollama_chat/llama3.1", auto_render=self._render_model
256
+ model = get_attr(ctx, self._model, None, auto_render=self._render_model)
257
+ if model is None:
258
+ return default_llm_config.get_default_model()
259
+ if isinstance(model, str):
260
+ llm_config = LLMConfig(
261
+ model_name=model,
262
+ base_url=get_attr(
263
+ ctx,
264
+ self._get_model_base_url(ctx),
265
+ None,
266
+ auto_render=self._render_model_base_url,
267
+ ),
268
+ api_key=get_attr(
269
+ ctx,
270
+ self._get_model_api_key(ctx),
271
+ None,
272
+ auto_render=self._render_model_api_key,
273
+ ),
274
+ )
275
+ return llm_config.get_default_model()
276
+ raise ValueError(f"Invalid model: {model}")
277
+
278
+ def _get_model_base_url(self, ctx: AnyContext) -> str | None:
279
+ base_url = get_attr(
280
+ ctx, self._model_base_url, None, auto_render=self._render_model_base_url
281
+ )
282
+ if isinstance(base_url, str) or base_url is None:
283
+ return base_url
284
+ raise ValueError(f"Invalid model base URL: {base_url}")
285
+
286
+ def _get_model_api_key(self, ctx: AnyContext) -> str | None:
287
+ api_key = get_attr(
288
+ ctx, self._model_api_key, None, auto_render=self._render_model_api_key
250
289
  )
251
- if isinstance(model, (Model, str)) or model is None:
252
- return model
253
- raise ValueError("Invalid model")
290
+ if isinstance(api_key, str) or api_key is None:
291
+ return api_key
292
+ raise ValueError(f"Invalid model base URL: {api_key}")
254
293
 
255
294
  def _get_system_prompt(self, ctx: AnyContext) -> str:
256
295
  return get_str_attr(
zrb/util/load.py CHANGED
@@ -9,22 +9,6 @@ from typing import Any
9
9
  pattern = re.compile("[^a-zA-Z0-9]")
10
10
 
11
11
 
12
- def load_zrb_init(dir_path: str | None = None) -> Any | None:
13
- if dir_path is None:
14
- dir_path = os.getcwd()
15
- # get path list from current path to the absolute root
16
- current_path = os.path.abspath(dir_path)
17
- path_list = [current_path]
18
- while current_path != os.path.dirname(current_path): # Stop at root
19
- current_path = os.path.dirname(current_path)
20
- path_list.append(current_path)
21
- # loop from root to current path to load zrb_init
22
- for current_path in path_list[::-1]:
23
- script_path = os.path.join(current_path, "zrb_init.py")
24
- if os.path.isfile(script_path):
25
- load_file(script_path)
26
-
27
-
28
12
  @lru_cache
29
13
  def load_file(script_path: str, sys_path_index: int = 0) -> Any | None:
30
14
  if not os.path.isfile(script_path):
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.1
2
+ Name: zrb
3
+ Version: 1.4.1
4
+ Summary: Your Automation Powerhouse
5
+ Home-page: https://github.com/state-alchemists/zrb
6
+ License: AGPL-3.0-or-later
7
+ Keywords: Automation,Task Runner,Code Generator,Monorepo,Low Code
8
+ Author: Go Frendi Gunawan
9
+ Author-email: gofrendiasgard@gmail.com
10
+ Requires-Python: >=3.10.0,<4.0.0
11
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Provides-Extra: rag
17
+ Requires-Dist: autopep8 (>=2.0.4,<3.0.0)
18
+ Requires-Dist: beautifulsoup4 (>=4.12.3,<5.0.0)
19
+ Requires-Dist: black (>=24.10.0,<24.11.0)
20
+ Requires-Dist: chromadb (>=0.5.20,<0.6.0) ; extra == "rag"
21
+ Requires-Dist: fastapi[standard] (>=0.115.6,<0.116.0)
22
+ Requires-Dist: fastembed (>=0.5.1,<0.6.0)
23
+ Requires-Dist: isort (>=5.13.2,<5.14.0)
24
+ Requires-Dist: libcst (>=1.5.0,<2.0.0)
25
+ Requires-Dist: pdfplumber (>=0.11.4,<0.12.0) ; extra == "rag"
26
+ Requires-Dist: psutil (>=6.1.1,<7.0.0)
27
+ Requires-Dist: pydantic-ai (>=0.0.42,<0.0.43)
28
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
29
+ Requires-Dist: python-jose[cryptography] (>=3.4.0,<4.0.0)
30
+ Requires-Dist: requests (>=2.32.3,<3.0.0)
31
+ Requires-Dist: ulid-py (>=1.1.0,<2.0.0)
32
+ Project-URL: Documentation, https://github.com/state-alchemists/zrb
33
+ Project-URL: Repository, https://github.com/state-alchemists/zrb
34
+ Description-Content-Type: text/markdown
35
+
36
+ ![](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/zrb/android-chrome-192x192.png)
37
+
38
+ [Documentation](https://github.com/state-alchemists/zrb/blob/main/docs/README.md)
39
+
40
+ # 🤖 Zrb: Your Automation Powerhouse
41
+
42
+
43
+ **Unlock the full potential of automation in your projects!**
44
+
45
+ Zrb streamlines repetitive tasks, integrates with powerful LLMs, and lets you create custom automation workflows effortlessly. Whether you’re building CI/CD pipelines, code generators, or unique automation scripts, Zrb is designed to simplify and supercharge your workflow.
46
+
47
+ ---
48
+
49
+ ## 🚀 Why Zrb?
50
+
51
+ - **Easy Automation with Python:** Write your tasks in Python and let Zrb handle the rest.
52
+ - **Seamless Integration:** Utilize built-in support for LLM tasks, command execution, and more.
53
+ - **Custom Workflows:** Chain tasks, set dependencies, and build robust automation pipelines.
54
+ - **Developer-Friendly:** Quick to install and get started, with clear documentation and examples.
55
+ - **Web Interface:** Run Zrb as a server to make tasks accessible even to non-technical team members.
56
+
57
+ ---
58
+
59
+ ## 🔥 Key Features
60
+
61
+ - **LLM Integration:** Leverage state-of-the-art language models to generate code, diagrams, and documentation.
62
+ - **Task Chaining:** Easily define dependencies between tasks to create complex workflows.
63
+ - **CLI & Server Mode:** Run tasks directly from the command line or through a user-friendly web UI.
64
+ - **Flexible Input Handling:** Defaults, prompts, and command-line parameters to suit any workflow.
65
+ - **Extensible & Open Source:** Contribute, customize, or extend Zrb to fit your unique needs.
66
+
67
+ ---
68
+
69
+ ## 🛠️ Getting Started
70
+
71
+ ### Quick Installation
72
+
73
+ Install Zrb via pip:
74
+
75
+ ```bash
76
+ pip install zrb
77
+
78
+ ```
79
+
80
+ Or run our installation script to set up Zrb along with all prerequisites:
81
+
82
+ ```bash
83
+ bash -c "$(curl -fsSL https://raw.githubusercontent.com/state-alchemists/zrb/main/install.sh)"
84
+
85
+ ```
86
+
87
+ ### Your First Task
88
+
89
+ Create a file at `/home/<your-user-name>/zrb_init.py` with the following content:
90
+
91
+
92
+ ```python
93
+ import os
94
+ from zrb import cli, llm_config, LLMTask, CmdTask, StrInput, Group
95
+ from zrb.builtin.llm.tool.file import read_all_files, write_text_file
96
+
97
+ CURRENT_DIR = os.getcwd()
98
+
99
+ # Make UML group
100
+ uml_group = cli.add_group(Group(name="uml", description="UML related tasks"))
101
+
102
+ # Generate UML script
103
+ make_uml_script = uml_group.add_task(
104
+ LLMTask(
105
+ name="make-script",
106
+ description="Creating plantuml diagram based on source code in current directory",
107
+ input=StrInput(name="diagram", default="state diagram"),
108
+ message=(
109
+ f"Read source code in {CURRENT_DIR}, "
110
+ "make a {ctx.input.diagram} in plantuml format. "
111
+ f"Write the script into {CURRENT_DIR}/{{ctx.input.diagram}}.uml"
112
+ ),
113
+ tools=[
114
+ read_all_files,
115
+ write_text_file,
116
+ ],
117
+ )
118
+ )
119
+
120
+ # Defining a Cmd Task to transform Plantuml script into a png image.
121
+ make_uml_image = uml_group.add_task(
122
+ CmdTask(
123
+ name="make-image",
124
+ description="Creating png based on source code in current directory",
125
+ input=StrInput(name="diagram", default="state diagram"),
126
+ cmd="plantuml -tpng '{ctx.input.diagram}.uml'",
127
+ cwd=CURRENT_DIR,
128
+ )
129
+ )
130
+
131
+ # Making sure that make_png has make_uml as its dependency.
132
+ make_uml_script >> make_uml_image
133
+ ```
134
+
135
+ You have just define two automation tasks.
136
+
137
+ The first one use LLM to read files in your current directory and create a `PlantUML script` on that directory.
138
+
139
+ The second task turn the PlantUML script into a `*.png` file. The second task depends on the first task and both of them are located under the same group.
140
+
141
+ You can run the tasks by invoking `zrb uml make-script` or `zrb uml make-image` respectively.
142
+
143
+ When you run zrb, it automatically searches for a file named `zrb_init.py` starting from your current directory and moving upward through its parent directories. This design lets you set up common automation tasks in a central location—like placing a `zrb_init.py` in your home directory (`/home/<your-user>/zrb_init.py`)—so that your tasks are available across all your projects.
144
+
145
+ Now, go to your project and create a state diagram:
146
+
147
+ ```bash
148
+ git clone git@github.com:jjinux/gotetris.git
149
+ cd gotetris
150
+ zrb uml make-image --diagram "state diagram"
151
+ ```
152
+
153
+ You can also invoke the task without specifying parameter.
154
+
155
+ ```bash
156
+ zrb uml make-image
157
+ ```
158
+
159
+ Once you do so, Zrb will ask you to provide the diagram type.
160
+
161
+ ```
162
+ diagram [state diagram]:
163
+ ```
164
+
165
+ You can just press enter if you want to use the default value (i.e., in this case `state diagram`).
166
+
167
+ Finally, you can also serve the tasks via a Web UI interface by invoking the following command:
168
+
169
+ ```bash
170
+ zrb server start
171
+ ```
172
+
173
+ You will have a nice web interface running on `http://localhost:12123`
174
+
175
+ ![Zrb Web UI](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/zrb-web-ui.png)
176
+
177
+ Now, let's see how things work in detail. First, Zrb generates a `state diagram.uml` in your current directory, it then transform the UML script into a PNG image `state diagram.png`.
178
+
179
+ ![State Diagram](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/state-diagram.png)
180
+
181
+
182
+ # 🎥 Demo & Documentation
183
+
184
+ - **Step by step guide:** [Getting started with Zrb](https://github.com/state-alchemists/zrb/blob/main/docs/recipes/getting-started/README.md).
185
+ - **Full documentation:** [Zrb Documentation](https://github.com/state-alchemists/zrb/blob/main/docs/README.md)
186
+ - **Video demo:** [![Video Title](https://img.youtube.com/vi/W7dgk96l__o/0.jpg)](https://www.youtube.com/watch?v=W7dgk96l__o)
187
+
188
+
189
+ # 🤝 Join the Community
190
+
191
+ - **Bug Reports & Feature Requests:** Create an [issue](https://github.com/state-alchemists/zrb/issues) on Zrb's GitHub Repositories and include:
192
+ - Your Zrb version (i.e., `zrb version`).
193
+ - Steps you’ve taken and what you expected versus what happened
194
+ - **Contributions:** We welcome pull requests! Check out our [contribution guidelines](https://github.com/state-alchemists/zrb/pulls).
195
+
196
+
197
+ # ☕ Support The Project
198
+
199
+ If you find Zrb valuable, please consider donating:
200
+
201
+ [![](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/donator.png)](https://stalchmst.com/donation)
202
+
203
+ # 🎉 Fun Fact
204
+
205
+ Did you know?
206
+
207
+ Zrb is named after `Zaruba`, a powerful support tool from the Garo universe!
208
+
209
+ > Madou Ring Zaruba (魔導輪ザルバ, Madōrin Zaruba) is a Madougu which supports bearers of the Garo Armor. [(Garo Wiki | Fandom)](https://garo.fandom.com/wiki/Zaruba)
210
+
211
+ ![Madou Ring Zaruba on Kouga's Hand](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/madou-ring-zaruba.jpg)
212
+
@@ -1,5 +1,5 @@
1
- zrb/__init__.py,sha256=JYLyBeSv-FP2iVKgsXJH8Ae-Cmjp5nmmIiwqayhCOEE,2964
2
- zrb/__main__.py,sha256=QcMnHfAFbDUFw9p9tgfFS4U0Ra9nE-TLU5YoMBiAriE,808
1
+ zrb/__init__.py,sha256=1waPjZcA3IHUEvIuVQso0YfNfW9i7SCJgEfzhiNTaCk,3020
2
+ zrb/__main__.py,sha256=MvAGzoM3ElJZOPMKNqaTdnrT9PXi9Saq8CPa11RiLQk,1748
3
3
  zrb/attr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  zrb/attr/type.py,sha256=4TV5gPYMMrKh5V-yB6iRYKCbsXAH_AvGXMsjxKLHcUs,568
5
5
  zrb/builtin/__init__.py,sha256=oXG4Zm_rIp3G81Y7hiSe38jeS2sGZAnADoP_yxxhYEc,1926
@@ -7,11 +7,11 @@ zrb/builtin/base64.py,sha256=1YnSwASp7OEAvQcsnHZGpJEvYoI1Z2zTIJ1bCDHfcPQ,921
7
7
  zrb/builtin/git.py,sha256=8_qVE_2lVQEVXQ9vhiw8Tn4Prj1VZB78ZjEJJS5Ab3M,5461
8
8
  zrb/builtin/git_subtree.py,sha256=7BKwOkVTWDrR0DXXQ4iJyHqeR6sV5VYRt8y_rEB0EHg,3505
9
9
  zrb/builtin/group.py,sha256=-phJfVpTX3_gUwS1u8-RbZUHe-X41kxDBSmrVh4rq8E,1682
10
- zrb/builtin/llm/llm_chat.py,sha256=yiL16XRy4oh5jKMzH0W7RBxfEwGKflvvrsYKleJFKWc,6221
10
+ zrb/builtin/llm/llm_chat.py,sha256=QCfxocM7UQPtpIWLMzr9wKbl9DCPcDZszAnPxszaww0,6071
11
11
  zrb/builtin/llm/previous-session.js,sha256=xMKZvJoAbrwiyHS0OoPrWuaKxWYLoyR5sguePIoCjTY,816
12
12
  zrb/builtin/llm/tool/api.py,sha256=bXFE7jihdhUscxJH8lu5imwlYH735AalbCyUTl28BaQ,826
13
13
  zrb/builtin/llm/tool/cli.py,sha256=to_IjkfrMGs6eLfG0cpVN9oyADWYsJQCtyluUhUdBww,253
14
- zrb/builtin/llm/tool/file.py,sha256=ibvh0zrsnponwyZvw6bWMUbpwSv5S5WUWCDfQ6BjVwk,1160
14
+ zrb/builtin/llm/tool/file.py,sha256=8H_qXt4imsy28DD21wBIo4ud5zfnkFjLLm8Simo84Q8,4732
15
15
  zrb/builtin/llm/tool/rag.py,sha256=vEIThEy0JGwXEiNRLOEJAHAE0l1Qie2qvU3ryioeYMk,6066
16
16
  zrb/builtin/llm/tool/web.py,sha256=SDnCtYHZ0Q4DtLbIhc11a0UyyKbTTeW60UfeIKzK35k,3204
17
17
  zrb/builtin/md5.py,sha256=0pNlrfZA0wlZlHvFHLgyqN0JZJWGKQIF5oXxO44_OJk,949
@@ -208,7 +208,7 @@ zrb/callback/callback.py,sha256=hKefB_Jd1XGjPSLQdMKDsGLHPzEGO2dqrIArLl_EmD0,848
208
208
  zrb/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
209
209
  zrb/cmd/cmd_result.py,sha256=L8bQJzWCpcYexIxHBNsXj2pT3BtLmWex0iJSMkvimOA,597
210
210
  zrb/cmd/cmd_val.py,sha256=7Doowyg6BK3ISSGBLt-PmlhzaEkBjWWm51cED6fAUOQ,1014
211
- zrb/config.py,sha256=MfHwcQ4OhCmCw6jXpFI8483Ase6YrqNGBvqYzwnwopw,4753
211
+ zrb/config.py,sha256=X0mlhmpUrYp_l4qI3CnsqOAfvxfLkteCOV9ABGF--Qc,4690
212
212
  zrb/content_transformer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
213
  zrb/content_transformer/any_content_transformer.py,sha256=v8ZUbcix1GGeDQwB6OKX_1TjpY__ksxWVeqibwa_iZA,850
214
214
  zrb/content_transformer/content_transformer.py,sha256=STl77wW-I69QaGzCXjvkppngYFLufow8ybPLSyAvlHs,2404
@@ -237,8 +237,9 @@ zrb/input/option_input.py,sha256=TQB82ko5odgzkULEizBZi0e9TIHEbIgvdP0AR3RhA74,213
237
237
  zrb/input/password_input.py,sha256=szBojWxSP9QJecgsgA87OIYwQrY2AQ3USIKdDZY6snU,1465
238
238
  zrb/input/str_input.py,sha256=NevZHX9rf1g8eMatPyy-kUX3DglrVAQpzvVpKAzf7bA,81
239
239
  zrb/input/text_input.py,sha256=shvVbc2U8Is36h23M5lcW8IEwKc9FR-4uEPZZroj3rU,3377
240
+ zrb/llm_config.py,sha256=SXSkDpmXxGLJaoUrT09oNdOGwHXc82TwIGssVeo6S7U,1553
240
241
  zrb/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
241
- zrb/runner/cli.py,sha256=HtfJQecFg2keIMK-7bVATBlTeZC_RvT8DsbUC58TMKU,6736
242
+ zrb/runner/cli.py,sha256=0mT0oO_yEhc8N4nYCJNujhgLjVykZ0B-kAOFXyAvAqM,6672
242
243
  zrb/runner/common_util.py,sha256=0zhZn1Jdmr194_nsL5_L-Kn9-_NDpMTI2z6_LXUQJ-U,1369
243
244
  zrb/runner/web_app.py,sha256=Ji2AWeFpJu5guXmur7mAAbjMToyjgmPDdfYu8047FFI,2616
244
245
  zrb/runner/web_config/config.py,sha256=0wR58KreAmawGGfamm0GLZY344HaXs7qfDgHLavBDwo,3125
@@ -299,7 +300,7 @@ zrb/task/base_task.py,sha256=SQRf37bylS586KwyW0eYDe9JZ5Hl18FP8kScHae6y3A,21251
299
300
  zrb/task/base_trigger.py,sha256=jC722rDvodaBLeNaFghkTyv1u0QXrK6BLZUUqcmBJ7Q,4581
300
301
  zrb/task/cmd_task.py,sha256=pUKRSR4DZKjbmluB6vi7cxqyhxOLfJ2czSpYeQbiDvo,10705
301
302
  zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
302
- zrb/task/llm_task.py,sha256=B4qhza-4fk7odI7-rv2rLYvBLt1dmZMNgKu8OK7rajM,11849
303
+ zrb/task/llm_task.py,sha256=Gf_Y8e3-U46wjnH5K36I1XJnFwwU-eTQlG5JL87UobM,13495
303
304
  zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
304
305
  zrb/task/rsync_task.py,sha256=GSL9144bmp6F0EckT6m-2a1xG25AzrrWYzH4k3SVUKM,6370
305
306
  zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
@@ -331,7 +332,7 @@ zrb/util/file.py,sha256=mgNobIKCr0eIQUlg6W2Yg1fvg943VyuOUF8WMFpJA5A,859
331
332
  zrb/util/git.py,sha256=TShnMxPAk20Tglp25d_XPVZX-q0mvKeqdprVMeXQ5f0,4787
332
333
  zrb/util/git_subtree.py,sha256=zyWl0aUEZJyUJKjfw1uglozB4R1kF9pWtfKjhu8DN44,2658
333
334
  zrb/util/group.py,sha256=Bg7HrSycoK110U5s_Tca6-uUQuZ5CMgb8wxZSrvDQ98,2790
334
- zrb/util/load.py,sha256=m0e5DVLV7_RON6AZdjkquPym6BBrvYBbDMrllV44Y_k,1993
335
+ zrb/util/load.py,sha256=Aeyh1EWtp-oJGVAhcjZn-VSB9innoOe8ZkUawha_ddk,1339
335
336
  zrb/util/run.py,sha256=DGHUP9x1Q8V8UF3FbpmjLGuhVVCCLfjTH2teT8qXlNI,207
336
337
  zrb/util/string/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
337
338
  zrb/util/string/conversion.py,sha256=636sfF1a3_bpzXNw5bdSzbJzwakyAoo70KT3_ItgZEo,4333
@@ -340,7 +341,7 @@ zrb/util/string/name.py,sha256=8picJfUBXNpdh64GNaHv3om23QHhUZux7DguFLrXHp8,1163
340
341
  zrb/util/todo.py,sha256=1nDdwPc22oFoK_1ZTXyf3638Bg6sqE2yp_U4_-frHoc,16015
341
342
  zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
342
343
  zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
343
- zrb-1.3.1.dist-info/METADATA,sha256=xYoLV2fDDSGBjJdgYOjXNy1rfAifx7VsGxIAGHCpBns,6303
344
- zrb-1.3.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
345
- zrb-1.3.1.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
346
- zrb-1.3.1.dist-info/RECORD,,
344
+ zrb-1.4.1.dist-info/METADATA,sha256=qKftyv7rxRUk1eiIo5hUOZJY80melIT4RpgAPXVa1Ew,8135
345
+ zrb-1.4.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
346
+ zrb-1.4.1.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
347
+ zrb-1.4.1.dist-info/RECORD,,
@@ -1,175 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: zrb
3
- Version: 1.3.1
4
- Summary: Your Automation Powerhouse
5
- Home-page: https://github.com/state-alchemists/zrb
6
- License: AGPL-3.0-or-later
7
- Keywords: Automation,Task Runner,Code Generator,Monorepo,Low Code
8
- Author: Go Frendi Gunawan
9
- Author-email: gofrendiasgard@gmail.com
10
- Requires-Python: >=3.10.0,<4.0.0
11
- Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Provides-Extra: rag
17
- Requires-Dist: autopep8 (>=2.0.4,<3.0.0)
18
- Requires-Dist: beautifulsoup4 (>=4.12.3,<5.0.0)
19
- Requires-Dist: black (>=24.10.0,<24.11.0)
20
- Requires-Dist: chromadb (>=0.5.20,<0.6.0) ; extra == "rag"
21
- Requires-Dist: fastapi[standard] (>=0.115.6,<0.116.0)
22
- Requires-Dist: fastembed (>=0.5.1,<0.6.0)
23
- Requires-Dist: isort (>=5.13.2,<5.14.0)
24
- Requires-Dist: libcst (>=1.5.0,<2.0.0)
25
- Requires-Dist: pdfplumber (>=0.11.4,<0.12.0) ; extra == "rag"
26
- Requires-Dist: psutil (>=6.1.1,<7.0.0)
27
- Requires-Dist: pydantic-ai (>=0.0.31,<0.0.32)
28
- Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
29
- Requires-Dist: python-jose[cryptography] (>=3.4.0,<4.0.0)
30
- Requires-Dist: requests (>=2.32.3,<3.0.0)
31
- Requires-Dist: ulid-py (>=1.1.0,<2.0.0)
32
- Project-URL: Documentation, https://github.com/state-alchemists/zrb
33
- Project-URL: Repository, https://github.com/state-alchemists/zrb
34
- Description-Content-Type: text/markdown
35
-
36
- ![](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/zrb/android-chrome-192x192.png)
37
-
38
- [Documentation](https://github.com/state-alchemists/zrb/blob/main/docs/README.md)
39
-
40
- # 🤖 Zrb: Your Automation Powerhouse
41
-
42
- Zrb allows you to write your automation tasks in Python. For example, you can define the following script in your home directory (`/home/<your-user-name>/zrb_init.py`).
43
-
44
-
45
- ```python
46
- import os
47
- from zrb import cli, LLMTask, CmdTask, StrInput
48
- from zrb.builtin.llm.tool.file import read_source_code, write_text_file
49
- from pydantic_ai.models.openai import OpenAIModel
50
-
51
-
52
- CURRENT_DIR = os.getcwd()
53
- OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
54
- OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
55
- OPENROUTER_MODEL_NAME = os.getenv(
56
- "AGENT_MODEL_NAME", "anthropic/claude-3.7-sonnet"
57
- )
58
-
59
-
60
- # Defining a LLM Task to create a Plantuml script based on source code in current directory.
61
- # User can choose the diagram type. By default it is "state diagram"
62
- make_uml = cli.add_task(
63
- LLMTask(
64
- name="make-uml",
65
- description="Creating plantuml diagram based on source code in current directory",
66
- input=StrInput(name="diagram", default="state diagram"),
67
- model=OpenAIModel(
68
- OPENROUTER_MODEL_NAME,
69
- base_url=OPENROUTER_BASE_URL,
70
- api_key=OPENROUTER_API_KEY,
71
- ),
72
- message=(
73
- f"Read source code in {CURRENT_DIR}, "
74
- "make a {ctx.input.diagram} in plantuml format. "
75
- f"Write the script into {CURRENT_DIR}/{{ctx.input.diagram}}.uml"
76
- ),
77
- tools=[
78
- read_source_code,
79
- write_text_file,
80
- ],
81
- )
82
- )
83
-
84
- # Defining a Cmd Task to transform Plantuml script into a png image.
85
- make_png = cli.add_task(
86
- CmdTask(
87
- name="make-png",
88
- description="Creating png based on source code in current directory",
89
- input=StrInput(name="diagram", default="state diagram"),
90
- cmd="plantuml -tpng '{ctx.input.diagram}.uml'",
91
- cwd=CURRENT_DIR,
92
- )
93
- )
94
-
95
- # Making sure that make_png has make_uml as its dependency.
96
- make_uml >> make_png
97
- ```
98
-
99
- Once defined, your automation tasks are immediately accessible from the CLI. You can then invoke the tasks by invoking.
100
-
101
- ```bash
102
- zrb make-png --diagram "state diagram"
103
- ```
104
-
105
- Or you can invoke the tasks without parameter.
106
-
107
- ```bash
108
- zrb make-png
109
- ```
110
-
111
- At this point, Zrb will politely ask you to provide the diagram type.
112
-
113
- ```
114
- diagram [state diagram]:
115
- ```
116
-
117
- You can just press enter if you want to use the default value.
118
-
119
- Finally, you can run Zrb as a server and make your tasks available for non technical users by invoking the following command.
120
-
121
- ```bash
122
- zrb server start
123
- ```
124
-
125
- You will have a nice web interface running on `http://localhost:12123`
126
-
127
- ![Zrb Web UI](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/zrb-web-ui.png)
128
-
129
- Now, let's see how Zrb generate the state diagram. Based on the source code in your current directory, Zrb will generate a `state diagram.uml` and transform it into `state diagram.png`.
130
-
131
- ![State Diagram](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/state-diagram.png)
132
-
133
- See the [getting started guide](https://github.com/state-alchemists/zrb/blob/main/docs/recipes/getting-started/README.md) for more information. Or just watch the demo:
134
-
135
- [![Video Title](https://img.youtube.com/vi/W7dgk96l__o/0.jpg)](https://www.youtube.com/watch?v=W7dgk96l__o)
136
-
137
-
138
- # 🫰 Installing Zrb
139
-
140
- You can install Zrb as a pip package by invoking the following command:
141
-
142
- ```bash
143
- pip install --pre zrb
144
- ```
145
-
146
- Alternatively, you can also use our installation script to install Zrb along with some prerequisites:
147
-
148
- ```bash
149
- bash -c "$(curl -fsSL https://raw.githubusercontent.com/state-alchemists/zrb/main/install.sh)"
150
- ```
151
-
152
- # 🐞 Bug Report + Feature Request
153
-
154
- You can submit bug reports and feature requests by creating a new [issue](https://github.com/state-alchemists/zrb/issues) on Zrb's GitHub Repositories. When reporting a bug or requesting a feature, please be sure to:
155
-
156
- - Include the version of Zrb you are using (i.e., `zrb version`)
157
- - Tell us what you have tried
158
- - Tell us what you expect
159
- - Tell us what you get
160
-
161
- We will also welcome your [pull requests and contributions](https://github.com/state-alchemists/zrb/pulls).
162
-
163
-
164
- # ☕ Donation
165
-
166
- Help Red Skull to click the donation button:
167
-
168
- [![](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/donator.png)](https://stalchmst.com/donation)
169
-
170
- # 🎉 Fun Fact
171
-
172
- > Madou Ring Zaruba (魔導輪ザルバ, Madōrin Zaruba) is a Madougu which supports bearers of the Garo Armor. [(Garo Wiki | Fandom)](https://garo.fandom.com/wiki/Zaruba)
173
-
174
- ![Madou Ring Zaruba on Kouga's Hand](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/madou-ring-zaruba.jpg)
175
-
File without changes