fabricatio 0.1.0__py3-none-any.whl → 0.1.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.
fabricatio/__init__.py CHANGED
@@ -1,18 +1,24 @@
1
- from fabricatio.core import Env
2
- from fabricatio.logger import logger
3
- from fabricatio.models.action import Action, WorkFlow
4
- from fabricatio.models.events import Event
5
- from fabricatio.models.role import Role
6
- from fabricatio.models.tool import ToolBox
7
- from fabricatio.models.utils import Messages
8
-
9
- __all__ = [
10
- "Env",
11
- "logger",
12
- "Action",
13
- "Event",
14
- "Messages",
15
- "Role",
16
- "ToolBox",
17
- "WorkFlow",
18
- ]
1
+ """Fabricatio is a Python library for building llm app using event-based agent structure."""
2
+
3
+ from fabricatio.core import env
4
+ from fabricatio.journal import logger
5
+ from fabricatio.models.action import Action, WorkFlow
6
+ from fabricatio.models.events import Event
7
+ from fabricatio.models.role import Role
8
+ from fabricatio.models.task import Task
9
+ from fabricatio.models.tool import ToolBox
10
+ from fabricatio.models.utils import Messages
11
+ from fabricatio.parser import Capture
12
+
13
+ __all__ = [
14
+ "Action",
15
+ "Capture",
16
+ "Event",
17
+ "Messages",
18
+ "Role",
19
+ "Task",
20
+ "ToolBox",
21
+ "WorkFlow",
22
+ "env",
23
+ "logger",
24
+ ]
fabricatio/config.py CHANGED
@@ -1,19 +1,35 @@
1
1
  from typing import Literal
2
2
 
3
3
  from appdirs import user_config_dir
4
- from pydantic import BaseModel, HttpUrl, SecretStr, PositiveInt, NonNegativeFloat, Field, FilePath
4
+ from pydantic import BaseModel, ConfigDict, Field, FilePath, HttpUrl, NonNegativeFloat, PositiveInt, SecretStr
5
5
  from pydantic_settings import (
6
6
  BaseSettings,
7
- SettingsConfigDict,
7
+ DotEnvSettingsSource,
8
+ EnvSettingsSource,
8
9
  PydanticBaseSettingsSource,
9
- TomlConfigSettingsSource,
10
10
  PyprojectTomlConfigSettingsSource,
11
- EnvSettingsSource,
12
- DotEnvSettingsSource,
11
+ SettingsConfigDict,
12
+ TomlConfigSettingsSource,
13
13
  )
14
14
 
15
15
 
16
16
  class LLMConfig(BaseModel):
17
+ """LLM configuration class.
18
+
19
+ Attributes:
20
+ api_endpoint (HttpUrl): OpenAI API Endpoint.
21
+ api_key (SecretStr): OpenAI API key. Empty by default for security reasons, should be set before use.
22
+ timeout (PositiveInt): The timeout of the LLM model in seconds. Default is 300 seconds as per request.
23
+ max_retries (PositiveInt): The maximum number of retries. Default is 3 retries.
24
+ model (str): The LLM model name. Set to 'gpt-3.5-turbo' as per request.
25
+ temperature (NonNegativeFloat): The temperature of the LLM model. Controls randomness in generation. Set to 1.0 as per request.
26
+ stop_sign (str): The stop sign of the LLM model. No default stop sign specified.
27
+ top_p (NonNegativeFloat): The top p of the LLM model. Controls diversity via nucleus sampling. Set to 0.35 as per request.
28
+ generation_count (PositiveInt): The number of generations to generate. Default is 1.
29
+ stream (bool): Whether to stream the LLM model's response. Default is False.
30
+ max_tokens (PositiveInt): The maximum number of tokens to generate. Set to 8192 as per request.
31
+ """
32
+ model_config = ConfigDict(use_attribute_docstrings=True)
17
33
  api_endpoint: HttpUrl = Field(default=HttpUrl("https://api.openai.com"))
