PraisonAI 2.0.62__cp313-cp313-manylinux_2_39_x86_64.whl → 2.0.64__cp313-cp313-manylinux_2_39_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.

Potentially problematic release.


This version of PraisonAI might be problematic. Click here for more details.

praisonai/cli.py CHANGED
@@ -25,7 +25,7 @@ CALL_MODULE_AVAILABLE = False
25
25
  CREWAI_AVAILABLE = False
26
26
  AUTOGEN_AVAILABLE = False
27
27
  PRAISONAI_AVAILABLE = False
28
-
28
+ TRAIN_AVAILABLE = False
29
29
  try:
30
30
  # Create necessary directories and set CHAINLIT_APP_ROOT
31
31
  if "CHAINLIT_APP_ROOT" not in os.environ:
@@ -72,6 +72,12 @@ try:
72
72
  except ImportError:
73
73
  pass
74
74
 
75
+ try:
76
+ import accelerate
77
+ TRAIN_AVAILABLE = True
78
+ except ImportError:
79
+ pass
80
+
75
81
  logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO'), format='%(asctime)s - %(levelname)s - %(message)s')
76
82
  logging.getLogger('alembic').setLevel(logging.ERROR)
77
83
  logging.getLogger('gradio').setLevel(logging.ERROR)
@@ -140,6 +146,8 @@ class PraisonAI:
140
146
  provided arguments.
141
147
  """
142
148
  args = self.parse_args()
149
+ # Store args for use in handle_direct_prompt
150
+ self.args = args
143
151
  invocation_cmd = "praisonai"
144
152
  version_string = f"PraisonAI version {__version__}"
145
153
 
@@ -304,6 +312,7 @@ class PraisonAI:
304
312
  parser.add_argument("command", nargs="?", help="Command to run or direct prompt")
305
313
  parser.add_argument("--deploy", action="store_true", help="Deploy the application")
306
314
  parser.add_argument("--model", type=str, help="Model name")
315
+ parser.add_argument("--llm", type=str, help="LLM model to use for direct prompts")
307
316
  parser.add_argument("--hf", type=str, help="Hugging Face model name")
308
317
  parser.add_argument("--ollama", type=str, help="Ollama model name")
309
318
  parser.add_argument("--dataset", type=str, help="Dataset name for training", default="yahma/alpaca-cleaned")
@@ -390,9 +399,13 @@ class PraisonAI:
390
399
  sys.exit(0)
391
400
 
392
401
  elif args.command == 'train':
393
- print("[red]ERROR: Train feature is not installed. Install with:[/red]")
394
- print("\npip install \"praisonai[train]\"\n")
395
- sys.exit(1)
402
+ if not TRAIN_AVAILABLE:
403
+ print("[red]ERROR: Train feature is not installed. Install with:[/red]")
404
+ print("\npip install \"praisonai[train]\"\n")
405
+ sys.exit(1)
406
+ package_root = os.path.dirname(os.path.abspath(__file__))
407
+ config_yaml_destination = os.path.join(os.getcwd(), 'config.yaml')
408
+
396
409
 
397
410
  elif args.command == 'ui':
398
411
  if not CHAINLIT_AVAILABLE:
@@ -424,21 +437,33 @@ class PraisonAI:
424
437
  Handle direct prompt by creating a single agent and running it.
425
438
  """
426
439
  if PRAISONAI_AVAILABLE:
427
- agent = PraisonAgent(
428
- name="DirectAgent",
429
- role="Assistant",
430
- goal="Complete the given task",
431
- backstory="You are a helpful AI assistant"
432
- )
433
- agent.start(prompt)
440
+ agent_config = {
441
+ "name": "DirectAgent",
442
+ "role": "Assistant",
443
+ "goal": "Complete the given task",
444
+ "backstory": "You are a helpful AI assistant"
445
+ }
446
+
447
+ # Add llm if specified
448
+ if hasattr(self, 'args') and self.args.llm:
449
+ agent_config["llm"] = self.args.llm
450
+
451
+ agent = PraisonAgent(**agent_config)
452
+ result = agent.start(prompt)
434
453
  return ""
435
454
  elif CREWAI_AVAILABLE:
