fabricatio 0.2.3.dev2__cp312-cp312-manylinux_2_34_x86_64.whl → 0.2.3.dev3__cp312-cp312-manylinux_2_34_x86_64.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,5 +1,7 @@
1
1
  """Fabricatio is a Python library for building llm app using event-based agent structure."""
2
2
 
3
+ from importlib.util import find_spec
4
+
3
5
  from fabricatio._rust_instances import template_manager
4
6
  from fabricatio.core import env
5
7
  from fabricatio.fs import magika
@@ -35,3 +37,9 @@ __all__ = [
35
37
  "task_toolbox",
36
38
  "template_manager",
37
39
  ]
40
+
41
+
42
+ if find_spec("pymilvus"):
43
+ from fabricatio.capabilities.rag import Rag
44
+
45
+ __all__ += ["Rag"]
@@ -1,11 +1,14 @@
1
1
  """A module for the RAG (Retrieval Augmented Generation) model."""
2
2
 
3
+ from functools import lru_cache
3
4
  from operator import itemgetter
4
5
  from os import PathLike
5
6
  from pathlib import Path
6
- from typing import Any, Callable, Dict, List, Optional, Self, Union
7
+ from typing import Any, Callable, Dict, List, Optional, Self, Union, Unpack
7
8
 
9
+ from fabricatio import template_manager
8
10
  from fabricatio.config import configs
11
+ from fabricatio.models.kwargs_types import LLMKwargs
9
12
  from fabricatio.models.usages import LLMUsage
10
13
  from fabricatio.models.utils import MilvusData
11
14
  from more_itertools.recipes import flatten
@@ -14,67 +17,77 @@ try:
14
17
  from pymilvus import MilvusClient
15
18
  except ImportError as e:
16
19
  raise RuntimeError("pymilvus is not installed. Have you installed `fabricatio[rag]` instead of `fabricatio`") from e
17
- from pydantic import PrivateAttr
20
+ from pydantic import Field, PrivateAttr
21
+
22
+
23
+ @lru_cache(maxsize=None)
24
+ def create_client(
25
+ uri: Optional[str] = None, token: Optional[str] = None, timeout: Optional[float] = None
26
+ ) -> MilvusClient:
27
+ """Create a Milvus client."""
28
+ return MilvusClient(
29
+ uri=uri or configs.rag.milvus_uri.unicode_string(),
30
+ token=token or configs.rag.milvus_token.get_secret_value() if configs.rag.milvus_token else "",
31
+ timeout=timeout or configs.rag.milvus_timeout,
32
+ )
18
33
 
19
34
 
20
35
  class Rag(LLMUsage):
21
36
  """A class representing the RAG (Retrieval Augmented Generation) model."""
22
37
 
23
- _client: MilvusClient = PrivateAttr(
24
- default=MilvusClient(
25
- uri=configs.rag.milvus_uri.unicode_string(),
26
- token=configs.rag.milvus_token.get_secret_value(),
27
- timeout=configs.rag.milvus_timeout,
28
- ),
29
- )
30
- _target_collection: Optional[str] = PrivateAttr(default=None)
38
+ milvus_uri: Optional[str] = Field(default=None, frozen=True)
39
+ """The URI of the Milvus server."""
40
+ milvus_token: Optional[str] = Field(default=None, frozen=True)
41
+ """The token for the Milvus server."""
42
+ milvus_timeout: Optional[float] = Field(default=None, frozen=True)
43
+ """The timeout for the Milvus server."""
44
+ target_collection: Optional[str] = Field(default=None)
45
+ """The name of the collection being viewed."""
46
+
47
+ _client: MilvusClient = PrivateAttr(None)
48
+ """The Milvus client used for the RAG model."""
31
49
 
32
50
  @property
33
51
  def client(self) -> MilvusClient:
34
- """The Milvus client."""
52
+ """Return the Milvus client."""
35
53
  return self._client
36
54
 
