Compare commits

...
18 Commits
Author SHA1 Message Date
Mervin Praison 5744ae853e Merge pull request #365 from MervinPraison/develop
Develop
2025-02-04 07:36:59 +00:00
MervinPraison bd6e3da4ff Bump version to 2.0.64 and update dependencies
- Updated version across multiple files (praisonai.rb, pyproject.toml, uv.lock, Dockerfile, deploy.py, docs)
- Added TRAIN_AVAILABLE flag in cli.py for optional training feature
- Updated Conda environment setup script with latest dependencies
- Minor improvements to training and deployment configurations
2025-02-04 07:36:43 +00:00
MervinPraison f2f51d9e8b Refactor train.py with Enhanced Dataset Processing and Tokenization
- Completely rewrote training script with robust dataset handling
- Added support for ShareGPT and Alpaca-style datasets
- Implemented advanced formatting and tokenization functions
- Enhanced debug logging and error handling
- Simplified model preparation and training workflow
- Updated chat template and tokenization strategies
- Improved inference and model saving methods
2025-02-03 19:38:57 +00:00
Mervin Praison 4a1098603d Merge pull request #364 from MervinPraison/develop
cli docs
2025-02-03 07:41:14 +00:00
MervinPraison 5468e35bc1 cli docs 2025-02-03 07:40:46 +00:00
Mervin Praison b468267a87 Merge pull request #362 from MervinPraison/develop
Develop
2025-02-02 06:45:30 +00:00
MervinPraison 48141bbd9e update readme 2025-02-02 06:45:12 +00:00
MervinPraison b3cdf53d07 adding demo 2025-02-02 06:43:16 +00:00
Mervin Praison aa594ddc32 Merge pull request #361 from MervinPraison/develop
update read me 2
2025-02-02 06:30:57 +00:00
MervinPraison 5f330e8fb2 update read me 2 2025-02-02 06:30:37 +00:00
Mervin Praison ff94e128de Merge pull request #360 from MervinPraison/develop
update read me
2025-02-02 06:28:35 +00:00
MervinPraison 854a016fc5 update read me 2025-02-02 06:28:08 +00:00
Mervin Praison 12e8ed7b77 Merge pull request #359 from MervinPraison/develop
Update CLI Documentation with Latest LLM Model Option
2025-02-01 21:51:34 +00:00
MervinPraison 13c5f5537e Update CLI Documentation with Latest LLM Model Option
- Updated CLI documentation to reflect the latest LLM model option
- Changed example command to use `gpt-4o-mini` instead of `gpt-4-mini`
- Minor documentation refinement for CLI feature
2025-02-01 21:51:20 +00:00
Mervin Praison e7c1614ac8 Merge pull request #358 from MervinPraison/develop
Add CLI Documentation and Enhance CLI Functionality
2025-02-01 00:20:09 +00:00
MervinPraison 7cc6db2a34 Add CLI Documentation and Enhance CLI Functionality
- Added comprehensive CLI documentation in docs/features/cli.mdx
- Enhanced CLI functionality with new command-line options
- Implemented `--llm` flag to specify LLM models for direct prompts
- Updated CLI parsing to support flexible LLM model selection
- Improved direct prompt handling across different AI frameworks
- Bumped package version to 2.0.63
2025-02-01 00:19:44 +00:00
Mervin Praison b869bbc346 Merge pull request #357 from MervinPraison/develop
Enhance CLI with Direct Prompt Handling and Framework Flexibility
2025-02-01 00:01:27 +00:00
MervinPraison 5492be4412 Enhance CLI with Direct Prompt Handling and Framework Flexibility
- Added direct prompt handling in CLI for immediate task execution
- Implemented fallback mechanism to support multiple AI agent frameworks (PraisonAI, CrewAI, AutoGen)
- Updated CLI parsing to handle various input scenarios
- Simplified agent initialization for direct prompts
- Improved error messaging for framework installation
- Bumped package version to 2.0.62
2025-02-01 00:01:12 +00:00
14 changed files with 496 additions and 2043 deletions

No files matched your search

