PraisonAI 0.0.6__tar.gz → 0.0.7__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: praisonAI
3
- Version: 0.0.6
3
+ Version: 0.0.7
4
4
  Summary: praisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration.
5
5
  Author: Mervin Praison
6
6
  Requires-Python: >=3.10,<3.13
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Provides-Extra: tools
12
12
  Requires-Dist: crewai (>=0.22.5)
13
+ Requires-Dist: gradio (>=4.20.0)
13
14
  Requires-Dist: pyautogen (>=0.2.19)
14
15
  Requires-Dist: rich (>=13.7)
15
16
  Project-URL: Homepage, https://github.com/mervinpraison/praisonAI
@@ -31,3 +32,9 @@ pip install praisonai
31
32
  ```bash
32
33
  praisonai
33
34
  ```
35
+
36
+ ## Test
37
+
38
+ ```bash
39
+ python -m unittest tests.test
40
+ ```
@@ -12,4 +12,10 @@ pip install praisonai
12
12
 
13
13
  ```bash
14
14
  praisonai
15
+ ```
16
+
17
+ ## Test
18
+
19
+ ```bash
20
+ python -m unittest tests.test
15
21
  ```
@@ -0,0 +1,2 @@
1
+ from .cli import PraisonAI
2
+ from .version import __version__
@@ -0,0 +1,7 @@
1
+ # __main__.py
2
+
3
+ from .cli import PraisonAI
4
+
5
+ if __name__ == "__main__":
6
+ praison_ai = PraisonAI()
7
+ praison_ai.main()
@@ -0,0 +1,151 @@
1
+ from openai import OpenAI
2
+ from pydantic import BaseModel, Field
3
+ from typing import Dict
4
+ import instructor
5
+ import os
6
+ import json
7
+
8
+ config_list = [
9
+ {
10
+ 'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-4-turbo-preview"),
11
+ 'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
12
+ }
13
+ ]
14
+
15
+ task = "Movie Story writing"
16
+
17
+ # Define new Pydantic models to represent the script writing team structure
18
+ class TaskDetails(BaseModel):
19
+ description: str
20
+ expected_output: str
21
+
22
+ class RoleDetails(BaseModel):
23
+ role: str
24
+ goal: str
25
+ backstory: str
26
+ tasks: Dict[str, TaskDetails]
27
+
28
+ class TeamStructure(BaseModel):
29
+ roles: Dict[str, RoleDetails]
30
+
31
+ # Enables `response_model` in create call with the new model
32
+ client = instructor.patch(
33
+ OpenAI(
34
+ base_url=config_list[0]['base_url'],
35
+ api_key=os.getenv("OPENAI_API_KEY"),
36
+ ),
37
+ mode=instructor.Mode.JSON,
38
+ )
39
+
40
+ response = client.chat.completions.create(
41
+ model=config_list[0]['model'],
42
+ response_model=TeamStructure,
43
+ max_retries=10,
44
+ messages=[
45
+ {"role": "system", "content": "You are a helpful assistant designed to output complex team structures."},
46
+ {"role": "user", "content":
47
+ """Generate a team structure for """ + task + """ task.
48
+ No Input data will be provided to the team.
49
+ The team will work in sequence. First role will pass the output to the next role, and so on.
50
+ The last role will generate the final output.
51
+ Think step by step.
52
+ With maximum 3 roles, each with 1 task. Include role goals, backstories, task descriptions, and expected outputs.
53
+ Output Structure:
54
+ {
55
+ "roles": {
56
+ "<role_id>": {
57
+ "role": "<role_name>",
58
+ "goal": "<role_goal>",
59
+ "backstory": "<role_backstory>",
60
+ "tasks": {
61
+ "<task_id>": {
62
+ "description": "<task_description>",
63
+ "expected_output": "<task_expected_output>"
64
+ }
65
+ }
66
+ },
67
+ "<role_id>": {
68
+ "role": "<role_name>",
69
+ "goal": "<role_goal>",
70
+ "backstory": "<role_backstory>",
71
+ "tasks": {
72
+ "<task_id>": {
73
+ "description": "<task_description>",
74
+ "expected_output": "<task_expected_output>"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ Example Below:
81
+ {
82
+ "roles": {
83
+ "narrative_designer": {
84
+ "role": "Narrative Designer",
85
+ "goal": "Create AI storylines",
86
+ "backstory": "Skilled in narrative development for AI, with a focus on story resonance.",
87
+ "tasks": {
88
+ "story_concept_development": {
89
+ "description": "Craft a unique AI story concept with depth and engagement.",
90
+ "expected_output": "Document with narrative arcs, character bios, and settings."
91
+ }
92
+ }
93
+ },
94
+ "scriptwriter": {
95
+ "role": "Scriptwriter",
96
+ "goal": "Write scripts from AI concepts",
97
+ "backstory": "Expert in dialogue and script structure, translating concepts into scripts.",
98
+ "tasks": {
99
+ "scriptwriting_task": {
100
+ "description": "Turn narrative concepts into scripts, including dialogue and scenes.",
101
+ "expected_output": "Production-ready script with dialogue and scene details."
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ """}
108
+ ]
109
+ )
110
+
111
+ print(response.model_dump_json(indent=2))
112
+
113
+ import yaml
114
+
115
+ def convert_and_save(json_data, topic="Artificial Intelligence", filename="test.yaml"):
116
+ """Converts the provided JSON data into the desired YAML format and saves it to a file.
117
+
118
+ Args:
119
+ json_data (dict): The JSON data representing the team structure.
120
+ topic (str, optional): The topic to be inserted into the YAML. Defaults to "Artificial Intelligence".
121
+ filename (str, optional): The name of the YAML file to save. Defaults to "test.yaml".
122
+ """
123
+
124
+ yaml_data = {
125
+ "framework": "crewai",
126
+ "topic": topic,
127
+ "roles": {},
128
+ "dependencies": []
129
+ }
130
+
131
+ # Process roles in the order they appear in JSON
132
+ for role_id, role_details in json_data['roles'].items():
133
+ yaml_data['roles'][role_id] = {
134
+ "backstory": "" + role_details['backstory'],
135
+ "goal": role_details['goal'],
136
+ "role": role_details['role'],
137
+ "tasks": {}
138
+ }
139
+
140
+ for task_id, task_details in role_details['tasks'].items():
141
+ yaml_data['roles'][role_id]['tasks'][task_id] = {
142
+ "description": "" + task_details['description'],
143
+ "expected_output": "" + task_details['expected_output']
144
+ }
145
+
146
+ # Save to YAML file, maintaining the order
147
+ with open(filename, 'w') as f:
148
+ yaml.dump(yaml_data, f, allow_unicode=True, sort_keys=False)
149
+
150
+ json_data = json.loads(response.model_dump_json())
151
+ convert_and_save(json_data)
@@ -0,0 +1,135 @@
1
+ from openai import OpenAI
2
+ from pydantic import BaseModel
3
+ from typing import Dict
4
+ import instructor
5
+ import os
6
+ import json
7
+ import yaml
8
+ from rich import print
9
+
10
+ # Define Pydantic models outside of the generate method
11
+ class TaskDetails(BaseModel):
12
+ description: str
13
+ expected_output: str
14
+
15
+ class RoleDetails(BaseModel):
16
+ role: str
17
+ goal: str
18
+ backstory: str
19
+ tasks: Dict[str, TaskDetails]
20
+
21
+ class TeamStructure(BaseModel):
22
+ roles: Dict[str, RoleDetails]
23
+
24
+ class AutoGenerator:
25
+ def __init__(self, topic="Movie Story writing about AI", filename="test.yaml", framework="crewai"):
26
+ self.config_list = [
27
+ {
28
+ 'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-4-turbo-preview"),
29
+ 'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
30
+ }
31
+ ]
32
+ self.topic = topic
33
+ self.filename = filename
34
+ self.framework = framework
35
+ self.client = instructor.patch(
36
+ OpenAI(
37
+ base_url=self.config_list[0]['base_url'],
38
+ api_key=os.getenv("OPENAI_API_KEY"),
39
+ ),
40
+ mode=instructor.Mode.JSON,
41
+ )
42
+
43
+ def generate(self):
44
+ response = self.client.chat.completions.create(
45
+ model=self.config_list[0]['model'],
46
+ response_model=TeamStructure,
47
+ max_retries=10,
48
+ messages=[
49
+ {"role": "system", "content": "You are a helpful assistant designed to output complex team structures."},
50
+ {"role": "user", "content": self.get_user_content()}
51
+ ]
52
+ )
53
+ json_data = json.loads(response.model_dump_json())
54
+ self.convert_and_save(json_data)
55
+ full_path = os.path.abspath(self.filename)
56
+ return full_path
57
+
58
+ def convert_and_save(self, json_data):
59
+ """Converts the provided JSON data into the desired YAML format and saves it to a file.
60
+
61
+ Args:
62
+ json_data (dict): The JSON data representing the team structure.
63
+ topic (str, optional): The topic to be inserted into the YAML. Defaults to "Artificial Intelligence".
64
+ filename (str, optional): The name of the YAML file to save. Defaults to "test.yaml".
65
+ """
66
+
67
+ yaml_data = {
68
+ "framework": "crewai",
69
+ "topic": self.topic,
70
+ "roles": {},
71
+ "dependencies": []
72
+ }
73
+ print(self.topic)
74
+ print(self.framework)
75
+
76
+ for role_id, role_details in json_data['roles'].items():
77
+ yaml_data['roles'][role_id] = {
78
+ "backstory": "" + role_details['backstory'],
79
+ "goal": role_details['goal'],
80
+ "role": role_details['role'],
81
+ "tasks": {}
82
+ }
83
+
84
+ for task_id, task_details in role_details['tasks'].items():
85
+ yaml_data['roles'][role_id]['tasks'][task_id] = {
86
+ "description": "" + task_details['description'],
87
+ "expected_output": "" + task_details['expected_output']
88
+ }
89
+
90
+ # Save to YAML file, maintaining the order
91
+ with open(self.filename, 'w') as f:
92
+ yaml.dump(yaml_data, f, allow_unicode=True, sort_keys=False)
93
+
94
+ def get_user_content(self):
95
+ user_content = """Generate a team structure for \"""" + self.topic + """\" task.
96
+ No Input data will be provided to the team.
97
+ The team will work in sequence. First role will pass the output to the next role, and so on.
98
+ The last role will generate the final output.
99
+ Think step by step.
100
+ With maximum 3 roles, each with 1 task. Include role goals, backstories, task descriptions, and expected outputs.
101
+ Example Below:
102
+ Use below example to understand the structure of the output.
103
+ The final role you create should satisfy the provided task: """ + self.topic + """.
104
+ {
105
+ "roles": {
106
+ "narrative_designer": {
107
+ "role": "Narrative Designer",
108
+ "goal": "Create AI storylines",
109
+ "backstory": "Skilled in narrative development for AI, with a focus on story resonance.",
110
+ "tasks": {
111
+ "story_concept_development": {
112
+ "description": "Craft a unique AI story concept with depth and engagement.",
113
+ "expected_output": "Document with narrative arcs, character bios, and settings."
114
+ }
115
+ }
116
+ },
117
+ "scriptwriter": {
118
+ "role": "Scriptwriter",
119
+ "goal": "Write scripts from AI concepts",
120
+ "backstory": "Expert in dialogue and script structure, translating concepts into scripts.",
121
+ "tasks": {
122
+ "scriptwriting_task": {
123
+ "description": "Turn narrative concepts into scripts, including dialogue and scenes.",
124
+ "expected_output": "Production-ready script with dialogue and scene details."
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ """
131
+ return user_content
132
+
133
+
134
+ generator = AutoGenerator(framework="crewai", topic="Create a snake game in python")
135
+ print(generator.generate())
@@ -1,13 +1,16 @@
1
- # cli.py
1
+ # praisonai/cli.py
2
2
 