37
- def view(self, collection_name: str, create: bool = False) -> Self:
55
+ def model_post_init(self, __context: Any) -> None:
56
+ """Initialize the RAG model by creating the collection if it does not exist."""
57
+ self._client = create_client(self.milvus_uri, self.milvus_token, self.milvus_timeout)
58
+ self.view(self.target_collection, create=True)
59
+
60
+ def view(self, collection_name: Optional[str], create: bool = False) -> Self:
38
61
  """View the specified collection.
39
62
 
40
63
  Args:
41
64
  collection_name (str): The name of the collection.
42
65
  create (bool): Whether to create the collection if it does not exist.
43
66
  """
44
- if create and self._client.has_collection(collection_name):
67
+ if create and collection_name and not self._client.has_collection(collection_name):
45
68
  self._client.create_collection(collection_name)
46
69
 
47
- self._target_collection = collection_name
70
+ self.target_collection = collection_name
48
71
  return self
49
72
 
50
- def quit_view(self) -> Self:
73
+ def quit_viewing(self) -> Self:
51
74
  """Quit the current view.
52
75
 
53
76
  Returns:
54
77
  Self: The current instance, allowing for method chaining.
55
78
  """
56
- self._target_collection = None
57
- return self
58
-
59
- @property
60
- def viewing_collection(self) -> Optional[str]:
61
- """Get the name of the collection being viewed.
62
-
63
- Returns:
64
- Optional[str]: The name of the collection being viewed.
65
- """
66
- return self._target_collection
79
+ return self.view(None)
67
80
 
68
81
  @property
69
- def safe_viewing_collection(self) -> str:
82
+ def safe_target_collection(self) -> str:
70
83
  """Get the name of the collection being viewed, raise an error if not viewing any collection.
71
84
 
72
85
  Returns:
73
86
  str: The name of the collection being viewed.
74
87
  """
75
- if self._target_collection is None:
88
+ if self.target_collection is None:
76
89
  raise RuntimeError("No collection is being viewed. Have you called `self.view()`?")
77
- return self._target_collection
90
+ return self.target_collection
78
91
 