18
34
  """
19
35
  OpenAI API Endpoint.
@@ -70,24 +86,65 @@ class LLMConfig(BaseModel):
70
86
  """
71
87
 
72
88
 
89
+ class PymitterConfig(BaseModel):
90
+ """Pymitter configuration class.
91
+
92
+ Attributes:
93
+ delimiter (str): The delimiter used to separate the event name into segments.
94
+ new_listener_event (bool): If set, a newListener event is emitted when a new listener is added.
95
+ max_listeners (int): The maximum number of listeners per event.
96
+ """
97
+ model_config = ConfigDict(use_attribute_docstrings=True)
98
+ delimiter: str = Field(default=".", frozen=True)
99
+ """
100
+ The delimiter used to separate the event name into segments.
101
+ """
102
+
103
+ new_listener_event: bool = Field(default=False, frozen=True)
104
+ """
105
+ If set, a newListener event is emitted when a new listener is added.
106
+ """
107
+
108
+ max_listeners: int = Field(default=-1, frozen=True)
109
+ """
110
+ The maximum number of listeners per event.
111
+ """
112
+
113
+
73
114
  class DebugConfig(BaseModel):
115
+ """Debug configuration class.
116
+
117
+ Attributes:
118
+ log_level (Literal["DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"]): The log level of the application.
119
+ log_file (FilePath): The log file of the application.
120
+ """
121
+ model_config = ConfigDict(use_attribute_docstrings=True)
122
+
74
123
  log_level: Literal["DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"] = Field(default="INFO")
75
124
  """
76
125
  The log level of the application.
77
126
  """
78
127
 
79
- log_file: FilePath = Field(default=f"{user_config_dir("fabricatio", roaming=True)}.log")
128
+ log_file: FilePath = Field(default=f"{user_config_dir('fabricatio', roaming=True)}.log")
80
129
  """
81
130
  The log file of the application.
82
131
  """
83
132
 
84
133
 
85
134
  class Settings(BaseSettings):
135
+ """Application settings class.
136
+
137
+ Attributes:
138
+ llm (LLMConfig): LLM Configuration
139
+ debug (DebugConfig): Debug Configuration
140
+ pymitter (PymitterConfig): Pymitter Configuration
141
+ """
86
142
  model_config = SettingsConfigDict(
87
143
  env_prefix="FABRIK_",
88
144
  env_nested_delimiter="__",
89
145
  pyproject_toml_depth=1,
90
- toml_file=["fabricatio.toml", f"{user_config_dir("fabricatio", roaming=True)}.toml"],
146
+ pyproject_toml_table_header=("tool", "fabricatio"),
147
+ toml_file=["fabricatio.toml", f"{user_config_dir('fabricatio', roaming=True)}.toml"],
91
148
  env_file=[".env", ".envrc"],
92
149
  use_attribute_docstrings=True,
93
150
  )
@@ -102,15 +159,32 @@ class Settings(BaseSettings):
102
159
  Debug Configuration
