Name | fabricatio JSON |
Version |
0.2.1
JSON |
| download |
home_page | None |
Summary | A LLM multi-agent framework. |
upload_time | 2025-02-21 11:56:24 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.12 |
license | None |
keywords |
ai
agents
multi-agent
llm
pyo3
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Fabricatio



Fabricatio is a Python library designed for building LLM (Large Language Model) applications using an event-based agent structure. It integrates Rust for performance-critical tasks, utilizes Handlebars for templating, and employs PyO3 for Python bindings.
## Features
- **Event-Based Architecture**: Utilizes an EventEmitter pattern for robust task management.
- **LLM Integration**: Supports interactions with large language models for intelligent task processing.
- **Templating Engine**: Uses Handlebars for dynamic content generation.
- **Toolboxes**: Provides predefined toolboxes for common operations like file manipulation and arithmetic.
- **Async Support**: Fully asynchronous for efficient execution.
- **Extensible**: Easy to extend with custom actions, workflows, and tools.
## Installation
### Using UV (Recommended)
To install Fabricatio using `uv` (a package manager for Python):
```bash
# Install uv if not already installed
pip install uv
# Clone the repository
git clone https://github.com/Whth/fabricatio.git
cd fabricatio
# Install the package in development mode with uv
uv --with-editable . maturin develop --uv -r
```
### Building Distribution
For production builds:
```bash
# Build distribution packages
make bdist
```
This will generate distribution files in the `dist` directory.
## Usage
### Basic Example
Below are some basic examples demonstrating how to use Fabricatio for different purposes.
#### Simple Hello World Program
```python
import asyncio
from fabricatio import Action, Role, Task, logger
class Hello(Action):
"""Action that says hello."""
name: str = "hello"
output_key: str = "task_output"
async def _execute(self, task_input: Task[str], **_) -> Any:
ret = "Hello fabricatio!"
logger.info("executing talk action")
return ret
async def main() -> None:
"""Main function."""
role = Role(
name="talker",
description="talker role",
registry={Task.pending_label: WorkFlow(name="talk", steps=(Hello,))}
)
task = Task(name="say hello", goal="say hello", description="say hello to the world")
result = await task.delegate()
logger.success(f"Result: {result}")
if __name__ == "__main__":
asyncio.run(main())
```
#### Writing and Dumping Code
```python
import asyncio
from fabricatio import Action, Event, PythonCapture, Role, Task, ToolBox, WorkFlow, fs_toolbox, logger
class WriteCode(Action):
"""Action that writes code based on a prompt."""
name: str = "write code"
output_key: str = "source_code"
async def _execute(self, task_input: Task[str], **_) -> str:
return await self.aask_validate(
task_input.briefing,
validator=PythonCapture.capture,
)
class DumpCode(Action):
"""Action that dumps code to the file system."""
name: str = "dump code"
description: str = "Dump code to file system"
toolboxes: set[ToolBox] = {fs_toolbox}
output_key: str = "task_output"
async def _execute(self, task_input: Task, source_code: str, **_) -> Any:
path = await self.handle_fin_grind(task_input, {"source_code": source_code})
return path[0] if path else None
async def main() -> None:
"""Main function."""
role = Role(
name="Coder",
description="A python coder who can write and document code",
registry={
Event.instantiate_from("coding.*").push("pending"): WorkFlow(
name="write code", steps=(WriteCode, DumpCode)
),
},
)
prompt = "write a Python CLI app which prints 'hello world' n times with detailed Google-style docstring. Write the source code to `cli.py`."
proposed_task = await role.propose(prompt)
path = await proposed_task.move_to("coding").delegate()
logger.success(f"Code Path: {path}")
if __name__ == "__main__":
asyncio.run(main())
```
#### Proposing Tasks
```python
import asyncio
from typing import Any
from fabricatio import Action, Role, Task, WorkFlow, logger
class WriteDocumentation(Action):
"""Action that generates documentation for the code in markdown format."""
name: str = "write documentation"
description: str = "Write detailed documentation for the provided code."
output_key: str = "task_output"
async def _execute(self, task_input: Task[str], **_) -> str:
return await self.aask(task_input.briefing)
async def main() -> None:
"""Main function."""
role = Role(
name="Documenter",
description="Role responsible for writing documentation.",
registry={
"doc.*": WorkFlow(name="write documentation", steps=(WriteDocumentation,))
}
)
prompt = "write a Rust clap CLI that downloads an HTML page"
proposed_task = await role.propose(prompt)
documentation = await proposed_task.move_to("doc").delegate()
logger.success(f"Documentation:\n{documentation}")
if __name__ == "__main__":
asyncio.run(main())
```
#### Complex Workflow Handling
```python
import asyncio
from fabricatio import Action, Event, Role, Task, WorkFlow, logger
class WriteCode(Action):
"""Action that writes code based on a prompt."""
name: str = "write code"
output_key: str = "source_code"
async def _execute(self, task_input: Task[str], **_) -> str:
return await self.aask_validate(
task_input.briefing,
validator=PythonCapture.capture,
)
class WriteDocumentation(Action):
"""Action that generates documentation for the code in markdown format."""
name: str = "write documentation"
description: str = "Write detailed documentation for the provided code."
output_key: str = "task_output"
async def _execute(self, task_input: Task[str], **_) -> str:
return await self.aask(task_input.briefing)
async def main() -> None:
"""Main function."""
role = Role(
name="Developer",
description="A developer who can write code and documentation.",
registry={
Event.instantiate_from("coding.*").push("pending"): WorkFlow(
name="write code", steps=(WriteCode,)
),
Event.instantiate_from("doc.*").push("pending"): WorkFlow(
name="write documentation", steps=(WriteDocumentation,)
),
}
)
# Propose a coding task
code_task_prompt = "write a Python CLI app which adds numbers from a file."
proposed_task = await role.propose(code_task_prompt)
code = await proposed_task.move_to("coding").delegate()
logger.success(f"Code:\n{code}")
# Propose a documentation task
doc_task_prompt = f"{code}\n\nwrite Readme.md file for the above code."
proposed_doc_task = await role.propose(doc_task_prompt)
documentation = await proposed_doc_task.move_to("doc").delegate()
logger.success(f"Documentation:\n{documentation}")
if __name__ == "__main__":
asyncio.run(main())
```
### Advanced Examples
#### Template Management and Rendering
```python
from fabricatio._rust_instances import template_manager
template_name = "claude-xml.hbs"
data = {
"absolute_code_path": "/path/to/project",
"source_tree": "source tree content",
"files": [{"path": "file1.py", "code": "print('Hello')"}],
}
rendered_template = template_manager.render_template(template_name, data)
print(rendered_template)
```
#### Handling Security Vulnerabilities
```python
from fabricatio.models.usages import ToolBoxUsage
from fabricatio.models.task import Task
toolbox_usage = ToolBoxUsage()
async def handle_security_vulnerabilities():
task = Task(
name="Security Check",
goal=["Identify security vulnerabilities"],
description="Perform a thorough security review on the project.",
dependencies=["./src/main.py"]
)
vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)
for vulnerability in vulnerabilities:
print(f"Found vulnerability: {vulnerability.name}")
```
#### Managing CTF Challenges
```python
import asyncio
from fabricatio.models.usages import ToolBoxUsage
from fabricatio.models.task import Task
toolbox_usage = ToolBoxUsage()
async def solve_ctf_challenge(challenge_name: str, challenge_description: str, files: list[str]):
task = Task(
name=challenge_name,
goal=[f"Solve {challenge_name} challenge"],
description=challenge_description,
dependencies=files
)
solution = await toolbox_usage.gather_tools_fine_grind(task)
print(f"Challenge Solved: {solution}")
if __name__ == "__main__":
asyncio.run(solve_ctf_challenge("Binary Exploitation", "CTF Binary Exploitation Challenge", ["./challenges/binary_exploit"]))
```
### Configuration
The configuration for Fabricatio is managed via environment variables or TOML files. The default configuration file (`config.toml`) can be overridden by specifying a custom path.
Example `config.toml`:
```toml
[llm]
api_endpoint = "https://api.openai.com"
api_key = "your_openai_api_key"
timeout = 300
max_retries = 3
model = "gpt-3.5-turbo"
temperature = 1.0
stop_sign = ["\n\n\n", "User:"]
top_p = 0.35
generation_count = 1
stream = false
max_tokens = 8192
```
### Development Setup
To set up a development environment for Fabricatio:
1. **Clone the Repository**:
```bash
git clone https://github.com/Whth/fabricatio.git
cd fabricatio
```
2. **Install Dependencies**:
```bash
uv --with-editable . maturin develop --uv -r
```
3. **Run Tests**:
```bash
make test
```
4. **Build Documentation**:
```bash
make docs
```
### Contributing
Contributions are welcome! Please follow these guidelines when contributing:
1. Fork the repository.
2. Create your feature branch (`git checkout -b feature/new-feature`).
3. Commit your changes (`git commit -am 'Add new feature'`).
4. Push to the branch (`git push origin feature/new-feature`).
5. Create a new Pull Request.
### License
Fabricatio is licensed under the MIT License. See [LICENSE](LICENSE) for more details.
### Acknowledgments
Special thanks to the contributors and maintainers of:
- [PyO3](https://github.com/PyO3/pyo3)
- [Maturin](https://github.com/PyO3/maturin)
- [Handlebars.rs](https://github.com/sunng87/handlebars-rust)
Raw data
{
"_id": null,
"home_page": null,
"name": "fabricatio",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "ai, agents, multi-agent, llm, pyo3",
"author": null,
"author_email": "Whth <zettainspector@foxmail.com>",
"download_url": "https://files.pythonhosted.org/packages/ed/52/b24f0ca42cfe89eb839c975b9daad347bc3dcdc1a724d802db0e85643317/fabricatio-0.2.1.tar.gz",
"platform": null,
"description": "\n# Fabricatio\n\n\n\n\n\nFabricatio is a Python library designed for building LLM (Large Language Model) applications using an event-based agent structure. It integrates Rust for performance-critical tasks, utilizes Handlebars for templating, and employs PyO3 for Python bindings.\n\n## Features\n\n- **Event-Based Architecture**: Utilizes an EventEmitter pattern for robust task management.\n- **LLM Integration**: Supports interactions with large language models for intelligent task processing.\n- **Templating Engine**: Uses Handlebars for dynamic content generation.\n- **Toolboxes**: Provides predefined toolboxes for common operations like file manipulation and arithmetic.\n- **Async Support**: Fully asynchronous for efficient execution.\n- **Extensible**: Easy to extend with custom actions, workflows, and tools.\n\n## Installation\n\n### Using UV (Recommended)\n\nTo install Fabricatio using `uv` (a package manager for Python):\n\n```bash\n# Install uv if not already installed\npip install uv\n\n# Clone the repository\ngit clone https://github.com/Whth/fabricatio.git\ncd fabricatio\n\n# Install the package in development mode with uv\nuv --with-editable . maturin develop --uv -r\n```\n\n### Building Distribution\n\nFor production builds:\n\n```bash\n# Build distribution packages\nmake bdist\n```\n\nThis will generate distribution files in the `dist` directory.\n\n## Usage\n\n### Basic Example\n\nBelow are some basic examples demonstrating how to use Fabricatio for different purposes.\n\n#### Simple Hello World Program\n\n```python\nimport asyncio\nfrom fabricatio import Action, Role, Task, logger\n\n\nclass Hello(Action):\n \"\"\"Action that says hello.\"\"\"\n\n name: str = \"hello\"\n output_key: str = \"task_output\"\n\n async def _execute(self, task_input: Task[str], **_) -> Any:\n ret = \"Hello fabricatio!\"\n logger.info(\"executing talk action\")\n return ret\n\n\nasync def main() -> None:\n \"\"\"Main function.\"\"\"\n role = Role(\n name=\"talker\", \n description=\"talker role\", \n registry={Task.pending_label: WorkFlow(name=\"talk\", steps=(Hello,))}\n )\n\n task = Task(name=\"say hello\", goal=\"say hello\", description=\"say hello to the world\")\n result = await task.delegate()\n logger.success(f\"Result: {result}\")\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n#### Writing and Dumping Code\n\n```python\nimport asyncio\nfrom fabricatio import Action, Event, PythonCapture, Role, Task, ToolBox, WorkFlow, fs_toolbox, logger\n\n\nclass WriteCode(Action):\n \"\"\"Action that writes code based on a prompt.\"\"\"\n\n name: str = \"write code\"\n output_key: str = \"source_code\"\n\n async def _execute(self, task_input: Task[str], **_) -> str:\n return await self.aask_validate(\n task_input.briefing,\n validator=PythonCapture.capture,\n )\n\n\nclass DumpCode(Action):\n \"\"\"Action that dumps code to the file system.\"\"\"\n\n name: str = \"dump code\"\n description: str = \"Dump code to file system\"\n toolboxes: set[ToolBox] = {fs_toolbox}\n output_key: str = \"task_output\"\n\n async def _execute(self, task_input: Task, source_code: str, **_) -> Any:\n path = await self.handle_fin_grind(task_input, {\"source_code\": source_code})\n return path[0] if path else None\n\n\nasync def main() -> None:\n \"\"\"Main function.\"\"\"\n role = Role(\n name=\"Coder\",\n description=\"A python coder who can write and document code\",\n registry={\n Event.instantiate_from(\"coding.*\").push(\"pending\"): WorkFlow(\n name=\"write code\", steps=(WriteCode, DumpCode)\n ),\n },\n )\n\n prompt = \"write a Python CLI app which prints 'hello world' n times with detailed Google-style docstring. Write the source code to `cli.py`.\"\n\n proposed_task = await role.propose(prompt)\n path = await proposed_task.move_to(\"coding\").delegate()\n logger.success(f\"Code Path: {path}\")\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n#### Proposing Tasks\n\n```python\nimport asyncio\nfrom typing import Any\n\nfrom fabricatio import Action, Role, Task, WorkFlow, logger\n\n\nclass WriteDocumentation(Action):\n \"\"\"Action that generates documentation for the code in markdown format.\"\"\"\n\n name: str = \"write documentation\"\n description: str = \"Write detailed documentation for the provided code.\"\n output_key: str = \"task_output\"\n\n async def _execute(self, task_input: Task[str], **_) -> str:\n return await self.aask(task_input.briefing)\n\n\nasync def main() -> None:\n \"\"\"Main function.\"\"\"\n role = Role(\n name=\"Documenter\",\n description=\"Role responsible for writing documentation.\",\n registry={\n \"doc.*\": WorkFlow(name=\"write documentation\", steps=(WriteDocumentation,))\n }\n )\n\n prompt = \"write a Rust clap CLI that downloads an HTML page\"\n proposed_task = await role.propose(prompt)\n documentation = await proposed_task.move_to(\"doc\").delegate()\n logger.success(f\"Documentation:\\n{documentation}\")\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n#### Complex Workflow Handling\n\n```python\nimport asyncio\nfrom fabricatio import Action, Event, Role, Task, WorkFlow, logger\n\n\nclass WriteCode(Action):\n \"\"\"Action that writes code based on a prompt.\"\"\"\n\n name: str = \"write code\"\n output_key: str = \"source_code\"\n\n async def _execute(self, task_input: Task[str], **_) -> str:\n return await self.aask_validate(\n task_input.briefing,\n validator=PythonCapture.capture,\n )\n\n\nclass WriteDocumentation(Action):\n \"\"\"Action that generates documentation for the code in markdown format.\"\"\"\n\n name: str = \"write documentation\"\n description: str = \"Write detailed documentation for the provided code.\"\n output_key: str = \"task_output\"\n\n async def _execute(self, task_input: Task[str], **_) -> str:\n return await self.aask(task_input.briefing)\n\n\nasync def main() -> None:\n \"\"\"Main function.\"\"\"\n role = Role(\n name=\"Developer\",\n description=\"A developer who can write code and documentation.\",\n registry={\n Event.instantiate_from(\"coding.*\").push(\"pending\"): WorkFlow(\n name=\"write code\", steps=(WriteCode,)\n ),\n Event.instantiate_from(\"doc.*\").push(\"pending\"): WorkFlow(\n name=\"write documentation\", steps=(WriteDocumentation,)\n ),\n }\n )\n\n # Propose a coding task\n code_task_prompt = \"write a Python CLI app which adds numbers from a file.\"\n proposed_task = await role.propose(code_task_prompt)\n code = await proposed_task.move_to(\"coding\").delegate()\n logger.success(f\"Code:\\n{code}\")\n\n # Propose a documentation task\n doc_task_prompt = f\"{code}\\n\\nwrite Readme.md file for the above code.\"\n proposed_doc_task = await role.propose(doc_task_prompt)\n documentation = await proposed_doc_task.move_to(\"doc\").delegate()\n logger.success(f\"Documentation:\\n{documentation}\")\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\n### Advanced Examples\n\n#### Template Management and Rendering\n\n```python\nfrom fabricatio._rust_instances import template_manager\n\ntemplate_name = \"claude-xml.hbs\"\ndata = {\n \"absolute_code_path\": \"/path/to/project\",\n \"source_tree\": \"source tree content\",\n \"files\": [{\"path\": \"file1.py\", \"code\": \"print('Hello')\"}],\n}\n\nrendered_template = template_manager.render_template(template_name, data)\nprint(rendered_template)\n```\n\n#### Handling Security Vulnerabilities\n\n```python\nfrom fabricatio.models.usages import ToolBoxUsage\nfrom fabricatio.models.task import Task\n\ntoolbox_usage = ToolBoxUsage()\n\nasync def handle_security_vulnerabilities():\n task = Task(\n name=\"Security Check\",\n goal=[\"Identify security vulnerabilities\"],\n description=\"Perform a thorough security review on the project.\",\n dependencies=[\"./src/main.py\"]\n )\n \n vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)\n for vulnerability in vulnerabilities:\n print(f\"Found vulnerability: {vulnerability.name}\")\n```\n\n#### Managing CTF Challenges\n\n```python\nimport asyncio\n\nfrom fabricatio.models.usages import ToolBoxUsage\nfrom fabricatio.models.task import Task\n\ntoolbox_usage = ToolBoxUsage()\n\nasync def solve_ctf_challenge(challenge_name: str, challenge_description: str, files: list[str]):\n task = Task(\n name=challenge_name,\n goal=[f\"Solve {challenge_name} challenge\"],\n description=challenge_description,\n dependencies=files\n )\n \n solution = await toolbox_usage.gather_tools_fine_grind(task)\n print(f\"Challenge Solved: {solution}\")\n\nif __name__ == \"__main__\":\n asyncio.run(solve_ctf_challenge(\"Binary Exploitation\", \"CTF Binary Exploitation Challenge\", [\"./challenges/binary_exploit\"]))\n```\n\n### Configuration\n\nThe configuration for Fabricatio is managed via environment variables or TOML files. The default configuration file (`config.toml`) can be overridden by specifying a custom path.\n\nExample `config.toml`:\n\n```toml\n[llm]\napi_endpoint = \"https://api.openai.com\"\napi_key = \"your_openai_api_key\"\ntimeout = 300\nmax_retries = 3\nmodel = \"gpt-3.5-turbo\"\ntemperature = 1.0\nstop_sign = [\"\\n\\n\\n\", \"User:\"]\ntop_p = 0.35\ngeneration_count = 1\nstream = false\nmax_tokens = 8192\n```\n\n### Development Setup\n\nTo set up a development environment for Fabricatio:\n\n1. **Clone the Repository**:\n ```bash\n git clone https://github.com/Whth/fabricatio.git\n cd fabricatio\n ```\n\n2. **Install Dependencies**:\n ```bash\n uv --with-editable . maturin develop --uv -r\n ```\n\n3. **Run Tests**:\n ```bash\n make test\n ```\n\n4. **Build Documentation**:\n ```bash\n make docs\n ```\n\n### Contributing\n\nContributions are welcome! Please follow these guidelines when contributing:\n\n1. Fork the repository.\n2. Create your feature branch (`git checkout -b feature/new-feature`).\n3. Commit your changes (`git commit -am 'Add new feature'`).\n4. Push to the branch (`git push origin feature/new-feature`).\n5. Create a new Pull Request.\n\n### License\n\nFabricatio is licensed under the MIT License. See [LICENSE](LICENSE) for more details.\n\n### Acknowledgments\n\nSpecial thanks to the contributors and maintainers of:\n- [PyO3](https://github.com/PyO3/pyo3)\n- [Maturin](https://github.com/PyO3/maturin)\n- [Handlebars.rs](https://github.com/sunng87/handlebars-rust)\n\n\n",
"bugtrack_url": null,
"license": null,
"summary": "A LLM multi-agent framework.",
"version": "0.2.1",
"project_urls": {
"Homepage": "https://github.com/Whth/fabricatio",
"Issues": "https://github.com/Whth/fabricatio/issues",
"Repository": "https://github.com/Whth/fabricatio"
},
"split_keywords": [
"ai",
" agents",
" multi-agent",
" llm",
" pyo3"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "8531cb064271a04345e72e01a925d077563838b4723f23676f3098854edc8577",
"md5": "2339d156f4452049e0ce2cbb58c41acf",
"sha256": "a52af33a5f94599d33a38faf4bf90c1d1c8cae5949cfead305d31ecde96b8ecb"
},
"downloads": -1,
"filename": "fabricatio-0.2.1-cp312-cp312-manylinux_2_34_x86_64.whl",
"has_sig": false,
"md5_digest": "2339d156f4452049e0ce2cbb58c41acf",
"packagetype": "bdist_wheel",
"python_version": "cp312",
"requires_python": ">=3.12",
"size": 2209782,
"upload_time": "2025-02-21T11:56:22",
"upload_time_iso_8601": "2025-02-21T11:56:22.106855Z",
"url": "https://files.pythonhosted.org/packages/85/31/cb064271a04345e72e01a925d077563838b4723f23676f3098854edc8577/fabricatio-0.2.1-cp312-cp312-manylinux_2_34_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7833dd1a2e7c1bb82b7c3efb62bbc9f7d9e65014487ac6d22ad1468865cd3f75",
"md5": "dc7f364e08a169d3b2c8071751c56091",
"sha256": "037e015cc72bd422372be9b52a86e8e2a3609c8f13599c58228a28998f7a0213"
},
"downloads": -1,
"filename": "fabricatio-0.2.1-cp312-cp312-win_amd64.whl",
"has_sig": false,
"md5_digest": "dc7f364e08a169d3b2c8071751c56091",
"packagetype": "bdist_wheel",
"python_version": "cp312",
"requires_python": ">=3.12",
"size": 1813352,
"upload_time": "2025-02-21T11:58:47",
"upload_time_iso_8601": "2025-02-21T11:58:47.840039Z",
"url": "https://files.pythonhosted.org/packages/78/33/dd1a2e7c1bb82b7c3efb62bbc9f7d9e65014487ac6d22ad1468865cd3f75/fabricatio-0.2.1-cp312-cp312-win_amd64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "ed52b24f0ca42cfe89eb839c975b9daad347bc3dcdc1a724d802db0e85643317",
"md5": "fb15dd943cdb1bb23236c8a0c9620b43",
"sha256": "9522c9e275ff4106ebfbe802ca74750355416b0dd20ccc9add2476f851c156d2"
},
"downloads": -1,
"filename": "fabricatio-0.2.1.tar.gz",
"has_sig": false,
"md5_digest": "fb15dd943cdb1bb23236c8a0c9620b43",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 157499,
"upload_time": "2025-02-21T11:56:24",
"upload_time_iso_8601": "2025-02-21T11:56:24.848689Z",
"url": "https://files.pythonhosted.org/packages/ed/52/b24f0ca42cfe89eb839c975b9daad347bc3dcdc1a724d802db0e85643317/fabricatio-0.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-21 11:56:24",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Whth",
"github_project": "fabricatio",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "fabricatio"
}