436
- agent = Agent(
437
- name="DirectAgent",
438
- role="Assistant",
439
- goal="Complete the given task",
440
- backstory="You are a helpful AI assistant"
441
- )
455
+ agent_config = {
456
+ "name": "DirectAgent",
457
+ "role": "Assistant",
458
+ "goal": "Complete the given task",
459
+ "backstory": "You are a helpful AI assistant"
460
+ }
461
+
462
+ # Add llm if specified
463
+ if hasattr(self, 'args') and self.args.llm:
464
+ agent_config["llm"] = self.args.llm
465
+
466
+ agent = Agent(**agent_config)
442
467
  task = Task(
443
468
  description=prompt,
444
469
  agent=agent
@@ -450,6 +475,10 @@ class PraisonAI:
450
475
  return crew.kickoff()
451
476
  elif AUTOGEN_AVAILABLE:
452
477
  config_list = self.config_list
478
+ # Add llm if specified
479
+ if hasattr(self, 'args') and self.args.llm:
480
+ config_list[0]['model'] = self.args.llm
481
+
453
482
  assistant = autogen.AssistantAgent(
454
483
  name="DirectAgent",
455
484
  llm_config={"config_list": config_list}
praisonai/deploy.py CHANGED
@@ -56,7 +56,7 @@ class CloudDeployer:
56
56
  file.write("FROM python:3.11-slim\n")
57
57
  file.write("WORKDIR /app\n")
58
58
  file.write("COPY . .\n")
59
- file.write("RUN pip install flask praisonai==2.0.62 gunicorn markdown\n")
59
+ file.write("RUN pip install flask praisonai==2.0.64 gunicorn markdown\n")
60
60
  file.write("EXPOSE 8080\n")
61
61
  file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
62
62
 
@@ -11,6 +11,20 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
11
11
  elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
12
12
  # Linux
13
13
  MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh"
14
+
15
+ # Check and install libcurl development package if not present
16
+ if command -v dpkg &> /dev/null; then
17
+ if ! dpkg -s libcurl4-openssl-dev &> /dev/null; then
18
+ echo "libcurl4-openssl-dev is not installed. Installing..."
19
+ sudo apt-get update
20
+ sudo apt-get install -y libcurl4-openssl-dev
21
+ else
22
+ echo "libcurl4-openssl-dev is already installed."
23
+ fi
24
+ else
25
+ echo "Non-Debian based Linux detected. Please ensure libcurl development libraries are installed."
26
+ fi
27
+
14
28
  elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
15
29
  # Windows
16
30
  MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe"
@@ -44,7 +58,6 @@ if conda info --envs | grep -q $ENV_NAME; then
44
58
  # Linux
45
59
  conda create --name $ENV_NAME python=3.10 pytorch=2.3.0 cudatoolkit=11.8 -c pytorch -c nvidia -y
46
60
  fi
47
- # conda activate $ENV_NAME
48
61
  else
49
62
  echo "Creating new environment $ENV_NAME..."
50
63
  if [[ "$OSTYPE" == "darwin"* ]]; then
@@ -54,19 +67,25 @@ else
54
67
  # Linux
55
68
  conda create --name $ENV_NAME python=3.10 pytorch=2.3.0 cudatoolkit=11.8 -c pytorch -c nvidia -y
56
69
  fi
57
- # conda activate $ENV_NAME
58
70
  fi
59
71
 
60
- # source $HOME/miniconda/bin/activate $ENV_NAME
72
+ # Activate the environment
73
+ source $HOME/miniconda/bin/activate $ENV_NAME
74
+
75
+ # Install cmake via conda
76
+ echo "Installing cmake..."
77
+ conda install -y cmake
61
78
 
62
- # Get full path of pip
79
+ # Get full path of pip within the activated environment
63
80
  PIP_FULL_PATH=$(conda run -n $ENV_NAME which pip)
64
81
 
65
- # Install other packages within the activated environment
66
- # Use PIP_FULL_PATH to run pip commands
82
+ # Install other packages within the activated environment using pip
67
83
  $PIP_FULL_PATH install --upgrade pip
68
84
  $PIP_FULL_PATH install "xformers==0.0.26.post1"
69
- $PIP_FULL_PATH install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git@4e570be9ae4ced8cdc64e498125708e34942befc"
85
+ $PIP_FULL_PATH install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git@038e6d4c8d40207a87297ab3aaf787c19b1006d1"
70
86
  $PIP_FULL_PATH install --no-deps "trl<0.9.0" peft accelerate bitsandbytes
87
+ $PIP_FULL_PATH install unsloth_zoo
88
+ $PIP_FULL_PATH install cut_cross_entropy
89
+ $PIP_FULL_PATH install sentencepiece protobuf datasets huggingface_hub hf_transfer
71
90
 
72
91
  echo "Setup completed successfully!"
praisonai/train.py CHANGED
@@ -1,168 +1,281 @@
1
- import subprocess
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ This script finetunes a model using Unsloth’s fast training framework.
5
+ It supports both ShareGPT and Alpaca‑style datasets by converting raw conversation
6
+ data into plain-text prompts using a chat template, then pre‑tokenizing the prompts.
7
+ Extra debug logging is added to help trace the root cause of errors.
8
+ """
9
+
2
10
  import os
3
11
  import sys
4
12
  import yaml
5
13
  import torch
6
14
  import shutil
15
+ import subprocess
7
16
  from transformers import TextStreamer
8
17
  from unsloth import FastLanguageModel, is_bfloat16_supported
9
18
  from trl import SFTTrainer
10
19
  from transformers import TrainingArguments
11
- from datasets import load_dataset, concatenate_datasets, Dataset
20
+ from datasets import load_dataset, concatenate_datasets
12
21
  from psutil import virtual_memory
13
-
14
- class train:
22
+ from unsloth.chat_templates import standardize_sharegpt, get_chat_template
23
+ from functools import partial
24
+
25
+ #####################################
26
+ # Step 1: Formatting Raw Conversations
27
+ #####################################
28
+ def formatting_prompts_func(examples, tokenizer):
29
+ """
30
+ Converts each example’s conversation into a single plain-text prompt.
31
+ If the example has a "conversations" field, process it as ShareGPT-style.
32
+ Otherwise, assume Alpaca-style data with "instruction", "input", and "output" fields.
33
+ """
34
+ print("DEBUG: formatting_prompts_func() received batch with keys:", list(examples.keys()))
35
+ texts = []
36
+ # Check if the example has a "conversations" field.
37
+ if "conversations" in examples:
38
+ for convo in examples["conversations"]:
39
+ try:
40
+ formatted = tokenizer.apply_chat_template(
41
+ convo,
42
+ tokenize=False, # Return a plain string
43
+ add_generation_prompt=False
44
+ )
45
+ except Exception as e:
46
+ print(f"ERROR in apply_chat_template (conversations): {e}")
47
+ formatted = ""
48
+ # Flatten list if necessary
49
+ if isinstance(formatted, list):
50
+ formatted = formatted[0] if len(formatted) == 1 else "\n".join(formatted)
51
+ texts.append(formatted)
52
+ else:
53
+ # Assume Alpaca format: use "instruction", "input", and "output" keys.
54
+ instructions = examples.get("instruction", [])
55
+ inputs_list = examples.get("input", [])
56
+ outputs_list = examples.get("output", [])
57
+ # If any field is missing, replace with empty string.
58
+ for ins, inp, out in zip(instructions, inputs_list, outputs_list):
59
+ # Create a conversation-like structure.
60
+ convo = [
61
+ {"role": "user", "content": ins + (f"\nInput: {inp}" if inp.strip() != "" else "")},
62
+ {"role": "assistant", "content": out}
63
+ ]
64
+ try:
65
+ formatted = tokenizer.apply_chat_template(
66
+ convo,
67
+ tokenize=False,
68
+ add_generation_prompt=False
69
+ )
70
+ except Exception as e:
71
+ print(f"ERROR in apply_chat_template (alpaca): {e}")
72
+ formatted = ""
73
+ if isinstance(formatted, list):
74
+ formatted = formatted[0] if len(formatted) == 1 else "\n".join(formatted)
75
+ texts.append(formatted)
76
+ if texts:
77
+ print("DEBUG: Raw texts sample (first 200 chars):", texts[0][:200])
78
+ return {"text": texts}
79
+
80
+ #####################################
81
+ # Step 2: Tokenizing the Prompts
82
+ #####################################
83
+ def tokenize_function(examples, hf_tokenizer, max_length):
84
+ """
85
+ Tokenizes a batch of text prompts with padding and truncation enabled.
86
+ """
87
+ flat_texts = []
88
+ for t in examples["text"]:
89
+ if isinstance(t, list):
90
+ t = t[0] if len(t) == 1 else " ".join(t)
91
+ flat_texts.append(t)
92
+ print("DEBUG: Tokenizing a batch of size:", len(flat_texts))
93
+ tokenized = hf_tokenizer(
94
+ flat_texts,
95
+ padding="max_length",
96
+ truncation=True,
97
+ max_length=max_length,
98
+ return_tensors="pt",
99
+ )
100
+ tokenized = {key: value.tolist() for key, value in tokenized.items()}
101
+ sample_key = list(tokenized.keys())[0]
102
+ print("DEBUG: Tokenized sample (first 10 tokens of", sample_key, "):", tokenized[sample_key][0][:10])
103
+ return tokenized
104
+
105
+ #####################################
106
+ # Main Training Class
107
+ #####################################
108
+ class TrainModel:
15
109
  def __init__(self, config_path="config.yaml"):
16
110
  self.load_config(config_path)
17
111
  self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
18
- self.model, self.tokenizer = None, None
112
+ self.model = None
113
+ self.hf_tokenizer = None # The underlying HF tokenizer
114
+ self.chat_tokenizer = None # Chat wrapper for formatting
19
115
 
20
116
  def load_config(self, path):
21
117
  with open(path, "r") as file:
22
118
  self.config = yaml.safe_load(file)
119
+ print("DEBUG: Loaded config:", self.config)
23
120
 
24
121
  def print_system_info(self):
25
- print(f"PyTorch version: {torch.__version__}")
26
- print(f"CUDA version: {torch.version.cuda}")
122
+ print("DEBUG: PyTorch version:", torch.__version__)
123
+ print("DEBUG: CUDA version:", torch.version.cuda)
27
124
  if torch.cuda.is_available():
28
- device_capability = torch.cuda.get_device_capability()
29
- print(f"CUDA Device Capability: {device_capability}")
125
+ print("DEBUG: CUDA Device Capability:", torch.cuda.get_device_capability())
30
126
  else:
31
- print("CUDA is not available")
32
-
33
- python_version = sys.version
34
- pip_version = subprocess.check_output(['pip', '--version']).decode().strip()
35
- python_path = sys.executable
36
- pip_path = subprocess.check_output(['which', 'pip']).decode().strip()
37
- print(f"Python Version: {python_version}")
38
- print(f"Pip Version: {pip_version}")
39
- print(f"Python Path: {python_path}")
40
- print(f"Pip Path: {pip_path}")
127
+ print("DEBUG: CUDA is not available")
128
+ print("DEBUG: Python Version:", sys.version)
129
+ print("DEBUG: Python Path:", sys.executable)
41
130
 
42
131
  def check_gpu(self):
43
132
  gpu_stats = torch.cuda.get_device_properties(0)
44
- print(f"GPU = {gpu_stats.name}. Max memory = {round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)} GB.")
133
+ print(f"DEBUG: GPU = {gpu_stats.name}. Max memory = {round(gpu_stats.total_memory/(1024**3),3)} GB.")
45
134
 
46
135
  def check_ram(self):
47
136
  ram_gb = virtual_memory().total / 1e9
48
- print('Your runtime has {:.1f} gigabytes of available RAM\n'.format(ram_gb))
137
+ print(f"DEBUG: Your runtime has {ram_gb:.1f} gigabytes of available RAM")
49
138
  if ram_gb < 20:
50
- print('Not using a high-RAM runtime')
139
+ print("DEBUG: Not using a high-RAM runtime")
51
140
  else:
52
- print('You are using a high-RAM runtime!')
53
-
54
- # def install_packages(self):
55
- # subprocess.run(["pip", "install", "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git@4e570be9ae4ced8cdc64e498125708e34942befc"])
56
- # subprocess.run(["pip", "install", "--no-deps", "trl<0.9.0", "peft==0.12.0", "accelerate==0.33.0", "bitsandbytes==0.43.3"])
141
+ print("DEBUG: You are using a high-RAM runtime!")
57
142
 
58
143
  def prepare_model(self):
59
- self.model, self.tokenizer = FastLanguageModel.from_pretrained(
144
+ print("DEBUG: Preparing model and tokenizer...")
145
+ self.model, original_tokenizer = FastLanguageModel.from_pretrained(
60
146
  model_name=self.config["model_name"],
61
147
  max_seq_length=self.config["max_seq_length"],
62
148
  dtype=None,
63
- load_in_4bit=self.config["load_in_4bit"]
149
+ load_in_4bit=self.config["load_in_4bit"],
64
150
  )
151
+ print("DEBUG: Model and original tokenizer loaded.")
152
+ if original_tokenizer.pad_token is None:
153
+ original_tokenizer.pad_token = original_tokenizer.eos_token
154
+ original_tokenizer.model_max_length = self.config["max_seq_length"]
155
+ self.chat_tokenizer = get_chat_template(original_tokenizer, chat_template="llama-3.1")
156
+ self.hf_tokenizer = original_tokenizer
157
+ print("DEBUG: Chat tokenizer created; HF tokenizer saved.")
65
158
  self.model = FastLanguageModel.get_peft_model(
66
159
  self.model,
67
- r=self.config["lora_r"],
68
- target_modules=self.config["lora_target_modules"],
69
- lora_alpha=self.config["lora_alpha"],
70
- lora_dropout=self.config["lora_dropout"],
71
- bias=self.config["lora_bias"],
72
- use_gradient_checkpointing=self.config["use_gradient_checkpointing"],
73
- random_state=self.config["random_state"],
74
- use_rslora=self.config["use_rslora"],
75
- loftq_config=self.config["loftq_config"],
160
+ r=16,
161
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
162
+ lora_alpha=16,
163
+ lora_dropout=0,
164
+ bias="none",
165
+ use_gradient_checkpointing="unsloth",
166
+ random_state=3407,
167
+ use_rslora=False,
168
+ loftq_config=None,
76
169
  )
170
+ print("DEBUG: LoRA adapters added.")
77
171
 
78
172
  def process_dataset(self, dataset_info):
79
173
  dataset_name = dataset_info["name"]
80
174
  split_type = dataset_info.get("split_type", "train")
81
- processing_func = getattr(self, dataset_info.get("processing_func", "format_prompts"))
82
- rename = dataset_info.get("rename", {})
83
- filter_data = dataset_info.get("filter_data", False)
84
- filter_column_value = dataset_info.get("filter_column_value", "id")
85
- filter_value = dataset_info.get("filter_value", "alpaca")
86
- num_samples = dataset_info.get("num_samples", 20000)
87
-
175
+ print(f"DEBUG: Loading dataset '{dataset_name}' split '{split_type}'...")
88
176
  dataset = load_dataset(dataset_name, split=split_type)
89
-
90
- if rename:
91
- dataset = dataset.rename_columns(rename)
92
- if filter_data:
93
- dataset = dataset.filter(lambda example: filter_value in example[filter_column_value]).shuffle(seed=42).select(range(num_samples))
94
- dataset = dataset.map(processing_func, batched=True)
177
+ print("DEBUG: Dataset columns:", dataset.column_names)
178
+ if "conversations" in dataset.column_names:
179
+ print("DEBUG: Standardizing dataset (ShareGPT style)...")
180
+ dataset = standardize_sharegpt(dataset)
181
+ else:
182
+ print("DEBUG: Dataset does not have 'conversations'; assuming Alpaca format.")
183
+ print("DEBUG: Applying formatting function to dataset...")
184
+ format_func = partial(formatting_prompts_func, tokenizer=self.chat_tokenizer)
185
+ dataset = dataset.map(format_func, batched=True, remove_columns=dataset.column_names)
186
+ sample = dataset[0]
187
+ print("DEBUG: Sample processed example keys:", list(sample.keys()))
188
+ if "text" in sample:
189
+ print("DEBUG: Sample processed 'text' type:", type(sample["text"]))
190
+ print("DEBUG: Sample processed 'text' content (first 200 chars):", sample["text"][:200])
191
+ else:
192
+ print("DEBUG: Processed sample does not contain 'text'.")
95
193
  return dataset
96
194
 
97
- def format_prompts(self, examples):
98
- alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
99
-
100
- ### Instruction:
101
- {}
102
-
103
- ### Input:
104
- {}
105
-
106
- ### Response:
107
- {}"""
108
- texts = [alpaca_prompt.format(ins, inp, out) + self.tokenizer.eos_token for ins, inp, out in zip(examples["instruction"], examples["input"], examples["output"])]
109
- return {"text": texts}
195
+ def tokenize_dataset(self, dataset):
196
+ print("DEBUG: Tokenizing the entire dataset...")
197
+ tokenized_dataset = dataset.map(
198
+ lambda examples: tokenize_function(examples, self.hf_tokenizer, self.config["max_seq_length"]),
199
+ batched=True
200
+ )
201
+ tokenized_dataset = tokenized_dataset.remove_columns(["text"])
202
+ print("DEBUG: Tokenized dataset sample keys:", tokenized_dataset[0].keys())
203
+ return tokenized_dataset
110
204
 
111
205
  def load_datasets(self):
112
206
  datasets = []
113
207
  for dataset_info in self.config["dataset"]:
208
+ print("DEBUG: Processing dataset info:", dataset_info)
114
209
  datasets.append(self.process_dataset(dataset_info))
115
- return concatenate_datasets(datasets)
210
+ combined = concatenate_datasets(datasets)
211
+ print("DEBUG: Combined dataset has", len(combined), "examples.")
212
+ return combined
116
213
 
117
214
  def train_model(self):
118
- dataset = self.load_datasets()
215
+ print("DEBUG: Starting training...")
216
+ raw_dataset = self.load_datasets()
217
+ tokenized_dataset = self.tokenize_dataset(raw_dataset)
218
+ print("DEBUG: Dataset tokenization complete.")
219
+ training_args = TrainingArguments(
220
+ per_device_train_batch_size=2,
221
+ gradient_accumulation_steps=4,
222
+ warmup_steps=5,
223
+ max_steps=60,
224
+ learning_rate=2e-4,
225
+ fp16=not is_bfloat16_supported(),
226
+ bf16=is_bfloat16_supported(),
227
+ logging_steps=1,
228
+ optim="adamw_8bit",
229
+ weight_decay=0.01,
230
+ lr_scheduler_type="linear",
231
+ seed=3407,
232
+ output_dir="outputs",
233
+ report_to="none",
234
+ remove_unused_columns=False,
235
+ )
236
+ # Since the dataset is pre-tokenized, we supply a dummy dataset_text_field.
119
237
  trainer = SFTTrainer(
120
238
  model=self.model,
121
- tokenizer=self.tokenizer,
122
- train_dataset=dataset,
123
- dataset_text_field=self.config["dataset_text_field"],
239
+ tokenizer=self.hf_tokenizer,
240
+ train_dataset=tokenized_dataset,
241
+ dataset_text_field="input_ids", # Dummy field since data is numeric
124
242
  max_seq_length=self.config["max_seq_length"],
125
- dataset_num_proc=self.config["dataset_num_proc"],
126
- packing=self.config["packing"],
127
- args=TrainingArguments(
128
- per_device_train_batch_size=self.config["per_device_train_batch_size"],
129
- gradient_accumulation_steps=self.config["gradient_accumulation_steps"],
130
- warmup_steps=self.config["warmup_steps"],
131
- num_train_epochs=self.config["num_train_epochs"],
132
- max_steps=self.config["max_steps"],
133
- learning_rate=self.config["learning_rate"],
134
- fp16=not is_bfloat16_supported(),
135
- bf16=is_bfloat16_supported(),
136
- logging_steps=self.config["logging_steps"],
137
- optim=self.config["optim"],
138
- weight_decay=self.config["weight_decay"],
139
- lr_scheduler_type=self.config["lr_scheduler_type"],
140
- seed=self.config["seed"],
141
- output_dir=self.config["output_dir"],
142
- ),
243
+ dataset_num_proc=1, # Use a single process to avoid pickling issues
244
+ packing=False,
245
+ args=training_args,
246
+ )
247
+ from unsloth.chat_templates import train_on_responses_only
248
+ trainer = train_on_responses_only(
249
+ trainer,
250
+ instruction_part="<|start_header_id|>user<|end_header_id|>\n\n",
251
+ response_part="<|start_header_id|>assistant<|end_header_id|>\n\n",
143
252
  )
253
+ print("DEBUG: Beginning trainer.train() ...")
144
254
  trainer.train()
145
- self.model.save_pretrained("lora_model") # Local saving
146
- self.tokenizer.save_pretrained("lora_model")
255
+ print("DEBUG: Training complete. Saving model and tokenizer locally...")
256
+ self.model.save_pretrained("lora_model")
257
+ self.hf_tokenizer.save_pretrained("lora_model")
258
+ print("DEBUG: Saved model and tokenizer to 'lora_model'.")
147
259
 
148
260
  def inference(self, instruction, input_text):
149
261
  FastLanguageModel.for_inference(self.model)
150
- alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
151
-
152
- ### Instruction:
153
- {}
154
-
155
- ### Input:
156
- {}
262
+ messages = [{"role": "user", "content": f"{instruction}\n\nInput: {input_text}"}]
263
+ inputs = self.hf_tokenizer.apply_chat_template(
264
+ messages,
265
+ tokenize=True,
266
+ add_generation_prompt=True,
267
+ return_tensors="pt"
268
+ ).to("cuda")
269
+ outputs = self.model.generate(
270
+ input_ids=inputs,
271
+ max_new_tokens=64,
272
+ use_cache=True,
273
+ temperature=1.5,
274
+ min_p=0.1
275
+ )
276
+ print("DEBUG: Inference output:", self.hf_tokenizer.batch_decode(outputs))
157
277
 
158
- ### Response:
159
- {}"""
160
- inputs = self.tokenizer([alpaca_prompt.format(instruction, input_text, "")], return_tensors="pt").to("cuda")
161
- outputs = self.model.generate(**inputs, max_new_tokens=64, use_cache=True)
162
- print(self.tokenizer.batch_decode(outputs))
163
-
164
278
  def load_model(self):
165
- """Loads the model and tokenizer using the FastLanguageModel library."""
166
279
  from unsloth import FastLanguageModel
167
280
  model, tokenizer = FastLanguageModel.from_pretrained(
168
281
  model_name=self.config["output_dir"],
@@ -177,33 +290,31 @@ class train:
177
290
  shutil.rmtree(self.config["hf_model_name"])
178
291
  self.model.push_to_hub_merged(
179
292
  self.config["hf_model_name"],
180
- self.tokenizer,
293
+ self.hf_tokenizer,
181
294
  save_method="merged_16bit",
182
- token=os.getenv('HF_TOKEN')
295
+ token=os.getenv("HF_TOKEN")
183
296
  )
184
297
 
185
298
  def push_model_gguf(self):
186
299
  self.model.push_to_hub_gguf(
187
300
  self.config["hf_model_name"],
188
- self.tokenizer,
301
+ self.hf_tokenizer,
189
302
  quantization_method=self.config["quantization_method"],
190
- token=os.getenv('HF_TOKEN')
303
+ token=os.getenv("HF_TOKEN")
191
304
  )
192
-
305
+
193
306
  def save_model_gguf(self):
194
307
  self.model.save_pretrained_gguf(
195
308
  self.config["hf_model_name"],
196
- self.tokenizer,
309
+ self.hf_tokenizer,
197
310
  quantization_method="q4_k_m"
198
311
  )
199
312
 
200
313
  def prepare_modelfile_content(self):
201
314
  output_model = self.config["hf_model_name"]
202
315
  gguf_path = f"{output_model}/unsloth.Q4_K_M.gguf"
203
-
204
- # Check if the GGUF file exists. If not, generate it ## TODO Multiple Quantisation other than Q4_K_M.gguf
205
316
  if not os.path.exists(gguf_path):
206
- self.model, self.tokenizer = self.load_model()
317
+ self.model, self.hf_tokenizer = self.load_model()
207
318
  self.save_model_gguf()
208
319
  return f"""FROM {output_model}/unsloth.Q4_K_M.gguf
209
320
 
@@ -224,9 +335,8 @@ PARAMETER stop "<|reserved_special_token_"
224
335
 
225
336
  def create_and_push_ollama_model(self):
226
337
  modelfile_content = self.prepare_modelfile_content()
227
- with open('Modelfile', 'w') as file:
338
+ with open("Modelfile", "w") as file:
228
339
  file.write(modelfile_content)
229
-
230
340
  subprocess.run(["ollama", "serve"])
231
341
  subprocess.run(["ollama", "create", f"{self.config['ollama_model']}:{self.config['model_parameters']}", "-f", "Modelfile"])
232
342
  subprocess.run(["ollama", "push", f"{self.config['ollama_model']}:{self.config['model_parameters']}"])
@@ -235,42 +345,30 @@ PARAMETER stop "<|reserved_special_token_"
235
345
  self.print_system_info()
236
346
  self.check_gpu()
237
347
  self.check_ram()
238
- # self.install_packages()
239
348
  if self.config.get("train", "true").lower() == "true":
240
349
  self.prepare_model()
241
350
  self.train_model()
242
-
243
351
  if self.config.get("huggingface_save", "true").lower() == "true":
244
- # self.model, self.tokenizer = self.load_model()
245
352
  self.save_model_merged()
246
-
247
353
  if self.config.get("huggingface_save_gguf", "true").lower() == "true":
248
- # self.model, self.tokenizer = self.load_model()
249
354
  self.push_model_gguf()
250
-
251
- # if self.config.get("save_gguf", "true").lower() == "true": ## TODO
252
- # self.model, self.tokenizer = self.load_model()
253
- # self.save_model_gguf()
254
-
255
- # if self.config.get("save_merged", "true").lower() == "true": ## TODO
256
- # self.model, self.tokenizer = self.load_model()
257
- # self.save_model_merged()
258
-
259
355
  if self.config.get("ollama_save", "true").lower() == "true":
260
356
  self.create_and_push_ollama_model()
261
357
 
262
-
263
358
  def main():
264
359
  import argparse
265
- parser = argparse.ArgumentParser(description='PraisonAI Training Script')
266
- parser.add_argument('command', choices=['train'], help='Command to execute')
267
- parser.add_argument('--config', default='config.yaml', help='Path to configuration file')
360
+ parser = argparse.ArgumentParser(description="PraisonAI Training Script")
361
+ parser.add_argument("command", choices=["train"], help="Command to execute")
362
+ parser.add_argument("--config", default="config.yaml", help="Path to configuration file")
363
+ parser.add_argument("--model", type=str, help="Model name")
364
+ parser.add_argument("--hf", type=str, help="Hugging Face model name")
365
+ parser.add_argument("--ollama", type=str, help="Ollama model name")
366
+ parser.add_argument("--dataset", type=str, help="Dataset name for training")
268
367
  args = parser.parse_args()
269
368
 
270
- if args.command == 'train':
271
- ai = train(config_path=args.config)
272
- ai.run()
273
-
369
+ if args.command == "train":
370
+ trainer_obj = TrainModel(config_path=args.config)
371
+ trainer_obj.run()
274
372
 
275
- if __name__ == '__main__':
373
+ if __name__ == "__main__":
276
374
  main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: PraisonAI
3
- Version: 2.0.62
3
+ Version: 2.0.64
4
4
  Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
5
5
  Author: Mervin Praison
6
6
  Requires-Python: >=3.10,<3.13
@@ -208,6 +208,8 @@ const agent = new Agent({ instructions: 'You are a helpful AI assistant' });
208
208
  agent.start('Write a movie script about a robot in Mars');
209
209
  ```
210
210
 
211
+ ![PraisonAI CLI Demo](docs/demo/praisonai-cli-demo.gif)
212
+
211
213
  ## AI Agents Flow
212
214
 
213
215
  ```mermaid
@@ -4,8 +4,8 @@ praisonai/agents_generator.py,sha256=j8lYudAr3wlVBQLng3iYL6mfRqx2i9M6wlryxIVRzDA
4
4
  praisonai/api/call.py,sha256=krOfTCZM_bdbsNuWQ1PijzCHECkDvEi9jIvvZaDQUUU,11035
5
5
  praisonai/auto.py,sha256=uLDm8CU3L_3amZsd55yzf9RdBF1uW-BGSx7nl9ctNZ4,8680
6
6
  praisonai/chainlit_ui.py,sha256=bNR7s509lp0I9JlJNvwCZRUZosC64qdvlFCt8NmFamQ,12216
7
- praisonai/cli.py,sha256=T4fXYBNNbrP-d05tZyyaCFv5yk1l8m5Tdq29UkUozEo,24129
8
- praisonai/deploy.py,sha256=-RfcWiORrXzvQulxuirz5_eNUWRp8DlZOiPU6_A2kZ4,6028
7
+ praisonai/cli.py,sha256=C4MoIXaLHldRNYPqXpHPpD8DjJU2qUJqZMHzbem7te4,25254
8
+ praisonai/deploy.py,sha256=iwY-SWVosSSDGSoHiX2k0ar1BGQee0rvyU8aTSD5IEA,6028
9
9
  praisonai/inbuilt_tools/__init__.py,sha256=fai4ZJIKz7-iOnGZv5jJX0wmT77PKa4x2jqyaJddKFA,569
10
10
  praisonai/inbuilt_tools/autogen_tools.py,sha256=kJdEv61BTYvdHOaURNEpBcWq8Rs-oC03loNFTIjT-ak,4687
11
11
  praisonai/inc/__init__.py,sha256=sPDlYBBwdk0VlWzaaM_lG0_LD07lS2HRGvPdxXJFiYg,62
@@ -30,10 +30,10 @@ praisonai/setup/build.py,sha256=NyTAXQ_UZ8vKo_KwCINp8ctmauZyCMDkw1rys3ay0ec,646
30
30
  praisonai/setup/config.yaml,sha256=sr_D1RIvv3LQ_eueOMZV0rAUiWTR-n2xuE1RhKK6b34,1211
31
31
  praisonai/setup/post_install.py,sha256=rlUYJhT4JXVrL2RQih5VUPr4PQEmpVypoRpe_vJDMBk,730
32
32
  praisonai/setup/setup_conda_env.py,sha256=4QiWrqgEObivzOMwfJgWaCPpUEpB68cQ6lFwVwFoufk,816
33
- praisonai/setup/setup_conda_env.sh,sha256=te7s0KHsTi7XM-vkNvE0dKC1HeU2tXxqE-sPUScV6fY,2718
33
+ praisonai/setup/setup_conda_env.sh,sha256=Xr9Az71A7jUozttnGQFHaUHIKZne4VIq9PPn6wrA18w,3466
34
34
  praisonai/setup.py,sha256=0jHgKnIPCtBZiGYaYyTz3PzrJI6nBy55VXk2UctXlDo,373
35
35
  praisonai/test.py,sha256=OL-wesjA5JTohr8rtr6kWoaS4ImkJg2l0GXJ-dUUfRU,4090
36
- praisonai/train.py,sha256=DvORlrwKOD-2v4r_z84eV3LsfzpNs-WnPKb5cQB3_t4,11071
36
+ praisonai/train.py,sha256=m3-N2XWRernBWRg_xRr7bilxhq7WvFJYwxfvjROIjEw,15541
37
37
  praisonai/ui/README.md,sha256=QG9yucvBieVjCjWFzu6hL9xNtYllkoqyJ_q1b0YYAco,1124
38
38
  praisonai/ui/agents.py,sha256=1qsWE2yCaQKhuc-1uLHdMfZJeOXzBtp4pe5q7bk2EuA,32813
39
39
  praisonai/ui/callbacks.py,sha256=V4_-GjxmjDFmugUZGfQHKtNSysx7rT6i1UblbM_8lIM,1968
@@ -82,8 +82,8 @@ praisonai/ui/realtimeclient/tools.py,sha256=IJOYwVOBW5Ocn5_iV9pFkmSKR3WU3YpX3kwF
82
82
  praisonai/ui/sql_alchemy.py,sha256=oekZOXlRGMJ2SuC-lmgMMIzAmvbMg2DWeGTSpOzbVBM,29674
83
83
  praisonai/ui/tools.md,sha256=Ad3YH_ZCLMWlz3mDXllQnQ_S5l55LWqLdcZSh-EXrHI,3956
84
84
  praisonai/version.py,sha256=ugyuFliEqtAwQmH4sTlc16YXKYbFWDmfyk87fErB8-8,21
85
- praisonai-2.0.62.dist-info/LICENSE,sha256=kqvFysVlnFxYOu0HxCe2HlmZmJtdmNGOxWRRkT9TsWc,1035
86
- praisonai-2.0.62.dist-info/METADATA,sha256=TARzV6EAJ7_dEkYMRcJ2vj2AQ7fpDXTGNe9vsMGHNZQ,21885
87
- praisonai-2.0.62.dist-info/WHEEL,sha256=OiNztsphQWM3l0xJ9BHQRElMnxzHbt1M68r2N60f8T8,110
88
- praisonai-2.0.62.dist-info/entry_points.txt,sha256=I_xc6a6MNTTfLxYmAxe0rgey0G-_hbY07oFW-ZDnkw4,135
89
- praisonai-2.0.62.dist-info/RECORD,,
85
+ praisonai-2.0.64.dist-info/LICENSE,sha256=kqvFysVlnFxYOu0HxCe2HlmZmJtdmNGOxWRRkT9TsWc,1035
86
+ praisonai-2.0.64.dist-info/METADATA,sha256=V6BK_5k6vwE-jzCGo8ea1Y6o-afs4mIa2n1BVaayZKE,21942
87
+ praisonai-2.0.64.dist-info/WHEEL,sha256=OiNztsphQWM3l0xJ9BHQRElMnxzHbt1M68r2N60f8T8,110
88
+ praisonai-2.0.64.dist-info/entry_points.txt,sha256=I_xc6a6MNTTfLxYmAxe0rgey0G-_hbY07oFW-ZDnkw4,135
89
+ praisonai-2.0.64.dist-info/RECORD,,