3
3
  import sys
4
- from .version import __version__
4
+ from version import __version__
5
5
  import yaml, os
6
6
  from rich import print
7
7
  from dotenv import load_dotenv
8
8
  from crewai import Agent, Task, Crew
9
9
  load_dotenv()
10
10
  import autogen
11
+ import gradio as gr
12
+ import argparse
13
+
11
14
  config_list = [
12
15
  {
13
16
  'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-3.5-turbo"),
@@ -47,7 +50,7 @@ def generate_crew_and_kickoff(agent_file, framework=None):
47
50
  chat_task = {
48
51
  "recipient": agents[role],
49
52
  "message": description_filled,
50
- "summary_method": "last_msg", # Customize as needed
53
+ "summary_method": "last_msg",
51
54
  # Additional fields like carryover can be added based on dependencies
52
55
  }
53
56
  tasks.append(chat_task)
@@ -56,7 +59,7 @@ def generate_crew_and_kickoff(agent_file, framework=None):
56
59
  user = autogen.UserProxyAgent(
57
60
  name="User",
58
61
  human_input_mode="NEVER",
59
- is_termination_msg=lambda x: (x.get("content") or "").rstrip().endswith("TERMINATE"),
62
+ is_termination_msg=lambda x: (x.get("content") or "").rstrip().rstrip(".").lower().endswith("terminate") or "TERMINATE" in (x.get("content") or ""),
60
63
  code_execution_config={
61
64
  "work_dir": "coding",
62
65
  "use_docker": False,
@@ -72,7 +75,7 @@ def generate_crew_and_kickoff(agent_file, framework=None):
72
75
  backstory_filled = details['backstory'].format(topic=topic)
73
76
 
74
77
  # Assume tools are loaded and handled here as per your requirements
75
- agent = Agent(role=role_filled, goal=goal_filled, backstory=backstory_filled)
78
+ agent = Agent(role=role_filled, goal=goal_filled, backstory=backstory_filled, allow_delegation=False)
76
79
  agents[role] = agent
77
80
 
78
81
  for task_name, task_details in details.get('tasks', {}).items():
@@ -88,29 +91,53 @@ def generate_crew_and_kickoff(agent_file, framework=None):
88
91
  verbose=2
89
92
  )
90
93
 
91
- result = crew.kickoff()
94
+ response = crew.kickoff()
95
+ result = f"### Output ###\n{response}"
92
96
  return result
93
97
 
94
98
  def main(args=None):
95
99
  if args is None:
96
- args = sys.argv[1:] # Skip the script name
100
+ args = parse_args()
97
101
 
98
102
  invocation_cmd = "praisonai"
99
103
  version_string = f"praisonAI version {__version__}"
100
- framework = "crewai" # Default framework
101
-
102
- if "--framework" in args:
103
- framework_index = args.index("--framework")
104
- framework = args[framework_index + 1]
105
- args = args[:framework_index] + args[framework_index + 2:]
104
+ framework = args.framework # Default framework
105
+ ui = args.ui # Default UI flag
106
+ print(args)
106
107
 
107
- if args:
108
- agent_file = args[-1] # Assuming the last argument is the agent file
108
+ if args.agent_file:
109
+ agent_file = args.agent_file
109
110
  else:
110
111
  agent_file = "agents.yaml"
111
112
 
112
- result = generate_crew_and_kickoff(agent_file, framework)
113
- print(result)
114
-
113
+ if ui:
114
+ create_gradio_interface()
115
+ else:
116
+ result = generate_crew_and_kickoff(agent_file, framework)
117
+ print(result)
118
+
119
+ def parse_args():
120
+ parser = argparse.ArgumentParser(prog="praisonai", description="praisonAI command-line interface")
121
+ parser.add_argument("--framework", choices=["crewai", "autogen"], default="crewai", help="Specify the framework")
122
+ parser.add_argument("--ui", action="store_true", help="Enable UI mode")
123
+ parser.add_argument("--auto", action="store_true", help="Enable auto mode")
124
+ parser.add_argument("agent_file", nargs="?", help="Specify the agent file")
125
+
126
+ return parser.parse_args()
127
+
128
+ def create_gradio_interface():
129
+ def generate_crew_and_kickoff_interface(agent_file, framework):
130
+ result = generate_crew_and_kickoff(agent_file, framework)
131
+ return result
132
+
133
+ gr.Interface(
134
+ fn=generate_crew_and_kickoff_interface,
135
+ inputs=["textbox", "radio"],
136
+ outputs="textbox",
137
+ title="Generate Crew and Kickoff",
138
+ description="Generate a crew and kickoff the tasks",
139
+ theme="default"
140
+ ).launch()
141
+
115
142
  if __name__ == "__main__":
116
143
  main()
@@ -0,0 +1,164 @@
1
+ # praisonai/cli.py
2
+
3
+ import sys
4
+ from .version import __version__
5
+ import yaml, os
6
+ from rich import print
7
+ from dotenv import load_dotenv
8
+ from crewai import Agent, Task, Crew
9
+ load_dotenv()
10
+ import autogen
11
+ import gradio as gr
12
+ import argparse
13
+ from .auto import AutoGenerator
14
+
15
+ class PraisonAI:
16
+ def __init__(self, agent_file="agents.yaml", framework="crewai", auto=False):
17
+ self.config_list = [
18
+ {
19
+ 'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-4-turbo-preview"),
20
+ 'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
21
+ }
22
+ ]
23
+ self.agent_file = agent_file
24
+ self.framework = framework
25
+ self.auto = auto
26
+
27
+ def generate_crew_and_kickoff(self):
28
+ with open(self.agent_file, 'r') as f:
29
+ config = yaml.safe_load(f)
30
+
31
+ topic = config['topic']
32
+ framework = self.framework or config.get('framework')
33
+
34
+ agents = {}
35
+ tasks = []
36
+ if framework == "autogen":
37
+ # Load the LLM configuration dynamically
38
+ print(self.config_list)
39
+ llm_config = {"config_list": self.config_list}
40
+
41
+ for role, details in config['roles'].items():
42
+ agent_name = details['role'].format(topic=topic).replace("{topic}", topic)
43
+ agent_goal = details['goal'].format(topic=topic)
44
+ # Creating an AssistantAgent for each role dynamically
45
+ agents[role] = autogen.AssistantAgent(
46
+ name=agent_name,
47
+ llm_config=llm_config,
48
+ system_message=details['backstory'].format(topic=topic)+". Must Reply \"TERMINATE\" in the end when everything is done.",
49
+ )
50
+
51
+ # Preparing tasks for initiate_chats
52
+ for task_name, task_details in details.get('tasks', {}).items():
53
+ description_filled = task_details['description'].format(topic=topic)
54
+ expected_output_filled = task_details['expected_output'].format(topic=topic)
55
+
56
+ chat_task = {
57
+ "recipient": agents[role],
58
+ "message": description_filled,
59
+ "summary_method": "last_msg",
60
+ # Additional fields like carryover can be added based on dependencies
61
+ }
62
+ tasks.append(chat_task)
63
+
64
+ # Assuming the user proxy agent is set up as per your requirements
65
+ user = autogen.UserProxyAgent(
66
+ name="User",
67
+ human_input_mode="NEVER",
68
+ is_termination_msg=lambda x: (x.get("content") or "").rstrip().rstrip(".").lower().endswith("terminate") or "TERMINATE" in (x.get("content") or ""),
69
+ code_execution_config={
70
+ "work_dir": "coding",
71
+ "use_docker": False,
72
+ },
73
+ # additional setup for the user proxy agent
74
+ )
75
+ response = user.initiate_chats(tasks)
76
+ result = "### Output ###\n"+response[-1].summary if hasattr(response[-1], 'summary') else ""
77
+ else:
78
+ for role, details in config['roles'].items():
79
+ role_filled = details['role'].format(topic=topic)
80
+ goal_filled = details['goal'].format(topic=topic)
81
+ backstory_filled = details['backstory'].format(topic=topic)
82
+
83
+ # Assume tools are loaded and handled here as per your requirements
84
+ agent = Agent(role=role_filled, goal=goal_filled, backstory=backstory_filled, allow_delegation=False)
85
+ agents[role] = agent
86
+
87
+ for task_name, task_details in details.get('tasks', {}).items():
88
+ description_filled = task_details['description'].format(topic=topic)
89
+ expected_output_filled = task_details['expected_output'].format(topic=topic)
90
+
91
+ task = Task(description=description_filled, expected_output=expected_output_filled, agent=agent)
92
+ tasks.append(task)
93
+
94
+ crew = Crew(
95
+ agents=list(agents.values()),
96
+ tasks=tasks,
97
+ verbose=2
98
+ )
99
+
100
+ response = crew.kickoff()
101
+ result = f"### Output ###\n{response}"
102
+ return result
103
+
104
+ def main(self):
105
+ args = self.parse_args()
106
+ if args is None:
107
+ result = self.generate_crew_and_kickoff()
108
+ return result
109
+ invocation_cmd = "praisonai"
110
+ version_string = f"praisonAI version {__version__}"
111
+
112
+ # if args.framework:
113
+ # self.framework = args.framework
114
+ ui = args.ui # Default UI flag
115
+
116
+ if args.agent_file:
117
+ self.agent_file = args.agent_file
118
+ else:
119
+ full_path = os.path.abspath(self.agent_file)
120
+ self.filename = full_path
121
+
122
+ if args.auto:
123
+ self.topic = ' '.join(args.auto)
124
+ elif self.auto: # Use the auto attribute if args.auto is not provided
125
+ self.topic = self.auto
126
+
127
+ if args.auto or self.auto:
128
+ generator = AutoGenerator(topic=self.topic , framework=self.framework)
129
+ self.agent_file = generator.generate()
130
+ result = self.generate_crew_and_kickoff()
131
+ return result
132
+
133
+ if ui:
134
+ self.create_gradio_interface()
135
+ else:
136
+ result = self.generate_crew_and_kickoff()
137
+ return result
138
+
139
+ def parse_args(self):
140
+ parser = argparse.ArgumentParser(prog="praisonai", description="praisonAI command-line interface")
141
+ parser.add_argument("--framework", choices=["crewai", "autogen"], default="crewai", help="Specify the framework")
142
+ parser.add_argument("--ui", action="store_true", help="Enable UI mode")
143
+ parser.add_argument("--auto", nargs=argparse.REMAINDER, help="Enable auto mode and pass arguments for it")
144
+ parser.add_argument("agent_file", nargs="?", help="Specify the agent file")
145
+
146
+ return parser.parse_args()
147
+
148
+ def create_gradio_interface(self):
149
+ def generate_crew_and_kickoff_interface(agent_file, framework):
150
+ result = self.generate_crew_and_kickoff()
151
+ return result
152
+
153
+ gr.Interface(
154
+ fn=generate_crew_and_kickoff_interface,
155
+ inputs=["textbox"],
156
+ outputs="textbox",
157
+ title="Praison AI Studio",
158
+ description="Create Agents and perform tasks",
159
+ theme="default"
160
+ ).launch()
161
+
162
+ if __name__ == "__main__":
163
+ praison_ai = PraisonAI()
164
+ praison_ai.main()
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "praisonAI"
3
- version = "0.0.6"
3
+ version = "0.0.7"
4
4
  description = "praisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration."
5
5
  authors = ["Mervin Praison"]
6
6
  license = ""
@@ -18,6 +18,7 @@ python = ">=3.10,<3.13"
18
18
  rich = ">=13.7"
19
19
  pyautogen = ">=0.2.19"
20
20
  crewai = ">=0.22.5"
21
+ gradio = ">=4.20.0"
21
22
 
22
23
  [tool.poetry.dev-dependencies]
23
24
  pytest = "^5.2"
@@ -1 +0,0 @@
1
- from .version import __version__
@@ -1,6 +0,0 @@
1
- # __main__.py
2
-
3
- from .cli import main
4
-
5
- if __name__ == "__main__":
6
- main()
File without changes