103
160
  """
104
161
 
162
+ pymitter: PymitterConfig = Field(default_factory=PymitterConfig)
163
+ """
164
+ Pymitter Configuration
165
+ """
166
+
105
167
  @classmethod
106
168
  def settings_customise_sources(
107
- cls,
108
- settings_cls: type[BaseSettings],
109
- init_settings: PydanticBaseSettingsSource,
110
- env_settings: PydanticBaseSettingsSource,
111
- dotenv_settings: PydanticBaseSettingsSource,
112
- file_secret_settings: PydanticBaseSettingsSource,
169
+ cls,
170
+ settings_cls: type[BaseSettings],
171
+ init_settings: PydanticBaseSettingsSource,
172
+ env_settings: PydanticBaseSettingsSource,
173
+ dotenv_settings: PydanticBaseSettingsSource,
174
+ file_secret_settings: PydanticBaseSettingsSource,
113
175
  ) -> tuple[PydanticBaseSettingsSource, ...]:
176
+ """Customize settings sources.
177
+
178
+ Args:
179
+ settings_cls (type[BaseSettings]): The settings class.
180
+ init_settings (PydanticBaseSettingsSource): Initial settings source.
181
+ env_settings (PydanticBaseSettingsSource): Environment settings source.
182
+ dotenv_settings (PydanticBaseSettingsSource): Dotenv settings source.
183
+ file_secret_settings (PydanticBaseSettingsSource): File secret settings source.
184
+
185
+ Returns:
186
+ tuple[PydanticBaseSettingsSource, ...]: A tuple of settings sources.
187
+ """
114
188
  return (
115
189
  DotEnvSettingsSource(settings_cls),
116
190
  EnvSettingsSource(settings_cls),
fabricatio/core.py CHANGED
@@ -1,148 +1,165 @@
1
- from typing import Callable, Self, overload
2
-
3
- from pydantic import BaseModel, ConfigDict, PrivateAttr
4
- from pymitter import EventEmitter
5
-
6
- from fabricatio.models.events import Event
7
-
8
-
9
- class Env(BaseModel):
10
- """
11
- Environment class that manages event handling using EventEmitter.
12
-
13
- Attributes:
14
- _ee (EventEmitter): Private attribute for event handling.
15
- """
16
-
17
- model_config = ConfigDict(use_attribute_docstrings=True)
18
- _ee: EventEmitter = PrivateAttr(default_factory=EventEmitter)
19
-
20
- @overload
21
- def on(self, event: str | Event, /, ttl: int = -1) -> Self:
22
- """
23
- Registers an event listener that listens indefinitely or for a specified number of times.
24
-
25
- Args:
26
- event (str | Event): The event to listen for.
27
- ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
28
-
29
- Returns:
30
- Self: The current instance of Env.
31
- """
32
- ...
33
-
34
- @overload
35
- def on[**P, R](
36
- self, event: str | Event, func: Callable[P, R] = None, /, ttl: int = -1
37
- ) -> Callable[[Callable[P, R]], Callable[P, R]]:
38
- """
39
- Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
40
-
41
- Args:
42
- event (str | Event): The event to listen for.
43
- func (Callable[P, R]): The function to be called when the event is emitted.
44
- ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
45
-
46
- Returns:
47
- Callable[[Callable[P, R]], Callable[P, R]]: A decorator that registers the function as an event listener.
48
- """
49
- ...
50
-
51
- def on[**P, R](
52
- self,
53
- event: str | Event,
54
- func: Callable[P, R] = None,
55
- /,
56
- ttl=-1,
57
- ) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
58
- """
59
- Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
60
- Args:
61
- event (str | Event): The event to listen for.
62
- func (Callable[P, R]): The function to be called when the event is emitted.
63
- ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
64
-
65
- Returns:
66
- Callable[[Callable[P, R]], Callable[P, R]] | Self: A decorator that registers the function as an event listener or the current instance of Env.
67
- """
68
- if isinstance(event, Event):
69
- event = event.collapse()
70
- if func is None:
71
- return self._ee.on(event, ttl=ttl)
72
-
73
- else:
74
- self._ee.on(event, func, ttl=ttl)
75
- return self
76
-
77
- @overload
78
- def once[**P, R](
79
- self,
80
- event: str | Event,
81
- ) -> Callable[[Callable[P, R]], Callable[P, R]]:
82
- """
83
- Registers an event listener that listens only once.
84
-
85
- Args:
86
- event (str | Event): The event to listen for.
87
-
88
- Returns:
89
- Callable[[Callable[P, R]], Callable[P, R]]: A decorator that registers the function as an event listener.
90
- """
91
- ...
92
-
93
- @overload
94
- def once[**P, R](self, event: str | Event, func: Callable[[Callable[P, R]], Callable[P, R]]) -> Self:
95
- """
96
- Registers an event listener with a specific function that listens only once.
97
-
98
- Args:
99
- event (str | Event): The event to listen for.
100
- func (Callable[P, R]): The function to be called when the event is emitted.
101
-
102
- Returns:
103
- Self: The current instance of Env.
104
- """
105
- ...
106
-
107
- def once[**P, R](
108
- self, event: str | Event, func: Callable[P, R] = None
109
- ) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
110
- """
111
-
112
- Args:
113
- event (str | Event): The event to listen for.
114
- func (Callable[P, R]): The function to be called when the event is emitted.
115
-
116
- Returns:
117
- Callable[[Callable[P, R]], Callable[P, R]] | Self: A decorator that registers the function as an event listener or the current instance
118
- """
119
- if isinstance(event, Event):
120
- event = event.collapse()
121
- if func is None:
122
- return self._ee.once(event)
123
-
124
- else:
125
- self._ee.once(event, func)
126
- return self
127
-
128
- def emit[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
129
- """
130
- Emits an event to all registered listeners.
131
-
132
- Args:
133
- event (str | Event): The event to emit.
134
- *args: Positional arguments to pass to the listeners.
135
- **kwargs: Keyword arguments to pass to the listeners.
136
- """
137
- self._ee.emit(event, *args, **kwargs)
138
-
139
- async def emit_async[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
140
- """
141
- Asynchronously emits an event to all registered listeners.
142
-
143
- Args:
144
- event (str | Event): The event to emit.
145
- *args: Positional arguments to pass to the listeners.
146
- **kwargs: Keyword arguments to pass to the listeners.
147
- """
148
- return await self._ee.emit_async(event, *args, **kwargs)
1
+ from typing import Callable, Optional, Self, overload
2
+
3
+ from pydantic import BaseModel, ConfigDict, PrivateAttr
4
+ from pymitter import EventEmitter
5
+
6
+ from fabricatio.config import configs
7
+ from fabricatio.models.events import Event
8
+
9
+
10
+ class Env(BaseModel):
11
+ """Environment class that manages event handling using EventEmitter."""
12
+
13
+ model_config = ConfigDict(use_attribute_docstrings=True)
14
+ _ee: EventEmitter = PrivateAttr(
15
+ default_factory=lambda: EventEmitter(
16
+ delimiter=configs.pymitter.delimiter,
17
+ new_listener=configs.pymitter.new_listener_event,
18
+ max_listeners=configs.pymitter.max_listeners,
19
+ wildcard=True,
20
+ )
21
+ )
22
+
23
+ @overload
24
+ def on(self, event: str | Event, /, ttl: int = -1) -> Self:
25
+ """
26
+ Registers an event listener that listens indefinitely or for a specified number of times.
27
+
28
+ Args:
29
+ event (str | Event): The event to listen for.
30
+ ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
31
+
32
+ Returns:
33
+ Self: The current instance of Env.
34
+ """
35
+ ...
36
+
37
+ @overload
38
+ def on[**P, R](
39
+ self,
40
+ event: str | Event,
41
+ func: Optional[Callable[P, R]] = None,
42
+ /,
43
+ ttl: int = -1,
44
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
45
+ """
46
+ Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
47
+
48
+ Args:
49
+ event (str | Event): The event to listen for.
50
+ func (Callable[P, R]): The function to be called when the event is emitted.
51
+ ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
52
+
53
+ Returns:
54
+ Callable[[Callable[P, R]], Callable[P, R]]: A decorator that registers the function as an event listener.
55
+ """
56
+ ...
57
+
58
+ def on[**P, R](
59
+ self,
60
+ event: str | Event,
61
+ func: Optional[Callable[P, R]] = None,
62
+ /,
63
+ ttl=-1,
64
+ ) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
65
+ """Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
66
+
67
+ Args:
68
+ event (str | Event): The event to listen for.
69
+ func (Callable[P, R]): The function to be called when the event is emitted.
70
+ ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
71
+
72
+ Returns:
73
+ Callable[[Callable[P, R]], Callable[P, R]] | Self: A decorator that registers the function as an event listener or the current instance of Env.
74
+ """
75
+ if isinstance(event, Event):
76
+ event = event.collapse()
77
+ if func is None:
78
+ return self._ee.on(event, ttl=ttl)
79
+
80
+ self._ee.on(event, func, ttl=ttl)
81
+ return self
82
+
83
+ @overload
84
+ def once[**P, R](
85
+ self,
86
+ event: str | Event,
87
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
88
+ """
89
+ Registers an event listener that listens only once.
90
+
91
+ Args:
92
+ event (str | Event): The event to listen for.
93
+
94
+ Returns:
95
+ Callable[[Callable[P, R]], Callable[P, R]]: A decorator that registers the function as an event listener.
96
+ """
97
+ ...
98
+
99
+ @overload
100
+ def once[**P, R](
101
+ self,
102
+ event: str | Event,
103
+ func: Callable[[Callable[P, R]], Callable[P, R]],
104
+ ) -> Self:
105
+ """
106
+ Registers an event listener with a specific function that listens only once.
107
+
108
+ Args:
109
+ event (str | Event): The event to listen for.
110
+ func (Callable[P, R]): The function to be called when the event is emitted.
111
+
112
+ Returns:
113
+ Self: The current instance of Env.
114
+ """
115
+ ...
116
+
117
+ def once[**P, R](
118
+ self,
119
+ event: str | Event,
120
+ func: Optional[Callable[P, R]] = None,
121
+ ) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
122
+ """Registers an event listener with a specific function that listens only once.
123
+
124
+ Args:
125
+ event (str | Event): The event to listen for.
126
+ func (Callable[P, R]): The function to be called when the event is emitted.
127
+
128
+ Returns:
129
+ Callable[[Callable[P, R]], Callable[P, R]] | Self: A decorator that registers the function as an event listener or the current instance
130
+ """
131
+ if isinstance(event, Event):
132
+ event = event.collapse()
133
+ if func is None:
134
+ return self._ee.once(event)
135
+
136
+ self._ee.once(event, func)
137
+ return self
138
+
139
+ def emit[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
140
+ """Emits an event to all registered listeners.
141
+
142
+ Args:
143
+ event (str | Event): The event to emit.
144
+ *args: Positional arguments to pass to the listeners.
145
+ **kwargs: Keyword arguments to pass to the listeners.
146
+ """
147
+ if isinstance(event, Event):
148
+ event = event.collapse()
149
+
150
+ self._ee.emit(event, *args, **kwargs)
151
+
152
+ async def emit_async[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
153
+ """Asynchronously emits an event to all registered listeners.
154
+
155
+ Args:
156
+ event (str | Event): The event to emit.
157
+ *args: Positional arguments to pass to the listeners.
158
+ **kwargs: Keyword arguments to pass to the listeners.
159
+ """
160
+ if isinstance(event, Event):
161
+ event = event.collapse()
162
+ return await self._ee.emit_async(event, *args, **kwargs)
163
+
164
+
165
+ env = Env()
@@ -1,11 +1,16 @@
1
+ import sys
2
+
1
3
  from loguru import logger
2
4
  from rich import traceback
3
5
 
4
6
  from fabricatio.config import configs
5
7
 
6
8
  traceback.install()
7
- logger.level(configs.debug.log_level)
8
- logger.add(configs.debug.log_file, rotation="1 weeks", retention="1 month", compression="zip")
9
+ logger.remove()
10
+ logger.add(
11
+ configs.debug.log_file, level=configs.debug.log_level, rotation="1 weeks", retention="1 month", compression="zip"
12
+ )
13
+ logger.add(sys.stderr, level=configs.debug.log_level)
9
14
 
10
15
  if __name__ == "__main__":
11
16
  logger.debug("This is a trace message.")
@@ -1,22 +1,90 @@
1
+ import traceback
1
2
  from abc import abstractmethod
2
- from typing import Tuple
3
+ from asyncio import Queue
4
+ from typing import Any, Dict, Tuple, Type, Unpack
3
5
 
4
- from pydantic import Field
6
+ from pydantic import Field, PrivateAttr
5
7
 
6
- from fabricatio.models.generic import WithBriefing, LLMUsage
8
+ from fabricatio.journal import logger
9
+ from fabricatio.models.generic import LLMUsage, WithBriefing
10
+ from fabricatio.models.task import Task
7
11
 
8
12
 
9
13
  class Action(WithBriefing, LLMUsage):
14
+ """Class that represents an action to be executed in a workflow."""
15
+
16
+ output_key: str = Field(default="")
17
+ """ The key of the output data."""
10
18
 
11
19
  @abstractmethod
12
- async def execute(self, *args, **kwargs):
20
+ async def _execute(self, **cxt: Unpack) -> Any:
21
+ """Execute the action with the provided arguments.
22
+
23
+ Args:
24
+ **cxt: The context dictionary containing input and output data.
25
+
26
+ Returns:
27
+ The result of the action execution.
28
+ """
13
29
  pass
14
30
 
31
+ async def act(self, cxt: Dict[str, Any]) -> Dict[str, Any]:
32
+ """Perform the action by executing it and setting the output data.
33
+
34
+ Args:
35
+ cxt: The context dictionary containing input and output data.
36
+ """
37
+ ret = await self._execute(**cxt)
38
+ if self.output_key:
39
+ logger.debug(f"Setting output: {self.output_key}")
40
+ cxt[self.output_key] = ret
41
+ return cxt
42
+
15
43
 
16
44
  class WorkFlow(WithBriefing, LLMUsage):
17
- steps: Tuple[Action, ...] = Field(default=())
45
+ """Class that represents a workflow to be executed in a task."""
46
+
47
+ _context: Queue[Dict[str, Any]] = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
48
+ """ The context dictionary to be used for workflow execution."""
49
+
50
+ _instances: Tuple[Action, ...] = PrivateAttr(...)
51
+
52
+ steps: Tuple[Type[Action], ...] = Field(...)
53
+ """ The steps to be executed in the workflow."""
54
+ task_input_key: str = Field(default="task_input")
55
+ """ The key of the task input data."""
56
+ task_output_key: str = Field(default="task_output")
57
+ """ The key of the task output data."""
58
+
59
+ def model_post_init(self, __context: Any) -> None:
60
+ """Initialize the workflow by setting fallbacks for each step.
61
+
62
+ Args:
63
+ __context: The context to be used for initialization.
64
+ """
65
+ self._instances = tuple(step() for step in self.steps)
66
+ for step in self._instances:
67
+ step.fallback_to(self)
68
+
69
+ async def serve(self, task: Task) -> None:
70
+ """Serve the task by executing the workflow steps.
18
71
 
19
- async def execute(self, *args, **kwargs):
20
- # TODO dispatch params to each step according to the step's signature
21
- for step in self.steps:
22
- await step.execute(*args, **kwargs)
72
+ Args:
73
+ task: The task to be served.
74
+ """
75
+ await task.start()
76
+ await self._context.put({self.task_input_key: task})
77
+ current_action = None
78
+ try:
79
+ for step in self._instances:
80
+ logger.debug(f"Executing step: {step.name}")
81
+ ctx = await self._context.get()
82
+ modified_ctx = await step.act(ctx)
83
+ await self._context.put(modified_ctx)
84
+ current_action = step.name
85
+ logger.info(f"Finished executing workflow: {self.name}")
86
+ await task.finish((await self._context.get()).get(self.task_output_key, None))
87
+ except RuntimeError as e:
88
+ logger.error(f"Error during task: {current_action} execution: {e}") # Log the exception
89
+ logger.error(traceback.format_exc()) # Add this line to log the traceback
90
+ await task.fail() # Mark the task as failed