79
92
  def add_document[D: Union[Dict[str, Any], MilvusData]](
80
93
  self, data: D | List[D], collection_name: Optional[str] = None
@@ -92,7 +105,7 @@ class Rag(LLMUsage):
92
105
  data = data.prepare_insertion()
93
106
  if isinstance(data, list):
94
107
  data = [d.prepare_insertion() if isinstance(d, MilvusData) else d for d in data]
95
- self._client.insert(collection_name or self.safe_viewing_collection, data)
108
+ self._client.insert(collection_name or self.safe_target_collection, data)
96
109
  return self
97
110
 
98
111
  def consume(
@@ -109,7 +122,7 @@ class Rag(LLMUsage):
109
122
  Self: The current instance, allowing for method chaining.
110
123
  """
111
124
  data = reader(Path(source))
112
- self.add_document(data, collection_name or self.safe_viewing_collection)
125
+ self.add_document(data, collection_name or self.safe_target_collection)
113
126
  return self
114
127
 
115
128
  async def afetch_document(
@@ -132,7 +145,7 @@ class Rag(LLMUsage):
132
145
  """
133
146
  # Step 1: Search for vectors
134
147
  search_results = self._client.search(
135
- collection_name or self.safe_viewing_collection,
148
+ collection_name or self.safe_target_collection,
136
149
  vecs,
137
150
  output_fields=desired_fields if isinstance(desired_fields, list) else [desired_fields],
138
151
  limit=result_per_query,
@@ -177,3 +190,36 @@ class Rag(LLMUsage):
177
190
  collection_name=collection_name,
178
191
  result_per_query=result_per_query,
179
192
  )[:final_limit]
193
+
194
+ async def aask_retrieved(
195
+ self,
196
+ question: str | List[str],
197
+ query: List[str] | str,
198
+ collection_name: Optional[str] = None,
199
+ result_per_query: int = 10,
200
+ final_limit: int = 20,
201
+ **kwargs: Unpack[LLMKwargs],
202
+ ) -> str:
203
+ """Asks a question by retrieving relevant documents based on the provided query.
204
+
205
+ This method performs document retrieval using the given query, then asks the
206
+ specified question using the retrieved documents as context.
207
+
208
+ Args:
209
+ question (str | List[str]): The question or list of questions to be asked.
210
+ query (List[str] | str): The query or list of queries used for document retrieval.
211
+ collection_name (Optional[str]): The name of the collection to retrieve documents from.
212
+ If not provided, the currently viewed collection is used.
213
+ result_per_query (int): The number of results to return per query. Default is 10.
214
+ final_limit (int): The maximum number of retrieved documents to consider. Default is 20.
215
+ **kwargs (Unpack[LLMKwargs]): Additional keyword arguments passed to the underlying `aask` method.
216
+
217
+ Returns:
218
+ str: A string response generated after asking with the context of retrieved documents.
219
+ """
220
+ docs = await self.aretrieve(query, collection_name, result_per_query, final_limit)
221
+ return await self.aask(
222
+ question,
223
+ template_manager.render_template(configs.templates.retrieved_display_template, {"docs": docs}),
224
+ **kwargs,
225
+ )
fabricatio/config.py CHANGED
@@ -176,6 +176,9 @@ class TemplateConfig(BaseModel):
176
176
  draft_rating_weights_klee_template: str = Field(default="draft_rating_weights_klee")
177
177
  """The name of the draft rating weights klee template which will be used to draft rating weights with Klee method."""
178
178
 
179
+ retrieved_display_template: str = Field(default="retrieved_display")
180
+ """The name of the retrieved display template which will be used to display retrieved documents."""
181
+
179
182
 
180
183
  class MagikaConfig(BaseModel):
181
184
  """Magika configuration class."""
fabricatio/models/task.py CHANGED
@@ -46,19 +46,19 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
46
46
  """
47
47
 
48
48
  name: str = Field(...)
49
- """The name of the task, which should be a concise and descriptive name."""
49
+ """The name of the task, which should be concise and descriptive."""
50
50
 
51
51
  description: str = Field(default="")
52
- """The description of the task, which should provide every details and noting about the task if provided, obeying the CEFR level rule and 5W1H rule."""
52
+ """A detailed explanation of the task that includes all necessary information. Should be clear and answer what, why, when, where, who, and how questions."""
53
53
 
54
- goal: List[str] = Field(default=[])
55
- """The goal of the task, a list of strings. The goal should be a concise and clear statement of what the task is intended to achieve, goal SHALL NOT be too broad or too narrow."""
54
+ goals: List[str] = Field(default=[])
55
+ """A list of objectives that the task aims to accomplish. Each goal should be clear and specific. Complex tasks should be broken into multiple smaller goals."""
56
56
 
57
57
  namespace: List[str] = Field(default_factory=list)
58
- """The namespace of the task, a list of namespace segment, as string, if it is not directly given out, it SHALL just be a empty list meaning `NOT ASSIGNED`"""
58
+ """A list of string segments that identify the task's location in the system. If not specified, defaults to an empty list."""
59
59
 
60
60
  dependencies: List[str] = Field(default_factory=list)
61
- """A list of file paths, These file are needed to read or write to meet a specific requirement of this task, if it is not directly given out, it SHALL just be a empty list meaning `NOT ASSIGNED`"""
61
+ """A list of file paths that are needed (either reading or writing) to complete this task. If not specified, defaults to an empty list."""
62
62
 
63
63
  _output: Queue[T | None] = PrivateAttr(default_factory=Queue)
64
64
  """The output queue of the task."""
@@ -113,7 +113,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
113
113
  Returns:
114
114
  Task: A new instance of the `Task` class.
115
115
  """
116
- return cls(name=name, goal=goal, description=description)
116
+ return cls(name=name, goals=goal, description=description)
117
117
 
118
118
  def update_task(self, goal: Optional[List[str] | str] = None, description: Optional[str] = None) -> Self:
119
119
  """Update the goal and description of the task.
@@ -126,7 +126,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
126
126
  Task: The updated instance of the `Task` class.
127
127
  """
128
128
  if goal:
129
- self.goal = goal if isinstance(goal, list) else [goal]
129
+ self.goals = goal if isinstance(goal, list) else [goal]
130
130
  if description:
131
131
  self.description = description
132
132
  return self
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.3.dev2
3
+ Version: 0.2.3.dev3
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -98,32 +98,32 @@ from fabricatio import Action, Role, Task, logger
98
98
 
99
99
 
100
100
  class Hello(Action):
101
- """Action that says hello."""
101
+ """Action that says hello."""
102
102
 
103
- name: str = "hello"
104
- output_key: str = "task_output"
103
+ name: str = "hello"
104
+ output_key: str = "task_output"
105
105
 
106
- async def _execute(self, task_input: Task[str], **_) -> Any:
107
- ret = "Hello fabricatio!"
108
- logger.info("executing talk action")
109
- return ret
106
+ async def _execute(self, task_input: Task[str], **_) -> Any:
107
+ ret = "Hello fabricatio!"
108
+ logger.info("executing talk action")
109
+ return ret
110
110
 
111
111
 
112
112
  async def main() -> None:
113
- """Main function."""
114
- role = Role(
115
- name="talker",
116
- description="talker role",
117
- registry={Task.pending_label: WorkFlow(name="talk", steps=(Hello,))}
118
- )
113
+ """Main function."""
114
+ role = Role(
115
+ name="talker",
116
+ description="talker role",
117
+ registry={Task.pending_label: WorkFlow(name="talk", steps=(Hello,))}
118
+ )
119
119
 
120
- task = Task(name="say hello", goal="say hello", description="say hello to the world")
121
- result = await task.delegate()
122
- logger.success(f"Result: {result}")
120
+ task = Task(name="say hello", goals="say hello", description="say hello to the world")
121
+ result = await task.delegate()
122
+ logger.success(f"Result: {result}")
123
123
 
124
124
 
125
125
  if __name__ == "__main__":
126
- asyncio.run(main())
126
+ asyncio.run(main())
127
127
  ```
128
128
 
129
129
  #### Writing and Dumping Code
@@ -311,17 +311,18 @@ from fabricatio.models.task import Task
311
311
 
312
312
  toolbox_usage = ToolBoxUsage()
313
313
 
314
+
314
315
  async def handle_security_vulnerabilities():
315
- task = Task(
316
- name="Security Check",
317
- goal=["Identify security vulnerabilities"],
318
- description="Perform a thorough security review on the project.",
319
- dependencies=["./src/main.py"]
320
- )
321
-
322
- vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)
323
- for vulnerability in vulnerabilities:
324
- print(f"Found vulnerability: {vulnerability.name}")
316
+ task = Task(
317
+ name="Security Check",
318
+ goals=["Identify security vulnerabilities"],
319
+ description="Perform a thorough security review on the project.",
320
+ dependencies=["./src/main.py"]
321
+ )
322
+
323
+ vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)
324
+ for vulnerability in vulnerabilities:
325
+ print(f"Found vulnerability: {vulnerability.name}")
325
326
  ```
326
327
 
327
328
  #### Managing CTF Challenges
@@ -334,19 +335,22 @@ from fabricatio.models.task import Task
334
335
 
335
336
  toolbox_usage = ToolBoxUsage()
336
337
 
338
+
337
339
  async def solve_ctf_challenge(challenge_name: str, challenge_description: str, files: list[str]):
338
- task = Task(
339
- name=challenge_name,
340
- goal=[f"Solve {challenge_name} challenge"],
341
- description=challenge_description,
342
- dependencies=files
343
- )
344
-
345
- solution = await toolbox_usage.gather_tools_fine_grind(task)
346
- print(f"Challenge Solved: {solution}")
340
+ task = Task(
341
+ name=challenge_name,
342
+ goals=[f"Solve {challenge_name} challenge"],
343
+ description=challenge_description,
344
+ dependencies=files
345
+ )
346
+
347
+ solution = await toolbox_usage.gather_tools_fine_grind(task)
348
+ print(f"Challenge Solved: {solution}")
349
+
347
350
 
348
351
  if __name__ == "__main__":
349
- asyncio.run(solve_ctf_challenge("Binary Exploitation", "CTF Binary Exploitation Challenge", ["./challenges/binary_exploit"]))
352
+ asyncio.run(
353
+ solve_ctf_challenge("Binary Exploitation", "CTF Binary Exploitation Challenge", ["./challenges/binary_exploit"]))
350
354
  ```
351
355
 
352
356
  ### Configuration
@@ -1,6 +1,6 @@
1
- fabricatio-0.2.3.dev2.dist-info/METADATA,sha256=UqSilptW1f3IhEtmlBleuXpaE1oN5H5Pl3ZPx-fQhi4,11960
2
- fabricatio-0.2.3.dev2.dist-info/WHEEL,sha256=RIvmwLDYujv60MYBx2jxyP4vdn1DD7X0kBgz1TQvZuc,108
3
- fabricatio-0.2.3.dev2.dist-info/licenses/LICENSE,sha256=yDZaTLnOi03bi3Dk6f5IjhLUc5old2yOsihHWU0z-i0,1067
1
+ fabricatio-0.2.3.dev3.dist-info/METADATA,sha256=-3639SKLzCTOvpu4nz4VKm00nP4zt-Ot3pz4_e6PG7o,11913
2
+ fabricatio-0.2.3.dev3.dist-info/WHEEL,sha256=RIvmwLDYujv60MYBx2jxyP4vdn1DD7X0kBgz1TQvZuc,108
3
+ fabricatio-0.2.3.dev3.dist-info/licenses/LICENSE,sha256=yDZaTLnOi03bi3Dk6f5IjhLUc5old2yOsihHWU0z-i0,1067
4
4
  fabricatio/decorators.py,sha256=cJHsxxbnMhc4SzPl4454CPLuDP3H0qbTrzV_U2rLPrs,6372
5
5
  fabricatio/core.py,sha256=MaEKZ6DDmbdScAY-7F1gwGA6fr7ADX6Mz5rNVi2msFA,6277
6
6
  fabricatio/models/generic.py,sha256=4j6DRNkHLNhx8U6aijf0Cz9HSahtEPQBh22i6d_PGTs,4981
@@ -10,7 +10,7 @@ fabricatio/models/kwargs_types.py,sha256=litzaTVzzLjOpddAVdLfsuys_rFoqTyv0etCh9y
10
10
  fabricatio/models/utils.py,sha256=ZmVHsfMbIWLGQoZSJLYInDZkQvmpmS5RGBTkJXveNHc,3825
11
11
  fabricatio/models/usages.py,sha256=0pyQBahL2lp_Bb_nSyxrW44cm3Me1Btxk3c7YVK32WI,27554
12
12
  fabricatio/models/events.py,sha256=TkMSJYSzRgurbfY2knWIc8gYw7rmaWqB7KeYzy5jeWU,2637
13
- fabricatio/models/task.py,sha256=tPeX9i-dPpxQfrkX7xR2ESFlIXUTg2z-TqOVx5_qUCU,11198
13
+ fabricatio/models/task.py,sha256=U63ULoPkOXx9UyqYqdGmALBGxn9lAidXbSxIZZXlbVM,11088
14
14
  fabricatio/models/action.py,sha256=006Jjfr9p-EBErboAn29yvyolIGi5mbJ_DA5og0Uh64,5780
15
15
  fabricatio/toolboxes/fs.py,sha256=oZjGOmtN8ZbkMXxCMatqvdPavVXyQ87AEDc1lx9jimY,483
16
16
  fabricatio/toolboxes/__init__.py,sha256=iEBsH0aRPLccIe6eyZ6iH5GdK8yYNQTnCwLzw4pWXAA,465
@@ -20,18 +20,18 @@ fabricatio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  fabricatio/fs/readers.py,sha256=lqMrM5Nt3l6QJmPDoBno2PSaN2BbFwfUjBqaghrbK3A,1002
21
21
  fabricatio/fs/curd.py,sha256=g74Pc2WPSkPlCXX0cWj2jyiDYTfkLwpwM480eyCkmc8,3851
22
22
  fabricatio/fs/__init__.py,sha256=bYE9r8uR0dtknzbg_YaGv_6Wwa27ntkQt0Tl7Kb3HFI,117
23
- fabricatio/config.py,sha256=7lmhzEkKi8eSiVBaHjjV0cEyGCanAUmFXYOrOGn7Y7c,11742
23
+ fabricatio/config.py,sha256=X4oDFVKPycSGuFYrn9cVURalU3fmUSRXBIF7bfpONZo,11920
24
24
  fabricatio/journal.py,sha256=bzxZay48ZWI0VIkkDXm4Wc_Cc9lBQYa2VGx3Hxy_PtA,753
25
- fabricatio/__init__.py,sha256=h0FgwSAdI2yhxYiuQi46uFvzqkK5LfJLt5XsHZ9rWIo,1063
25
+ fabricatio/__init__.py,sha256=DNPOP3s4_5KswbEyFojSEjMp3JuckcsGz8OVGfRaUhY,1201
26
26
  fabricatio/actions/communication.py,sha256=MrL_revv1zm1VXqjDMKZkg4PHmyMxYThAmmw2wF791k,414
27
27
  fabricatio/actions/transmission.py,sha256=WM-kssiqzTLZuZSiqbRvBaC2qynd9kjrKXya4f9Zcb0,839
28
28
  fabricatio/actions/__init__.py,sha256=eLa_5ACZ-FqdrLtOfCHk5nQBxzhIs1kgMIXWmkm2P8Y,110
29
29
  fabricatio/_rust_instances.py,sha256=JAtO-vL8ihvduf1SHLNf0w7ZSVGCJeIv6zZ9Ekyy1hY,271
30
30
  fabricatio/parser.py,sha256=Q4laV79Svggl09UKa3lAw6NYPuDz1sP2F-6U4tsSvQw,3429
31
- fabricatio/capabilities/rag.py,sha256=qjcX8h_gyp6EKYtBOWSYmSspLCwWYhO69CgVblIxzHA,6775
31
+ fabricatio/capabilities/rag.py,sha256=prL7RL3RtB64lXj0q8TUyksZ39MrEXnWXDgaACQryRQ,9259
32
32
  fabricatio/capabilities/rating.py,sha256=KLJlaufG74lHJ5hHI_tjhQgjWjHhO_38Q1ILW2cY9fA,14552
33
33
  fabricatio/capabilities/task.py,sha256=UnkeON3VCADXUBtUmQbMwl9TEwK0yQSED3EglqMMHQ4,5196
34
34
  fabricatio/_rust.pyi,sha256=clhcURuiB9zlFo4m3VyoWQ8Xs4tvg6KNHXpF-ok9h4o,1703
35
- fabricatio/_rust.cpython-312-x86_64-linux-gnu.so,sha256=XkIJsEkHwNsKoAdgOehV1Nq533nVbeD0AqlnWbhlkag,1333640
36
- fabricatio-0.2.3.dev2.data/scripts/tdown,sha256=58oe0xuGg_QYvCA5BWXESzFqlUb6FKrHsloRHP8pCUY,4577624
37
- fabricatio-0.2.3.dev2.dist-info/RECORD,,
35
+ fabricatio/_rust.cpython-312-x86_64-linux-gnu.so,sha256=8sQa5DyKEnGWd4_JacuJksUI8m0pjyGKoF66BzOXmNA,1336728
36
+ fabricatio-0.2.3.dev3.data/scripts/tdown,sha256=KnHYVZuHJisg6DR_zhOUGujgx25BZ9Jsw3HykXNrOlM,4578648
37
+ fabricatio-0.2.3.dev3.dist-info/RECORD,,