+8 -1885
View File
File diff suppressed because it is too large. Load diff
+2
View File
@@ -118,6 +118,8 @@ const agent = new Agent({ instructions: 'You are a helpful AI assistant' });
agent.start('Write a movie script about a robot in Mars');
```
![PraisonAI CLI Demo](docs/demo/praisonai-cli-demo.gif)
## AI Agents Flow
```mermaid
+1 -1
View File
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.61 gunicorn markdown
RUN pip install flask praisonai==2.0.64 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
+1 -1
View File
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
file.write(&#34;FROM python:3.11-slim\n&#34;)
file.write(&#34;WORKDIR /app\n&#34;)
file.write(&#34;COPY . .\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.61 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.64 gunicorn markdown\n&#34;)
file.write(&#34;EXPOSE 8080\n&#34;)
file.write(&#39;CMD [&#34;gunicorn&#34;, &#34;-b&#34;, &#34;0.0.0.0:8080&#34;, &#34;api:app&#34;]\n&#39;)
Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

+117
View File
@@ -0,0 +1,117 @@
---
title: "Command Line Interface"
sidebarTitle: "CLI"
description: "Use PraisonAI directly from your terminal with simple commands"
icon: "terminal"
---
```mermaid
flowchart LR
Start((🚀)) --> CLI[CLI Command]
CLI --> Agent[AI Agent]
Agent --> Config[Config/YAML]
Config --> Out((🏁))
style Start fill:#8B0000,color:#fff,stroke:#8B0000
style CLI fill:#2E8B57,color:#fff
style Agent fill:#2E8B57,color:#fff
style Config fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff,stroke:#8B0000
```
PraisonAI CLI provides a simple way to interact with AI agents directly from your terminal. You can run quick commands, specify LLM options, or use YAML configuration files for more complex scenarios.
<Frame>
<img src="../demo/praisonai-cli-demo.gif" alt="PraisonAI CLI Demo" />
</Frame>
## Quick Start
<Steps>
<Step title="Install Package">
Install the PraisonAI package:
```bash
pip install praisonai
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key as an environment variable:
```bash
export OPENAI_API_KEY=your_api_key_here
```
</Step>
</Steps>
## Usage Examples
<AccordionGroup>
<Accordion title="Simple Command" icon="terminal" defaultOpen>
Run a simple command directly:
```bash
praisonai "write a movie script in 3 lines"
```
</Accordion>
<Accordion title="With LLM Option" icon="wand-magic-sparkles" defaultOpen>
Specify a different LLM model:
```bash
praisonai "write a movie script in 3 lines" --llm gpt-4o-mini
```
</Accordion>
<Accordion title="Using YAML Config" icon="file-code" defaultOpen>
Run agents defined in a YAML file:
```bash
praisonai agents.yaml
```
</Accordion>
</AccordionGroup>
## Configuration
<Tabs>
<Tab title="Create Config">
Initialize a new agents.yaml file for your project:
```bash
praisonai --init "Create a movie script about AI"
```
This will create an `agents.yaml` file with predefined configuration for your task.
</Tab>
<Tab title="Auto Mode">
Let PraisonAI automatically create and configure agents:
```bash
praisonai --auto "Create a movie script about AI"
```
This mode will analyze your task and set up appropriate agents automatically.
</Tab>
</Tabs>
## Features
<CardGroup cols={2}>
<Card title="Simple Commands" icon="terminal">
Run AI tasks directly from your terminal with simple commands.
</Card>
<Card title="LLM Options" icon="wand-magic-sparkles">
Choose from different LLM models for your specific needs.
</Card>
<Card title="YAML Support" icon="file-code">
Use YAML files for complex agent configurations and workflows.
</Card>
<Card title="Auto Configuration" icon="robot">
Automatic agent setup based on task requirements.
</Card>
</CardGroup>
## Next Steps
<CardGroup>
<Card title="YAML Configuration" icon="file-code" href="/config">
Learn more about YAML configuration options
</Card>
<Card title="API Reference" icon="code" href="/api-reference">
View the complete API documentation
</Card>
</CardGroup>
+1
View File
@@ -119,6 +119,7 @@
{
"group": "Features",
"pages": [
"features/cli",
"features/autoagents",
"features/image-generation",
"features/selfreflection",
+1 -1
View File
@@ -3,7 +3,7 @@ class Praisonai < Formula
desc "AI tools for various AI applications"
homepage "https://github.com/MervinPraison/PraisonAI"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.61.tar.gz"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.64.tar.gz"
sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32" # Replace with actual SHA256 checksum
license "MIT"
+101 -8
View File
@@ -25,7 +25,7 @@ CALL_MODULE_AVAILABLE = False
CREWAI_AVAILABLE = False
AUTOGEN_AVAILABLE = False
PRAISONAI_AVAILABLE = False
TRAIN_AVAILABLE = False
try:
# Create necessary directories and set CHAINLIT_APP_ROOT
if "CHAINLIT_APP_ROOT" not in os.environ:
@@ -72,6 +72,12 @@ try:
except ImportError:
pass
try:
import accelerate
TRAIN_AVAILABLE = True
except ImportError:
pass
logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO'), format='%(asctime)s - %(levelname)s - %(message)s')
logging.getLogger('alembic').setLevel(logging.ERROR)
logging.getLogger('gradio').setLevel(logging.ERROR)
@@ -140,6 +146,8 @@ class PraisonAI:
provided arguments.
"""
args = self.parse_args()
# Store args for use in handle_direct_prompt
self.args = args
invocation_cmd = "praisonai"
version_string = f"PraisonAI version {__version__}"
@@ -148,8 +156,16 @@ class PraisonAI:
if args.command:
if args.command.startswith("tests.test"): # Argument used for testing purposes
print("test")
return "test"
else:
self.agent_file = args.command
elif hasattr(args, 'direct_prompt') and args.direct_prompt:
result = self.handle_direct_prompt(args.direct_prompt)
print(result)
return result
else:
# Default to agents.yaml if no command provided
self.agent_file = "agents.yaml"
if args.deploy:
from .deploy import CloudDeployer
@@ -285,14 +301,18 @@ class PraisonAI:
"""
Parse the command-line arguments for the PraisonAI CLI.
"""
# Define special commands
special_commands = ['chat', 'code', 'call', 'realtime', 'train', 'ui']
parser = argparse.ArgumentParser(prog="praisonai", description="praisonAI command-line interface")
parser.add_argument("--framework", choices=["crewai", "autogen", "praisonai"], help="Specify the framework")
parser.add_argument("--ui", choices=["chainlit", "gradio"], help="Specify the UI framework (gradio or chainlit).")
parser.add_argument("--auto", nargs=argparse.REMAINDER, help="Enable auto mode and pass arguments for it")
parser.add_argument("--init", nargs=argparse.REMAINDER, help="Initialize agents with optional topic")
parser.add_argument("command", nargs="?", help="Command to run")
parser.add_argument("command", nargs="?", help="Command to run or direct prompt")
parser.add_argument("--deploy", action="store_true", help="Deploy the application")
parser.add_argument("--model", type=str, help="Model name")
parser.add_argument("--llm", type=str, help="LLM model to use for direct prompts")
parser.add_argument("--hf", type=str, help="Hugging Face model name")
parser.add_argument("--ollama", type=str, help="Ollama model name")
parser.add_argument("--dataset", type=str, help="Dataset name for training", default="yahma/alpaca-cleaned")
@@ -301,6 +321,7 @@ class PraisonAI:
parser.add_argument("--public", action="store_true", help="Use ngrok to expose the server publicly (only with --call)")
args, unknown_args = parser.parse_known_args()
# Handle special cases first
if unknown_args and unknown_args[0] == '-b' and unknown_args[1] == 'api:app':
args.command = 'agents.yaml'
if args.command == 'api:app' or args.command == '/app/api:app':
@@ -331,9 +352,7 @@ class PraisonAI:
call_module.main(call_args)
sys.exit(0)
# Handle special commands first
special_commands = ['chat', 'code', 'call', 'realtime', 'train', 'ui']
# Handle special commands
if args.command in special_commands:
if args.command == 'chat':
if not CHAINLIT_AVAILABLE:
@@ -380,9 +399,13 @@ class PraisonAI:
sys.exit(0)
elif args.command == 'train':
print("[red]ERROR: Train feature is not installed. Install with:[/red]")
print("\npip install \"praisonai[train]\"\n")
sys.exit(1)
if not TRAIN_AVAILABLE:
print("[red]ERROR: Train feature is not installed. Install with:[/red]")
print("\npip install \"praisonai[train]\"\n")
sys.exit(1)
package_root = os.path.dirname(os.path.abspath(__file__))
config_yaml_destination = os.path.join(os.getcwd(), 'config.yaml')
elif args.command == 'ui':
if not CHAINLIT_AVAILABLE:
@@ -402,8 +425,78 @@ class PraisonAI:
print("pip install praisonaiagents # For PraisonAIAgents\n")
sys.exit(1)
# Handle direct prompt if command is not a special command or file
if args.command and not args.command.endswith('.yaml') and args.command not in special_commands:
args.direct_prompt = args.command
args.command = None
return args
def handle_direct_prompt(self, prompt):
"""
Handle direct prompt by creating a single agent and running it.
"""
if PRAISONAI_AVAILABLE:
agent_config = {
"name": "DirectAgent",
"role": "Assistant",
"goal": "Complete the given task",
"backstory": "You are a helpful AI assistant"
}
# Add llm if specified
if hasattr(self, 'args') and self.args.llm:
agent_config["llm"] = self.args.llm
agent = PraisonAgent(**agent_config)
result = agent.start(prompt)
return ""
elif CREWAI_AVAILABLE:
agent_config = {
"name": "DirectAgent",
"role": "Assistant",
"goal": "Complete the given task",
"backstory": "You are a helpful AI assistant"
}
# Add llm if specified
if hasattr(self, 'args') and self.args.llm:
agent_config["llm"] = self.args.llm
agent = Agent(**agent_config)
task = Task(
description=prompt,
agent=agent
)
crew = Crew(
agents=[agent],
tasks=[task]
)
return crew.kickoff()
elif AUTOGEN_AVAILABLE:
config_list = self.config_list
# Add llm if specified
if hasattr(self, 'args') and self.args.llm:
config_list[0]['model'] = self.args.llm
assistant = autogen.AssistantAgent(
name="DirectAgent",
llm_config={"config_list": config_list}
)
user_proxy = autogen.UserProxyAgent(
name="UserProxy",
code_execution_config={"work_dir": "coding"}
)
user_proxy.initiate_chat(assistant, message=prompt)
return "Task completed"
else:
print("[red]ERROR: No framework is installed. Please install at least one framework:[/red]")
print("\npip install \"praisonai\\[crewai]\" # For CrewAI")
print("pip install \"praisonai\\[autogen]\" # For AutoGen")
print("pip install \"praisonai\\[crewai,autogen]\" # For both frameworks\n")
print("pip install praisonaiagents # For PraisonAIAgents\n")
sys.exit(1)
def create_chainlit_chat_interface(self):
"""
Create a Chainlit interface for the chat application.
+1 -1
View File
@@ -56,7 +56,7 @@ class CloudDeployer:
file.write("FROM python:3.11-slim\n")
file.write("WORKDIR /app\n")
file.write("COPY . .\n")
file.write("RUN pip install flask praisonai==2.0.61 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.0.64 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+26 -7
View File
@@ -11,6 +11,20 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
# Linux
MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh"
# Check and install libcurl development package if not present
if command -v dpkg &> /dev/null; then
if ! dpkg -s libcurl4-openssl-dev &> /dev/null; then
echo "libcurl4-openssl-dev is not installed. Installing..."
sudo apt-get update
sudo apt-get install -y libcurl4-openssl-dev
else
echo "libcurl4-openssl-dev is already installed."
fi
else
echo "Non-Debian based Linux detected. Please ensure libcurl development libraries are installed."
fi
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
# Windows
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
# Linux
conda create --name $ENV_NAME python=3.10 pytorch=2.3.0 cudatoolkit=11.8 -c pytorch -c nvidia -y
fi
# conda activate $ENV_NAME
else
echo "Creating new environment $ENV_NAME..."
if [[ "$OSTYPE" == "darwin"* ]]; then
@@ -54,19 +67,25 @@ else
# Linux
conda create --name $ENV_NAME python=3.10 pytorch=2.3.0 cudatoolkit=11.8 -c pytorch -c nvidia -y
fi
# conda activate $ENV_NAME
fi
# source $HOME/miniconda/bin/activate $ENV_NAME
# Activate the environment
source $HOME/miniconda/bin/activate $ENV_NAME
# Get full path of pip
# Install cmake via conda
echo "Installing cmake..."
conda install -y cmake
# Get full path of pip within the activated environment
PIP_FULL_PATH=$(conda run -n $ENV_NAME which pip)
# Install other packages within the activated environment
# Use PIP_FULL_PATH to run pip commands
# Install other packages within the activated environment using pip
$PIP_FULL_PATH install --upgrade pip
$PIP_FULL_PATH install "xformers==0.0.26.post1"
$PIP_FULL_PATH install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git@4e570be9ae4ced8cdc64e498125708e34942befc"
$PIP_FULL_PATH install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git@038e6d4c8d40207a87297ab3aaf787c19b1006d1"
$PIP_FULL_PATH install --no-deps "trl<0.9.0" peft accelerate bitsandbytes
$PIP_FULL_PATH install unsloth_zoo
$PIP_FULL_PATH install cut_cross_entropy
$PIP_FULL_PATH install sentencepiece protobuf datasets huggingface_hub hf_transfer
echo "Setup completed successfully!"
+234 -136
View File
@@ -1,168 +1,281 @@
import subprocess
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This script finetunes a model using Unsloth’s fast training framework.
It supports both ShareGPT and Alpaca‑style datasets by converting raw conversation
data into plain-text prompts using a chat template, then pre‑tokenizing the prompts.
Extra debug logging is added to help trace the root cause of errors.
"""
import os
import sys
import yaml
import torch
import shutil
import subprocess
from transformers import TextStreamer
from unsloth import FastLanguageModel, is_bfloat16_supported
from trl import SFTTrainer
from transformers import TrainingArguments
from datasets import load_dataset, concatenate_datasets, Dataset
from datasets import load_dataset, concatenate_datasets
from psutil import virtual_memory
from unsloth.chat_templates import standardize_sharegpt, get_chat_template
from functools import partial
class train:
#####################################
# Step 1: Formatting Raw Conversations
#####################################
def formatting_prompts_func(examples, tokenizer):
"""
Converts each example’s conversation into a single plain-text prompt.
If the example has a "conversations" field, process it as ShareGPT-style.
Otherwise, assume Alpaca-style data with "instruction", "input", and "output" fields.
"""
print("DEBUG: formatting_prompts_func() received batch with keys:", list(examples.keys()))
texts = []
# Check if the example has a "conversations" field.
if "conversations" in examples:
for convo in examples["conversations"]:
try:
formatted = tokenizer.apply_chat_template(
convo,
tokenize=False, # Return a plain string
add_generation_prompt=False
)
except Exception as e:
print(f"ERROR in apply_chat_template (conversations): {e}")
formatted = ""
# Flatten list if necessary
if isinstance(formatted, list):
formatted = formatted[0] if len(formatted) == 1 else "\n".join(formatted)
texts.append(formatted)
else:
# Assume Alpaca format: use "instruction", "input", and "output" keys.
instructions = examples.get("instruction", [])
inputs_list = examples.get("input", [])
outputs_list = examples.get("output", [])
# If any field is missing, replace with empty string.
for ins, inp, out in zip(instructions, inputs_list, outputs_list):
# Create a conversation-like structure.
convo = [
{"role": "user", "content": ins + (f"\nInput: {inp}" if inp.strip() != "" else "")},
{"role": "assistant", "content": out}
]
try:
formatted = tokenizer.apply_chat_template(
convo,
tokenize=False,
add_generation_prompt=False
)
except Exception as e:
print(f"ERROR in apply_chat_template (alpaca): {e}")
formatted = ""
if isinstance(formatted, list):
formatted = formatted[0] if len(formatted) == 1 else "\n".join(formatted)
texts.append(formatted)
if texts:
print("DEBUG: Raw texts sample (first 200 chars):", texts[0][:200])
return {"text": texts}
#####################################
# Step 2: Tokenizing the Prompts
#####################################
def tokenize_function(examples, hf_tokenizer, max_length):
"""
Tokenizes a batch of text prompts with padding and truncation enabled.
"""
flat_texts = []
for t in examples["text"]:
if isinstance(t, list):
t = t[0] if len(t) == 1 else " ".join(t)
flat_texts.append(t)
print("DEBUG: Tokenizing a batch of size:", len(flat_texts))
tokenized = hf_tokenizer(
flat_texts,
padding="max_length",
truncation=True,
max_length=max_length,
return_tensors="pt",
)
tokenized = {key: value.tolist() for key, value in tokenized.items()}
sample_key = list(tokenized.keys())[0]
print("DEBUG: Tokenized sample (first 10 tokens of", sample_key, "):", tokenized[sample_key][0][:10])
return tokenized
#####################################
# Main Training Class
#####################################
class TrainModel:
def __init__(self, config_path="config.yaml"):
self.load_config(config_path)
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.model, self.tokenizer = None, None
self.model = None
self.hf_tokenizer = None # The underlying HF tokenizer
self.chat_tokenizer = None # Chat wrapper for formatting
def load_config(self, path):
with open(path, "r") as file:
self.config = yaml.safe_load(file)
print("DEBUG: Loaded config:", self.config)
def print_system_info(self):
print(f"PyTorch version: {torch.__version__}")
print(f"CUDA version: {torch.version.cuda}")
print("DEBUG: PyTorch version:", torch.__version__)
print("DEBUG: CUDA version:", torch.version.cuda)
if torch.cuda.is_available():
device_capability = torch.cuda.get_device_capability()
print(f"CUDA Device Capability: {device_capability}")
print("DEBUG: CUDA Device Capability:", torch.cuda.get_device_capability())
else:
print("CUDA is not available")
python_version = sys.version
pip_version = subprocess.check_output(['pip', '--version']).decode().strip()
python_path = sys.executable
pip_path = subprocess.check_output(['which', 'pip']).decode().strip()
print(f"Python Version: {python_version}")
print(f"Pip Version: {pip_version}")
print(f"Python Path: {python_path}")
print(f"Pip Path: {pip_path}")
print("DEBUG: CUDA is not available")
print("DEBUG: Python Version:", sys.version)
print("DEBUG: Python Path:", sys.executable)
def check_gpu(self):
gpu_stats = torch.cuda.get_device_properties(0)
print(f"GPU = {gpu_stats.name}. Max memory = {round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)} GB.")
print(f"DEBUG: GPU = {gpu_stats.name}. Max memory = {round(gpu_stats.total_memory/(1024**3),3)} GB.")
def check_ram(self):
ram_gb = virtual_memory().total / 1e9
print('Your runtime has {:.1f} gigabytes of available RAM\n'.format(ram_gb))
print(f"DEBUG: Your runtime has {ram_gb:.1f} gigabytes of available RAM")
if ram_gb < 20:
print('Not using a high-RAM runtime')
print("DEBUG: Not using a high-RAM runtime")
else:
print('You are using a high-RAM runtime!')
# def install_packages(self):
# subprocess.run(["pip", "install", "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git@4e570be9ae4ced8cdc64e498125708e34942befc"])
# subprocess.run(["pip", "install", "--no-deps", "trl<0.9.0", "peft==0.12.0", "accelerate==0.33.0", "bitsandbytes==0.43.3"])
print("DEBUG: You are using a high-RAM runtime!")
def prepare_model(self):
self.model, self.tokenizer = FastLanguageModel.from_pretrained(
print("DEBUG: Preparing model and tokenizer...")
self.model, original_tokenizer = FastLanguageModel.from_pretrained(
model_name=self.config["model_name"],
max_seq_length=self.config["max_seq_length"],
dtype=None,
load_in_4bit=self.config["load_in_4bit"]
load_in_4bit=self.config["load_in_4bit"],
)
print("DEBUG: Model and original tokenizer loaded.")
if original_tokenizer.pad_token is None:
original_tokenizer.pad_token = original_tokenizer.eos_token
original_tokenizer.model_max_length = self.config["max_seq_length"]
self.chat_tokenizer = get_chat_template(original_tokenizer, chat_template="llama-3.1")
self.hf_tokenizer = original_tokenizer
print("DEBUG: Chat tokenizer created; HF tokenizer saved.")
self.model = FastLanguageModel.get_peft_model(
self.model,
r=self.config["lora_r"],
target_modules=self.config["lora_target_modules"],
lora_alpha=self.config["lora_alpha"],
lora_dropout=self.config["lora_dropout"],
bias=self.config["lora_bias"],
use_gradient_checkpointing=self.config["use_gradient_checkpointing"],
random_state=self.config["random_state"],
use_rslora=self.config["use_rslora"],
loftq_config=self.config["loftq_config"],
r=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_alpha=16,
lora_dropout=0,
bias="none",
use_gradient_checkpointing="unsloth",
random_state=3407,
use_rslora=False,
loftq_config=None,
)
print("DEBUG: LoRA adapters added.")
def process_dataset(self, dataset_info):
dataset_name = dataset_info["name"]
split_type = dataset_info.get("split_type", "train")
processing_func = getattr(self, dataset_info.get("processing_func", "format_prompts"))
rename = dataset_info.get("rename", {})
filter_data = dataset_info.get("filter_data", False)
filter_column_value = dataset_info.get("filter_column_value", "id")
filter_value = dataset_info.get("filter_value", "alpaca")
num_samples = dataset_info.get("num_samples", 20000)
print(f"DEBUG: Loading dataset '{dataset_name}' split '{split_type}'...")
dataset = load_dataset(dataset_name, split=split_type)
if rename:
dataset = dataset.rename_columns(rename)
if filter_data:
dataset = dataset.filter(lambda example: filter_value in example[filter_column_value]).shuffle(seed=42).select(range(num_samples))
dataset = dataset.map(processing_func, batched=True)
print("DEBUG: Dataset columns:", dataset.column_names)
if "conversations" in dataset.column_names:
print("DEBUG: Standardizing dataset (ShareGPT style)...")
dataset = standardize_sharegpt(dataset)
else:
print("DEBUG: Dataset does not have 'conversations'; assuming Alpaca format.")
print("DEBUG: Applying formatting function to dataset...")
format_func = partial(formatting_prompts_func, tokenizer=self.chat_tokenizer)
dataset = dataset.map(format_func, batched=True, remove_columns=dataset.column_names)
sample = dataset[0]
print("DEBUG: Sample processed example keys:", list(sample.keys()))
if "text" in sample:
print("DEBUG: Sample processed 'text' type:", type(sample["text"]))
print("DEBUG: Sample processed 'text' content (first 200 chars):", sample["text"][:200])
else:
print("DEBUG: Processed sample does not contain 'text'.")
return dataset
def format_prompts(self, examples):
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.
### Instruction:
{}
### Input:
{}
### Response:
{}"""
texts = [alpaca_prompt.format(ins, inp, out) + self.tokenizer.eos_token for ins, inp, out in zip(examples["instruction"], examples["input"], examples["output"])]
return {"text": texts}
def tokenize_dataset(self, dataset):
print("DEBUG: Tokenizing the entire dataset...")
tokenized_dataset = dataset.map(
lambda examples: tokenize_function(examples, self.hf_tokenizer, self.config["max_seq_length"]),
batched=True
)
tokenized_dataset = tokenized_dataset.remove_columns(["text"])
print("DEBUG: Tokenized dataset sample keys:", tokenized_dataset[0].keys())
return tokenized_dataset
def load_datasets(self):
datasets = []
for dataset_info in self.config["dataset"]:
print("DEBUG: Processing dataset info:", dataset_info)
datasets.append(self.process_dataset(dataset_info))
return concatenate_datasets(datasets)
combined = concatenate_datasets(datasets)
print("DEBUG: Combined dataset has", len(combined), "examples.")
return combined
def train_model(self):
dataset = self.load_datasets()
print("DEBUG: Starting training...")
raw_dataset = self.load_datasets()
tokenized_dataset = self.tokenize_dataset(raw_dataset)
print("DEBUG: Dataset tokenization complete.")
training_args = TrainingArguments(
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
warmup_steps=5,
max_steps=60,
learning_rate=2e-4,
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
logging_steps=1,
optim="adamw_8bit",
weight_decay=0.01,
lr_scheduler_type="linear",
seed=3407,
output_dir="outputs",
report_to="none",
remove_unused_columns=False,
)
# Since the dataset is pre-tokenized, we supply a dummy dataset_text_field.
trainer = SFTTrainer(
model=self.model,
tokenizer=self.tokenizer,
train_dataset=dataset,
dataset_text_field=self.config["dataset_text_field"],
tokenizer=self.hf_tokenizer,
train_dataset=tokenized_dataset,
dataset_text_field="input_ids", # Dummy field since data is numeric
max_seq_length=self.config["max_seq_length"],
dataset_num_proc=self.config["dataset_num_proc"],
packing=self.config["packing"],
args=TrainingArguments(
per_device_train_batch_size=self.config["per_device_train_batch_size"],
gradient_accumulation_steps=self.config["gradient_accumulation_steps"],
warmup_steps=self.config["warmup_steps"],
num_train_epochs=self.config["num_train_epochs"],
max_steps=self.config["max_steps"],
learning_rate=self.config["learning_rate"],
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
logging_steps=self.config["logging_steps"],
optim=self.config["optim"],
weight_decay=self.config["weight_decay"],
lr_scheduler_type=self.config["lr_scheduler_type"],
seed=self.config["seed"],
output_dir=self.config["output_dir"],
),
dataset_num_proc=1, # Use a single process to avoid pickling issues
packing=False,
args=training_args,
)
from unsloth.chat_templates import train_on_responses_only
trainer = train_on_responses_only(
trainer,
instruction_part="<|start_header_id|>user<|end_header_id|>\n\n",
response_part="<|start_header_id|>assistant<|end_header_id|>\n\n",
)
print("DEBUG: Beginning trainer.train() ...")
trainer.train()
self.model.save_pretrained("lora_model") # Local saving
self.tokenizer.save_pretrained("lora_model")
print("DEBUG: Training complete. Saving model and tokenizer locally...")
self.model.save_pretrained("lora_model")
self.hf_tokenizer.save_pretrained("lora_model")
print("DEBUG: Saved model and tokenizer to 'lora_model'.")
def inference(self, instruction, input_text):
FastLanguageModel.for_inference(self.model)
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.
messages = [{"role": "user", "content": f"{instruction}\n\nInput: {input_text}"}]
inputs = self.hf_tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors="pt"
).to("cuda")
outputs = self.model.generate(
input_ids=inputs,
max_new_tokens=64,
use_cache=True,
temperature=1.5,
min_p=0.1
)
print("DEBUG: Inference output:", self.hf_tokenizer.batch_decode(outputs))
### Instruction:
{}
### Input:
{}
### Response:
{}"""
inputs = self.tokenizer([alpaca_prompt.format(instruction, input_text, "")], return_tensors="pt").to("cuda")
outputs = self.model.generate(**inputs, max_new_tokens=64, use_cache=True)
print(self.tokenizer.batch_decode(outputs))
def load_model(self):
"""Loads the model and tokenizer using the FastLanguageModel library."""
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name=self.config["output_dir"],
@@ -177,33 +290,31 @@ class train:
shutil.rmtree(self.config["hf_model_name"])
self.model.push_to_hub_merged(
self.config["hf_model_name"],
self.tokenizer,
self.hf_tokenizer,
save_method="merged_16bit",
token=os.getenv('HF_TOKEN')
token=os.getenv("HF_TOKEN")
)
def push_model_gguf(self):
self.model.push_to_hub_gguf(
self.config["hf_model_name"],
self.tokenizer,
self.hf_tokenizer,
quantization_method=self.config["quantization_method"],
token=os.getenv('HF_TOKEN')
token=os.getenv("HF_TOKEN")
)
def save_model_gguf(self):
self.model.save_pretrained_gguf(
self.config["hf_model_name"],
self.tokenizer,
self.hf_tokenizer,
quantization_method="q4_k_m"
)
def prepare_modelfile_content(self):
output_model = self.config["hf_model_name"]
gguf_path = f"{output_model}/unsloth.Q4_K_M.gguf"
# Check if the GGUF file exists. If not, generate it ## TODO Multiple Quantisation other than Q4_K_M.gguf
if not os.path.exists(gguf_path):
self.model, self.tokenizer = self.load_model()
self.model, self.hf_tokenizer = self.load_model()
self.save_model_gguf()
return f"""FROM {output_model}/unsloth.Q4_K_M.gguf
@@ -224,9 +335,8 @@ PARAMETER stop "<|reserved_special_token_"
def create_and_push_ollama_model(self):
modelfile_content = self.prepare_modelfile_content()
with open('Modelfile', 'w') as file:
with open("Modelfile", "w") as file:
file.write(modelfile_content)
subprocess.run(["ollama", "serve"])
subprocess.run(["ollama", "create", f"{self.config['ollama_model']}:{self.config['model_parameters']}", "-f", "Modelfile"])
subprocess.run(["ollama", "push", f"{self.config['ollama_model']}:{self.config['model_parameters']}"])
@@ -235,42 +345,30 @@ PARAMETER stop "<|reserved_special_token_"
self.print_system_info()
self.check_gpu()
self.check_ram()
# self.install_packages()
if self.config.get("train", "true").lower() == "true":
self.prepare_model()
self.train_model()
if self.config.get("huggingface_save", "true").lower() == "true":
# self.model, self.tokenizer = self.load_model()
self.save_model_merged()
if self.config.get("huggingface_save_gguf", "true").lower() == "true":
# self.model, self.tokenizer = self.load_model()
self.push_model_gguf()
# if self.config.get("save_gguf", "true").lower() == "true": ## TODO
# self.model, self.tokenizer = self.load_model()
# self.save_model_gguf()
# if self.config.get("save_merged", "true").lower() == "true": ## TODO
# self.model, self.tokenizer = self.load_model()
# self.save_model_merged()
if self.config.get("ollama_save", "true").lower() == "true":
self.create_and_push_ollama_model()
def main():
import argparse
parser = argparse.ArgumentParser(description='PraisonAI Training Script')
parser.add_argument('command', choices=['train'], help='Command to execute')
parser.add_argument('--config', default='config.yaml', help='Path to configuration file')
parser = argparse.ArgumentParser(description="PraisonAI Training Script")
parser.add_argument("command", choices=["train"], help="Command to execute")
parser.add_argument("--config", default="config.yaml", help="Path to configuration file")
parser.add_argument("--model", type=str, help="Model name")
parser.add_argument("--hf", type=str, help="Hugging Face model name")
parser.add_argument("--ollama", type=str, help="Ollama model name")
parser.add_argument("--dataset", type=str, help="Dataset name for training")
args = parser.parse_args()
if args.command == 'train':
ai = train(config_path=args.config)
ai.run()
if args.command == "train":
trainer_obj = TrainModel(config_path=args.config)
trainer_obj.run()
if __name__ == '__main__':
if __name__ == "__main__":
main()
+2 -2
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.0.61"
version = "2.0.64"
description = "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."
readme = "README.md"
license = ""
@@ -84,7 +84,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.7", "crewai"]
[tool.poetry]
name = "PraisonAI"
version = "2.0.61"
version = "2.0.64"
description = "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."
authors = ["Mervin Praison"]
license = ""
Generated
+1 -1
View File
@@ -3060,7 +3060,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.0.61"
version = "2.0.64"
source = { editable = "." }
dependencies = [
{ name = "instructor" },