mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
904e21ca87 | ||
|
|
526665ea11 | ||
|
|
c2e97d2432 | ||
|
|
60be6a1f99 | ||
|
|
0ba7d2e834 | ||
|
|
184b241395 | ||
|
|
4354e76fe5 | ||
|
|
4dd77efb75 | ||
|
|
11db6e18ba | ||
|
|
0d6f698e6c |
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN pip install flask praisonai==2.0.74 gunicorn markdown
|
||||
RUN pip install flask praisonai==2.0.77 gunicorn markdown
|
||||
EXPOSE 8080
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
|
||||
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
|
||||
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.74 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.0.77 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
+1
-1
@@ -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.74.tar.gz"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.77.tar.gz"
|
||||
sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32" # Replace with actual SHA256 checksum
|
||||
license "MIT"
|
||||
|
||||
|
||||
+12
-1
@@ -240,7 +240,18 @@ class PraisonAI:
|
||||
print("All packages installed.")
|
||||
|
||||
train_args = sys.argv[2:] # Get all arguments after 'train'
|
||||
train_script_path = os.path.join(package_root, 'train.py')
|
||||
|
||||
# Check if this is a vision model - handle all case variations
|
||||
model_name = config.get("model_name", "").lower()
|
||||
is_vision_model = any(x in model_name for x in ["-vl-", "-vl", "vl-", "-vision-", "-vision", "vision-", "visionmodel"])
|
||||
|
||||
# Choose appropriate training script
|
||||
if is_vision_model:
|
||||
train_script_path = os.path.join(package_root, 'train_vision.py')
|
||||
print("Using vision training script for VL model...")
|
||||
else:
|
||||
train_script_path = os.path.join(package_root, 'train.py')
|
||||
print("Using standard training script...")
|
||||
|
||||
# Set environment variables
|
||||
env = os.environ.copy()
|
||||
|
||||
+1
-1
@@ -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.74 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.0.77 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ PIP_FULL_PATH=$(conda run -n "$ENV_NAME" which pip)
|
||||
# Install other packages 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@038e6d4c8d40207a87297ab3aaf787c19b1006d1"
|
||||
$PIP_FULL_PATH install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git@53a773e4fbc53a1d96c7ba107e5fe75dab07027b"
|
||||
$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
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This script finetunes a vision language model using Unsloth's fast training framework.
|
||||
It supports vision tasks by converting raw image-caption samples into a conversation format,
|
||||
adding vision-specific LoRA adapters, and training using TRL's SFTTrainer with UnslothVisionDataCollator.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import torch
|
||||
import shutil
|
||||
import subprocess
|
||||
import gc # For garbage collection
|
||||
|
||||
from datasets import load_dataset, concatenate_datasets, Dataset
|
||||
from unsloth import FastVisionModel, is_bf16_supported
|
||||
from unsloth.trainer import UnslothVisionDataCollator
|
||||
from transformers import TrainingArguments
|
||||
from trl import SFTTrainer
|
||||
from tqdm import tqdm # Add progress bar
|
||||
|
||||
|
||||
class TrainVisionModel:
|
||||
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 = None
|
||||
self.hf_tokenizer = None # The underlying tokenizer
|
||||
|
||||
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("DEBUG: PyTorch version:", torch.__version__)
|
||||
print("DEBUG: CUDA version:", torch.version.cuda)
|
||||
if torch.cuda.is_available():
|
||||
print("DEBUG: CUDA Device Capability:", torch.cuda.get_device_capability())
|
||||
else:
|
||||
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"DEBUG: GPU = {gpu_stats.name}. Max memory = {round(gpu_stats.total_memory/(1024**3),3)} GB.")
|
||||
|
||||
def check_ram(self):
|
||||
from psutil import virtual_memory
|
||||
ram_gb = virtual_memory().total / 1e9
|
||||
print(f"DEBUG: Your runtime has {ram_gb:.1f} gigabytes of available RAM")
|
||||
if ram_gb < 20:
|
||||
print("DEBUG: Not using a high-RAM runtime")
|
||||
else:
|
||||
print("DEBUG: You are using a high-RAM runtime!")
|
||||
|
||||
def prepare_model(self):
|
||||
print("DEBUG: Preparing vision model and tokenizer...")
|
||||
self.model, original_tokenizer = FastVisionModel.from_pretrained(
|
||||
model_name=self.config["model_name"],
|
||||
load_in_4bit=self.config["load_in_4bit"],
|
||||
use_gradient_checkpointing="unsloth"
|
||||
)
|
||||
print("DEBUG: Vision model and original tokenizer loaded.")
|
||||
|
||||
# Use the full processor that supports image inputs.
|
||||
self.hf_tokenizer = original_tokenizer
|
||||
|
||||
# Set pad token if needed
|
||||
if not hasattr(self.hf_tokenizer, 'pad_token') or self.hf_tokenizer.pad_token is None:
|
||||
if hasattr(self.hf_tokenizer, 'eos_token'):
|
||||
self.hf_tokenizer.pad_token = self.hf_tokenizer.eos_token
|
||||
elif hasattr(self.hf_tokenizer, 'bos_token'):
|
||||
self.hf_tokenizer.pad_token = self.hf_tokenizer.bos_token
|
||||
|
||||
# Set max length
|
||||
if hasattr(self.hf_tokenizer, 'model_max_length'):
|
||||
self.hf_tokenizer.model_max_length = self.config.get("max_seq_length", 2048)
|
||||
|
||||
# Add vision-specific LoRA adapters
|
||||
self.model = FastVisionModel.get_peft_model(
|
||||
self.model,
|
||||
finetune_vision_layers=self.config.get("finetune_vision_layers", False),
|
||||
finetune_language_layers=self.config.get("finetune_language_layers", True),
|
||||
finetune_attention_modules=self.config.get("finetune_attention_modules", True),
|
||||
finetune_mlp_modules=self.config.get("finetune_mlp_modules", True),
|
||||
r=16,
|
||||
lora_alpha=16,
|
||||
lora_dropout=0,
|
||||
bias="none",
|
||||
random_state=3407,
|
||||
use_rslora=False,
|
||||
loftq_config=None
|
||||
)
|
||||
print("DEBUG: Vision LoRA adapters added.")
|
||||
|
||||
def convert_sample(self, sample):
|
||||
|
||||
instruction = self.config.get(
|
||||
"vision_instruction",
|
||||
"You are an expert radiographer. Describe accurately what you see in this image."
|
||||
)
|
||||
conversation = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": instruction},
|
||||
{"type": "image", "image": sample["image"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{"type": "text", "text": sample["caption"]}
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
return {"messages": conversation}
|
||||
|
||||
def load_datasets(self):
|
||||
all_converted = []
|
||||
for dataset_info in self.config["dataset"]:
|
||||
print("\nDEBUG: Loading vision dataset:", dataset_info)
|
||||
ds = load_dataset(
|
||||
dataset_info["name"],
|
||||
split=dataset_info.get("split_type", "train")
|
||||
)
|
||||
print("DEBUG: Dataset size:", len(ds))
|
||||
print("DEBUG: First raw sample:", ds[0])
|
||||
print("DEBUG: Dataset features:", ds.features)
|
||||
|
||||
print("\nDEBUG: Converting dataset to vision conversation format...")
|
||||
converted_ds = [self.convert_sample(sample) for sample in ds]
|
||||
|
||||
# Debug first converted sample
|
||||
print("\nDEBUG: First converted sample structure:")
|
||||
first = converted_ds[0]
|
||||
print("DEBUG: Message keys:", first["messages"][0]["content"][1].keys())
|
||||
print("DEBUG: Image type in converted:", type(first["messages"][0]["content"][1].get("image")))
|
||||
|
||||
all_converted.extend(converted_ds)
|
||||
|
||||
print("\nDEBUG: Combined vision dataset has", len(all_converted), "examples.")
|
||||
return all_converted
|
||||
|
||||
def train_model(self):
|
||||
print("DEBUG: Starting vision training...")
|
||||
raw_dataset = self.load_datasets()
|
||||
|
||||
# Build training arguments using TrainingArguments
|
||||
training_args = TrainingArguments(
|
||||
per_device_train_batch_size=self.config.get("per_device_train_batch_size", 1),
|
||||
gradient_accumulation_steps=self.config.get("gradient_accumulation_steps", 4),
|
||||
warmup_steps=self.config.get("warmup_steps", 5),
|
||||
max_steps=self.config.get("max_steps", 30),
|
||||
learning_rate=self.config.get("learning_rate", 2e-4),
|
||||
fp16=self.config.get("fp16", not is_bf16_supported()),
|
||||
bf16=self.config.get("bf16", is_bf16_supported()),
|
||||
logging_steps=self.config.get("logging_steps", 1),
|
||||
optim=self.config.get("optim", "adamw_8bit"),
|
||||
weight_decay=self.config.get("weight_decay", 0.01),
|
||||
lr_scheduler_type=self.config.get("lr_scheduler_type", "linear"),
|
||||
seed=self.config.get("seed", 3407),
|
||||
output_dir=self.config.get("output_dir", "outputs"),
|
||||
report_to="none" if not os.getenv("PRAISON_WANDB") else "wandb",
|
||||
remove_unused_columns=False,
|
||||
# Add memory optimization settings
|
||||
gradient_checkpointing=True,
|
||||
max_grad_norm=1.0,
|
||||
)
|
||||
|
||||
trainer = SFTTrainer(
|
||||
model=self.model,
|
||||
tokenizer=self.hf_tokenizer,
|
||||
data_collator=UnslothVisionDataCollator(self.model, self.hf_tokenizer),
|
||||
train_dataset=raw_dataset,
|
||||
args=training_args,
|
||||
max_seq_length=self.config.get("max_seq_length", 2048),
|
||||
dataset_text_field="", # Required for vision training
|
||||
dataset_kwargs={"skip_prepare_dataset": True}, # Required for vision training
|
||||
packing=False # Explicitly set packing to False
|
||||
)
|
||||
print("DEBUG: Beginning vision trainer.train() ...")
|
||||
trainer.train()
|
||||
print("DEBUG: Vision training complete. Saving model and tokenizer locally...")
|
||||
self.model.save_pretrained("lora_vision_model")
|
||||
self.hf_tokenizer.save_pretrained("lora_vision_model")
|
||||
print("DEBUG: Saved vision model and tokenizer to 'lora_vision_model'.")
|
||||
|
||||
def vision_inference(self, instruction, image):
|
||||
FastVisionModel.for_inference(self.model)
|
||||
messages = [
|
||||
{"role": "user", "content": [
|
||||
{"type": "image"},
|
||||
{"type": "text", "text": instruction}
|
||||
]}
|
||||
]
|
||||
input_text = self.hf_tokenizer.apply_chat_template(messages, add_generation_prompt=True)
|
||||
inputs = self.hf_tokenizer(
|
||||
image,
|
||||
input_text,
|
||||
add_special_tokens=False,
|
||||
return_tensors="pt"
|
||||
).to("cuda")
|
||||
outputs = self.model.generate(
|
||||
**inputs,
|
||||
max_new_tokens=128,
|
||||
use_cache=True,
|
||||
temperature=1.5,
|
||||
min_p=0.1
|
||||
)
|
||||
print("DEBUG: Vision inference output:", self.hf_tokenizer.batch_decode(outputs))
|
||||
|
||||
def save_model_merged(self):
|
||||
if os.path.exists(self.config["hf_model_name"]):
|
||||
shutil.rmtree(self.config["hf_model_name"])
|
||||
self.model.push_to_hub_merged(
|
||||
self.config["hf_model_name"],
|
||||
self.hf_tokenizer,
|
||||
save_method="merged_16bit",
|
||||
token=os.getenv("HF_TOKEN")
|
||||
)
|
||||
|
||||
def push_model_gguf(self):
|
||||
self.model.push_to_hub_gguf(
|
||||
self.config["hf_model_name"],
|
||||
self.hf_tokenizer,
|
||||
quantization_method=self.config.get("quantization_method", "q4_k_m"),
|
||||
token=os.getenv("HF_TOKEN")
|
||||
)
|
||||
|
||||
def save_model_gguf(self):
|
||||
self.model.save_pretrained_gguf(
|
||||
self.config["hf_model_name"],
|
||||
self.hf_tokenizer,
|
||||
quantization_method="q4_k_m"
|
||||
)
|
||||
|
||||
def prepare_modelfile_content(self):
|
||||
output_model = self.config["hf_model_name"]
|
||||
|
||||
template = '''{{- range $index, $_ := .Messages }}<|start_header_id|>{{ .Role }}<|end_header_id|>
|
||||
|
||||
{{ .Content }}
|
||||
{{- if gt (len (slice $.Messages $index)) 1 }}<|eot_id|>
|
||||
{{- else if ne .Role "assistant" }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
|
||||
|
||||
{{ end }}
|
||||
{{- end }}'''
|
||||
|
||||
return f"""FROM {output_model}
|
||||
TEMPLATE {template}
|
||||
PARAMETER temperature 0.6
|
||||
PARAMETER top_p 0.9
|
||||
"""
|
||||
|
||||
def create_and_push_ollama_model(self):
|
||||
modelfile_content = self.prepare_modelfile_content()
|
||||
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']}"])
|
||||
|
||||
def run(self):
|
||||
self.print_system_info()
|
||||
self.check_gpu()
|
||||
self.check_ram()
|
||||
if self.config.get("train", "true").lower() == "true":
|
||||
self.prepare_model()
|
||||
self.train_model()
|
||||
if self.config.get("huggingface_save", "true").lower() == "true":
|
||||
self.save_model_merged()
|
||||
if self.config.get("huggingface_save_gguf", "true").lower() == "true":
|
||||
self.push_model_gguf()
|
||||
if self.config.get("ollama_save", "true").lower() == "true":
|
||||
self.create_and_push_ollama_model()
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="PraisonAI Vision Training Script")
|
||||
parser.add_argument("command", choices=["train", "inference"], help="Command to execute")
|
||||
parser.add_argument("--config", default="config.yaml", help="Path to configuration file")
|
||||
args = parser.parse_args()
|
||||
|
||||
trainer_obj = TrainVisionModel(config_path=args.config)
|
||||
if args.command == "train":
|
||||
trainer_obj.run()
|
||||
elif args.command == "inference":
|
||||
# For inference, we load a sample image from the first dataset
|
||||
instr = trainer_obj.config.get("vision_instruction", "You are an expert radiographer. Describe accurately what you see in this image.")
|
||||
ds_info = trainer_obj.config["dataset"][0]
|
||||
ds = load_dataset(ds_info["name"], split=ds_info.get("split_type", "train"))
|
||||
sample_image = ds[0]["image"]
|
||||
if trainer_obj.model is None or trainer_obj.hf_tokenizer is None:
|
||||
trainer_obj.prepare_model()
|
||||
trainer_obj.vision_inference(instr, sample_image)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This script handles uploading trained vision models to Hugging Face and Ollama.
|
||||
It reads configuration from config.yaml and provides options to upload in different formats.
|
||||
"""
|
||||
|
||||
import os
|
||||
import yaml
|
||||
import torch
|
||||
import shutil
|
||||
import subprocess
|
||||
from unsloth import FastVisionModel
|
||||
|
||||
class UploadVisionModel:
|
||||
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 = None
|
||||
self.hf_tokenizer = None
|
||||
|
||||
def load_config(self, path):
|
||||
"""Load configuration from yaml file."""
|
||||
with open(path, "r") as file:
|
||||
self.config = yaml.safe_load(file)
|
||||
print("DEBUG: Loaded config:", self.config)
|
||||
|
||||
def prepare_model(self):
|
||||
"""Load the trained model for uploading."""
|
||||
print("DEBUG: Loading trained model and tokenizer...")
|
||||
self.model, original_tokenizer = FastVisionModel.from_pretrained(
|
||||
model_name=self.config.get("output_dir", "lora_model"),
|
||||
load_in_4bit=self.config.get("load_in_4bit", True)
|
||||
)
|
||||
self.hf_tokenizer = original_tokenizer
|
||||
print("DEBUG: Model and tokenizer loaded successfully.")
|
||||
|
||||
def save_model_merged(self):
|
||||
"""Save merged model to Hugging Face Hub."""
|
||||
print(f"DEBUG: Saving merged model to Hugging Face Hub: {self.config['hf_model_name']}")
|
||||
if os.path.exists(self.config["hf_model_name"]):
|
||||
shutil.rmtree(self.config["hf_model_name"])
|
||||
self.model.push_to_hub_merged(
|
||||
self.config["hf_model_name"],
|
||||
self.hf_tokenizer,
|
||||
save_method="merged_16bit",
|
||||
token=os.getenv("HF_TOKEN")
|
||||
)
|
||||
print("DEBUG: Model saved to Hugging Face Hub successfully.")
|
||||
|
||||
def push_model_gguf(self):
|
||||
"""Push model in GGUF format to Hugging Face Hub."""
|
||||
print(f"DEBUG: Pushing GGUF model to Hugging Face Hub: {self.config['hf_model_name']}")
|
||||
self.model.push_to_hub_gguf(
|
||||
self.config["hf_model_name"],
|
||||
self.hf_tokenizer,
|
||||
quantization_method=self.config.get("quantization_method", "q4_k_m"),
|
||||
token=os.getenv("HF_TOKEN")
|
||||
)
|
||||
print("DEBUG: GGUF model pushed to Hugging Face Hub successfully.")
|
||||
|
||||
def prepare_modelfile_content(self):
|
||||
"""Prepare Ollama modelfile content using Llama 3.2 vision template."""
|
||||
output_model = self.config["hf_model_name"]
|
||||
|
||||
# Using Llama 3.2 vision template format
|
||||
template = """{{- range $index, $_ := .Messages }}<|start_header_id|>{{ .Role }}<|end_header_id|>
|
||||
|
||||
{{ .Content }}
|
||||
{{- if gt (len (slice $.Messages $index)) 1 }}<|eot_id|>
|
||||
{{- else if ne .Role "assistant" }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
|
||||
|
||||
{{ end }}
|
||||
{{- end }}"""
|
||||
|
||||
# Assemble the modelfile content with Llama 3.2 vision parameters
|
||||
modelfile = f"FROM {output_model}\n"
|
||||
modelfile += "TEMPLATE \"""" + template + "\"""\n"
|
||||
modelfile += "PARAMETER temperature 0.6\n"
|
||||
modelfile += "PARAMETER top_p 0.9\n"
|
||||
return modelfile
|
||||
|
||||
def create_and_push_ollama_model(self):
|
||||
"""Create and push model to Ollama."""
|
||||
print(f"DEBUG: Creating Ollama model: {self.config['ollama_model']}:{self.config['model_parameters']}")
|
||||
modelfile_content = self.prepare_modelfile_content()
|
||||
with open("Modelfile", "w") as file:
|
||||
file.write(modelfile_content)
|
||||
|
||||
print("DEBUG: Starting Ollama server...")
|
||||
subprocess.run(["ollama", "serve"])
|
||||
|
||||
print("DEBUG: Creating Ollama model...")
|
||||
subprocess.run([
|
||||
"ollama", "create",
|
||||
f"{self.config['ollama_model']}:{self.config['model_parameters']}",
|
||||
"-f", "Modelfile"
|
||||
])
|
||||
|
||||
print("DEBUG: Pushing model to Ollama...")
|
||||
subprocess.run([
|
||||
"ollama", "push",
|
||||
f"{self.config['ollama_model']}:{self.config['model_parameters']}"
|
||||
])
|
||||
print("DEBUG: Model pushed to Ollama successfully.")
|
||||
|
||||
def upload(self, target="all"):
|
||||
"""
|
||||
Upload the model to specified targets.
|
||||
Args:
|
||||
target (str): One of 'all', 'huggingface', 'huggingface_gguf', or 'ollama'
|
||||
"""
|
||||
self.prepare_model()
|
||||
|
||||
if target in ["all", "huggingface"]:
|
||||
self.save_model_merged()
|
||||
|
||||
if target in ["all", "huggingface_gguf"]:
|
||||
self.push_model_gguf()
|
||||
|
||||
if target in ["all", "ollama"]:
|
||||
self.create_and_push_ollama_model()
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Upload Vision Model to Various Platforms")
|
||||
parser.add_argument("--config", default="config.yaml", help="Path to configuration file")
|
||||
parser.add_argument(
|
||||
"--target",
|
||||
choices=["all", "huggingface", "huggingface_gguf", "ollama"],
|
||||
default="all",
|
||||
help="Target platform to upload to"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
uploader = UploadVisionModel(config_path=args.config)
|
||||
uploader.upload(target=args.target)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "PraisonAI"
|
||||
version = "2.0.74"
|
||||
version = "2.0.77"
|
||||
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 = ""
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.0.61",
|
||||
"praisonaiagents>=0.0.62",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
@@ -84,7 +84,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.7", "crewai"]
|
||||
|
||||
[tool.poetry]
|
||||
name = "PraisonAI"
|
||||
version = "2.0.74"
|
||||
version = "2.0.77"
|
||||
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 = ""
|
||||
@@ -102,7 +102,7 @@ python = ">=3.10,<3.13"
|
||||
rich = ">=13.7"
|
||||
markdown = ">=3.5"
|
||||
pyparsing = ">=3.0.0"
|
||||
praisonaiagents = ">=0.0.61"
|
||||
praisonaiagents = ">=0.0.62"
|
||||
python-dotenv = ">=0.19.0"
|
||||
instructor = ">=1.3.3"
|
||||
PyYAML = ">=6.0"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language
|
||||
|
||||
agent1 = Agent(
|
||||
instructions="You are a Wikipedia Agent",
|
||||
tools=[wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language],
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=10
|
||||
)
|
||||
agent1.start("history of AI in 1 line")
|
||||
|
||||
agent2 = Agent(
|
||||
instructions="You are a Wikipedia Agent",
|
||||
tools=[wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language],
|
||||
llm="gpt-4o-mini",
|
||||
verbose=10
|
||||
)
|
||||
agent2.start("history of AI in 1 line")
|
||||
@@ -714,6 +714,22 @@ Your Goal: {self.goal}
|
||||
return None
|
||||
|
||||
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False):
|
||||
# Log all parameter values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
param_info = {
|
||||
"prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt),
|
||||
"temperature": temperature,
|
||||
"tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None,
|
||||
"output_json": str(output_json.__class__.__name__) if output_json else None,
|
||||
"output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None,
|
||||
"reasoning_steps": reasoning_steps,
|
||||
"agent_name": self.name,
|
||||
"agent_role": self.role,
|
||||
"agent_goal": self.goal
|
||||
}
|
||||
logging.debug(f"Agent.chat parameters: {json.dumps(param_info, indent=2, default=str)}")
|
||||
|
||||
start_time = time.time()
|
||||
reasoning_steps = reasoning_steps or self.reasoning_steps
|
||||
# Search for existing knowledge if any knowledge is provided
|
||||
if self.knowledge:
|
||||
@@ -738,7 +754,7 @@ Your Goal: {self.goal}
|
||||
system_prompt=f"{self.backstory}\n\nYour Role: {self.role}\n\nYour Goal: {self.goal}" if self.use_system_prompt else None,
|
||||
chat_history=self.chat_history,
|
||||
temperature=temperature,
|
||||
tools=tools,
|
||||
tools=self.tools if tools is None else tools,
|
||||
output_json=output_json,
|
||||
output_pydantic=output_pydantic,
|
||||
verbose=self.verbose,
|
||||
@@ -749,7 +765,7 @@ Your Goal: {self.goal}
|
||||
console=self.console,
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools],
|
||||
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools if tools is not None else self.tools)],
|
||||
execute_tool_fn=self.execute_tool, # Pass tool execution function
|
||||
reasoning_steps=reasoning_steps
|
||||
)
|
||||
@@ -757,6 +773,11 @@ Your Goal: {self.goal}
|
||||
self.chat_history.append({"role": "user", "content": prompt})
|
||||
self.chat_history.append({"role": "assistant", "content": response_text})
|
||||
|
||||
# Log completion time if in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.chat completed in {total_time:.2f} seconds")
|
||||
|
||||
return response_text
|
||||
except Exception as e:
|
||||
display_error(f"Error in LLM chat: {e}")
|
||||
@@ -944,6 +965,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
display_error(f"Error in chat: {e}", console=self.console)
|
||||
return None
|
||||
|
||||
# Log completion time if in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.chat completed in {total_time:.2f} seconds")
|
||||
|
||||
return response_text
|
||||
|
||||
def clean_json_output(self, output: str) -> str:
|
||||
"""Clean and extract JSON from response text."""
|
||||
cleaned = output.strip()
|
||||
@@ -958,6 +986,22 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
|
||||
async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False):
|
||||
"""Async version of chat method. TODO: Requires Syncing with chat method."""
|
||||
# Log all parameter values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
param_info = {
|
||||
"prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt),
|
||||
"temperature": temperature,
|
||||
"tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None,
|
||||
"output_json": str(output_json.__class__.__name__) if output_json else None,
|
||||
"output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None,
|
||||
"reasoning_steps": reasoning_steps,
|
||||
"agent_name": self.name,
|
||||
"agent_role": self.role,
|
||||
"agent_goal": self.goal
|
||||
}
|
||||
logging.debug(f"Agent.achat parameters: {json.dumps(param_info, indent=2, default=str)}")
|
||||
|
||||
start_time = time.time()
|
||||
reasoning_steps = reasoning_steps or self.reasoning_steps
|
||||
try:
|
||||
# Search for existing knowledge if any knowledge is provided
|
||||
@@ -996,9 +1040,15 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
self.chat_history.append({"role": "user", "content": prompt})
|
||||
self.chat_history.append({"role": "assistant", "content": response_text})
|
||||
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.achat completed in {total_time:.2f} seconds")
|
||||
return response_text
|
||||
except Exception as e:
|
||||
display_error(f"Error in LLM chat: {e}")
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.achat failed in {total_time:.2f} seconds: {str(e)}")
|
||||
return None
|
||||
|
||||
# For OpenAI client
|
||||
@@ -1081,7 +1131,11 @@ Your Goal: {self.goal}
|
||||
temperature=temperature,
|
||||
tools=formatted_tools
|
||||
)
|
||||
return await self._achat_completion(response, tools)
|
||||
result = await self._achat_completion(response, tools)
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.achat completed in {total_time:.2f} seconds")
|
||||
return result
|
||||
elif output_json or output_pydantic:
|
||||
response = await async_client.chat.completions.create(
|
||||
model=self.llm,
|
||||
@@ -1090,6 +1144,9 @@ Your Goal: {self.goal}
|
||||
response_format={"type": "json_object"}
|
||||
)
|
||||
# Return the raw response
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.achat completed in {total_time:.2f} seconds")
|
||||
return response.choices[0].message.content
|
||||
else:
|
||||
response = await async_client.chat.completions.create(
|
||||
@@ -1097,12 +1154,21 @@ Your Goal: {self.goal}
|
||||
messages=messages,
|
||||
temperature=temperature
|
||||
)
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.achat completed in {total_time:.2f} seconds")
|
||||
return response.choices[0].message.content
|
||||
except Exception as e:
|
||||
display_error(f"Error in chat completion: {e}")
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.achat failed in {total_time:.2f} seconds: {str(e)}")
|
||||
return None
|
||||
except Exception as e:
|
||||
display_error(f"Error in achat: {e}")
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"Agent.achat failed in {total_time:.2f} seconds: {str(e)}")
|
||||
return None
|
||||
|
||||
async def _achat_completion(self, response, tools, reasoning_steps=False):
|
||||
|
||||
@@ -172,6 +172,36 @@ class LLM:
|
||||
# Enable error dropping for cleaner output
|
||||
litellm.drop_params = True
|
||||
self._setup_event_tracking(events)
|
||||
|
||||
# Log all initialization parameters when in debug mode
|
||||
if not isinstance(verbose, bool) and verbose >= 10:
|
||||
debug_info = {
|
||||
"model": self.model,
|
||||
"timeout": self.timeout,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"n": self.n,
|
||||
"max_tokens": self.max_tokens,
|
||||
"presence_penalty": self.presence_penalty,
|
||||
"frequency_penalty": self.frequency_penalty,
|
||||
"logit_bias": self.logit_bias,
|
||||
"response_format": self.response_format,
|
||||
"seed": self.seed,
|
||||
"logprobs": self.logprobs,
|
||||
"top_logprobs": self.top_logprobs,
|
||||
"api_version": self.api_version,
|
||||
"stop_phrases": self.stop_phrases,
|
||||
"api_key": "***" if self.api_key else None, # Mask API key for security
|
||||
"base_url": self.base_url,
|
||||
"verbose": self.verbose,
|
||||
"markdown": self.markdown,
|
||||
"self_reflect": self.self_reflect,
|
||||
"max_reflect": self.max_reflect,
|
||||
"min_reflect": self.min_reflect,
|
||||
"reasoning_steps": self.reasoning_steps,
|
||||
"extra_settings": {k: v for k, v in self.extra_settings.items() if k not in ["api_key"]}
|
||||
}
|
||||
logging.debug(f"LLM instance initialized with: {json.dumps(debug_info, indent=2, default=str)}")
|
||||
|
||||
def get_response(
|
||||
self,
|
||||
@@ -195,6 +225,56 @@ class LLM:
|
||||
**kwargs
|
||||
) -> str:
|
||||
"""Enhanced get_response with all OpenAI-like features"""
|
||||
logging.info(f"Getting response from {self.model}")
|
||||
# Log all self values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
debug_info = {
|
||||
"model": self.model,
|
||||
"timeout": self.timeout,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"n": self.n,
|
||||
"max_tokens": self.max_tokens,
|
||||
"presence_penalty": self.presence_penalty,
|
||||
"frequency_penalty": self.frequency_penalty,
|
||||
"logit_bias": self.logit_bias,
|
||||
"response_format": self.response_format,
|
||||
"seed": self.seed,
|
||||
"logprobs": self.logprobs,
|
||||
"top_logprobs": self.top_logprobs,
|
||||
"api_version": self.api_version,
|
||||
"stop_phrases": self.stop_phrases,
|
||||
"api_key": "***" if self.api_key else None, # Mask API key for security
|
||||
"base_url": self.base_url,
|
||||
"verbose": self.verbose,
|
||||
"markdown": self.markdown,
|
||||
"self_reflect": self.self_reflect,
|
||||
"max_reflect": self.max_reflect,
|
||||
"min_reflect": self.min_reflect,
|
||||
"reasoning_steps": self.reasoning_steps
|
||||
}
|
||||
logging.debug(f"LLM instance configuration: {json.dumps(debug_info, indent=2, default=str)}")
|
||||
|
||||
# Log the parameter values passed to get_response
|
||||
param_info = {
|
||||
"prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt),
|
||||
"system_prompt": system_prompt[:100] + "..." if system_prompt and len(system_prompt) > 100 else system_prompt,
|
||||
"chat_history": f"[{len(chat_history)} messages]" if chat_history else None,
|
||||
"temperature": temperature,
|
||||
"tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None,
|
||||
"output_json": str(output_json.__class__.__name__) if output_json else None,
|
||||
"output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None,
|
||||
"verbose": verbose,
|
||||
"markdown": markdown,
|
||||
"self_reflect": self_reflect,
|
||||
"max_reflect": max_reflect,
|
||||
"min_reflect": min_reflect,
|
||||
"agent_name": agent_name,
|
||||
"agent_role": agent_role,
|
||||
"agent_tools": agent_tools,
|
||||
"kwargs": str(kwargs)
|
||||
}
|
||||
logging.debug(f"get_response parameters: {json.dumps(param_info, indent=2, default=str)}")
|
||||
try:
|
||||
import litellm
|
||||
# This below **kwargs** is passed to .completion() directly. so reasoning_steps has to be popped. OR find alternate best way of handling this.
|
||||
@@ -202,6 +282,23 @@ class LLM:
|
||||
# Disable litellm debug messages
|
||||
litellm.set_verbose = False
|
||||
|
||||
# Format tools if provided
|
||||
formatted_tools = None
|
||||
if tools:
|
||||
formatted_tools = []
|
||||
for tool in tools:
|
||||
if callable(tool):
|
||||
tool_def = self._generate_tool_definition(tool.__name__)
|
||||
elif isinstance(tool, str):
|
||||
tool_def = self._generate_tool_definition(tool)
|
||||
else:
|
||||
continue
|
||||
|
||||
if tool_def:
|
||||
formatted_tools.append(tool_def)
|
||||
if not formatted_tools:
|
||||
formatted_tools = None
|
||||
|
||||
# Build messages list
|
||||
messages = []
|
||||
if system_prompt:
|
||||
@@ -260,6 +357,7 @@ class LLM:
|
||||
messages=messages,
|
||||
temperature=temperature,
|
||||
stream=False, # force non-streaming
|
||||
tools=formatted_tools,
|
||||
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
|
||||
)
|
||||
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
|
||||
@@ -291,6 +389,7 @@ class LLM:
|
||||
for chunk in litellm.completion(
|
||||
model=self.model,
|
||||
messages=messages,
|
||||
tools=formatted_tools,
|
||||
temperature=temperature,
|
||||
stream=True,
|
||||
**kwargs
|
||||
@@ -305,6 +404,7 @@ class LLM:
|
||||
for chunk in litellm.completion(
|
||||
model=self.model,
|
||||
messages=messages,
|
||||
tools=formatted_tools,
|
||||
temperature=temperature,
|
||||
stream=True,
|
||||
**kwargs
|
||||
@@ -318,6 +418,7 @@ class LLM:
|
||||
final_response = litellm.completion(
|
||||
model=self.model,
|
||||
messages=messages,
|
||||
tools=formatted_tools,
|
||||
temperature=temperature,
|
||||
stream=False, # No streaming for tool call check
|
||||
**kwargs
|
||||
@@ -552,6 +653,11 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
except Exception as error:
|
||||
display_error(f"Error in get_response: {str(error)}")
|
||||
raise
|
||||
|
||||
# Log completion time if in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"get_response completed in {total_time:.2f} seconds")
|
||||
|
||||
async def get_response_async(
|
||||
self,
|
||||
@@ -577,6 +683,56 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
"""Async version of get_response with identical functionality."""
|
||||
try:
|
||||
import litellm
|
||||
logging.info(f"Getting async response from {self.model}")
|
||||
# Log all self values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
debug_info = {
|
||||
"model": self.model,
|
||||
"timeout": self.timeout,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"n": self.n,
|
||||
"max_tokens": self.max_tokens,
|
||||
"presence_penalty": self.presence_penalty,
|
||||
"frequency_penalty": self.frequency_penalty,
|
||||
"logit_bias": self.logit_bias,
|
||||
"response_format": self.response_format,
|
||||
"seed": self.seed,
|
||||
"logprobs": self.logprobs,
|
||||
"top_logprobs": self.top_logprobs,
|
||||
"api_version": self.api_version,
|
||||
"stop_phrases": self.stop_phrases,
|
||||
"api_key": "***" if self.api_key else None, # Mask API key for security
|
||||
"base_url": self.base_url,
|
||||
"verbose": self.verbose,
|
||||
"markdown": self.markdown,
|
||||
"self_reflect": self.self_reflect,
|
||||
"max_reflect": self.max_reflect,
|
||||
"min_reflect": self.min_reflect,
|
||||
"reasoning_steps": self.reasoning_steps
|
||||
}
|
||||
logging.debug(f"LLM async instance configuration: {json.dumps(debug_info, indent=2, default=str)}")
|
||||
|
||||
# Log the parameter values passed to get_response_async
|
||||
param_info = {
|
||||
"prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt),
|
||||
"system_prompt": system_prompt[:100] + "..." if system_prompt and len(system_prompt) > 100 else system_prompt,
|
||||
"chat_history": f"[{len(chat_history)} messages]" if chat_history else None,
|
||||
"temperature": temperature,
|
||||
"tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None,
|
||||
"output_json": str(output_json.__class__.__name__) if output_json else None,
|
||||
"output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None,
|
||||
"verbose": verbose,
|
||||
"markdown": markdown,
|
||||
"self_reflect": self_reflect,
|
||||
"max_reflect": max_reflect,
|
||||
"min_reflect": min_reflect,
|
||||
"agent_name": agent_name,
|
||||
"agent_role": agent_role,
|
||||
"agent_tools": agent_tools,
|
||||
"kwargs": str(kwargs)
|
||||
}
|
||||
logging.debug(f"get_response_async parameters: {json.dumps(param_info, indent=2, default=str)}")
|
||||
reasoning_steps = kwargs.pop('reasoning_steps', self.reasoning_steps)
|
||||
litellm.set_verbose = False
|
||||
|
||||
@@ -983,6 +1139,11 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
raise LLMContextLengthExceededException(str(error))
|
||||
display_error(f"Error in get_response_async: {str(error)}")
|
||||
raise
|
||||
|
||||
# Log completion time if in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(f"get_response_async completed in {total_time:.2f} seconds")
|
||||
|
||||
def can_use_tools(self) -> bool:
|
||||
"""Check if this model can use tool functions"""
|
||||
@@ -1065,6 +1226,24 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
|
||||
logger.debug("Using synchronous response function")
|
||||
|
||||
# Log all self values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
debug_info = {
|
||||
"model": self.model,
|
||||
"timeout": self.timeout,
|
||||
"temperature": temperature,
|
||||
"top_p": self.top_p,
|
||||
"n": self.n,
|
||||
"max_tokens": self.max_tokens,
|
||||
"presence_penalty": self.presence_penalty,
|
||||
"frequency_penalty": self.frequency_penalty,
|
||||
"stream": stream,
|
||||
"verbose": verbose,
|
||||
"markdown": markdown,
|
||||
"kwargs": str(kwargs)
|
||||
}
|
||||
logger.debug(f"Response method configuration: {json.dumps(debug_info, indent=2, default=str)}")
|
||||
|
||||
# Build messages list
|
||||
messages = []
|
||||
if system_prompt:
|
||||
@@ -1150,6 +1329,24 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
|
||||
logger.debug("Using asynchronous response function")
|
||||
|
||||
# Log all self values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
debug_info = {
|
||||
"model": self.model,
|
||||
"timeout": self.timeout,
|
||||
"temperature": temperature,
|
||||
"top_p": self.top_p,
|
||||
"n": self.n,
|
||||
"max_tokens": self.max_tokens,
|
||||
"presence_penalty": self.presence_penalty,
|
||||
"frequency_penalty": self.frequency_penalty,
|
||||
"stream": stream,
|
||||
"verbose": verbose,
|
||||
"markdown": markdown,
|
||||
"kwargs": str(kwargs)
|
||||
}
|
||||
logger.debug(f"Async response method configuration: {json.dumps(debug_info, indent=2, default=str)}")
|
||||
|
||||
# Build messages list
|
||||
messages = []
|
||||
if system_prompt:
|
||||
@@ -1210,4 +1407,117 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
|
||||
except Exception as error:
|
||||
display_error(f"Error in response_async: {str(error)}")
|
||||
raise
|
||||
raise
|
||||
|
||||
def _generate_tool_definition(self, function_name: str) -> Optional[Dict]:
|
||||
"""Generate a tool definition from a function name."""
|
||||
logging.debug(f"Attempting to generate tool definition for: {function_name}")
|
||||
|
||||
# First try to get the tool definition if it exists
|
||||
tool_def_name = f"{function_name}_definition"
|
||||
tool_def = globals().get(tool_def_name)
|
||||
logging.debug(f"Looking for {tool_def_name} in globals: {tool_def is not None}")
|
||||
|
||||
if not tool_def:
|
||||
import __main__
|
||||
tool_def = getattr(__main__, tool_def_name, None)
|
||||
logging.debug(f"Looking for {tool_def_name} in __main__: {tool_def is not None}")
|
||||
|
||||
if tool_def:
|
||||
logging.debug(f"Found tool definition: {tool_def}")
|
||||
return tool_def
|
||||
|
||||
# Try to find the function
|
||||
func = globals().get(function_name)
|
||||
logging.debug(f"Looking for {function_name} in globals: {func is not None}")
|
||||
|
||||
if not func:
|
||||
import __main__
|
||||
func = getattr(__main__, function_name, None)
|
||||
logging.debug(f"Looking for {function_name} in __main__: {func is not None}")
|
||||
|
||||
if not func or not callable(func):
|
||||
logging.debug(f"Function {function_name} not found or not callable")
|
||||
return None
|
||||
|
||||
import inspect
|
||||
# Handle Langchain and CrewAI tools
|
||||
if inspect.isclass(func) and hasattr(func, 'run') and not hasattr(func, '_run'):
|
||||
original_func = func
|
||||
func = func.run
|
||||
function_name = original_func.__name__
|
||||
elif inspect.isclass(func) and hasattr(func, '_run'):
|
||||
original_func = func
|
||||
func = func._run
|
||||
function_name = original_func.__name__
|
||||
|
||||
sig = inspect.signature(func)
|
||||
logging.debug(f"Function signature: {sig}")
|
||||
|
||||
# Skip self, *args, **kwargs
|
||||
parameters_list = []
|
||||
for name, param in sig.parameters.items():
|
||||
if name == "self":
|
||||
continue
|
||||
if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
|
||||
continue
|
||||
parameters_list.append((name, param))
|
||||
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
|
||||
# Parse docstring for parameter descriptions
|
||||
docstring = inspect.getdoc(func)
|
||||
logging.debug(f"Function docstring: {docstring}")
|
||||
|
||||
param_descriptions = {}
|
||||
if docstring:
|
||||
import re
|
||||
param_section = re.split(r'\s*Args:\s*', docstring)
|
||||
logging.debug(f"Param section split: {param_section}")
|
||||
if len(param_section) > 1:
|
||||
param_lines = param_section[1].split('\n')
|
||||
for line in param_lines:
|
||||
line = line.strip()
|
||||
if line and ':' in line:
|
||||
param_name, param_desc = line.split(':', 1)
|
||||
param_descriptions[param_name.strip()] = param_desc.strip()
|
||||
|
||||
logging.debug(f"Parameter descriptions: {param_descriptions}")
|
||||
|
||||
for name, param in parameters_list:
|
||||
param_type = "string" # Default type
|
||||
if param.annotation != inspect.Parameter.empty:
|
||||
if param.annotation == int:
|
||||
param_type = "integer"
|
||||
elif param.annotation == float:
|
||||
param_type = "number"
|
||||
elif param.annotation == bool:
|
||||
param_type = "boolean"
|
||||
elif param.annotation == list:
|
||||
param_type = "array"
|
||||
elif param.annotation == dict:
|
||||
param_type = "object"
|
||||
|
||||
parameters["properties"][name] = {
|
||||
"type": param_type,
|
||||
"description": param_descriptions.get(name, "Parameter description not available")
|
||||
}
|
||||
|
||||
if param.default == inspect.Parameter.empty:
|
||||
parameters["required"].append(name)
|
||||
|
||||
logging.debug(f"Generated parameters: {parameters}")
|
||||
tool_def = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": function_name,
|
||||
"description": docstring.split('\n\n')[0] if docstring else "No description available",
|
||||
"parameters": parameters
|
||||
}
|
||||
}
|
||||
logging.debug(f"Generated tool definition: {tool_def}")
|
||||
return tool_def
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.61"
|
||||
version = "0.0.62"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
authors = [
|
||||
{ name="Mervin Praison" }
|
||||
|
||||
Generated
+1
-1
@@ -1868,7 +1868,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.61"
|
||||
version = "0.0.62"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "openai" },
|
||||
|
||||
@@ -3060,7 +3060,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonai"
|
||||
version = "2.0.74"
|
||||
version = "2.0.77"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "instructor" },
|
||||
@@ -3197,7 +3197,7 @@ requires-dist = [
|
||||
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.7" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.7" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.61" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.62" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -3250,16 +3250,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.61"
|
||||
version = "0.0.62"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "openai" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/be/09af17ea9eee5135c7b091bca77e1fc7a8ff732a6148497276ff5a06bd2e/praisonaiagents-0.0.61.tar.gz", hash = "sha256:9e178b99a1d2ffe23acd8413810e5d937d910a72fbd851e04d10b0bcb129b48a", size = 102057 }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/26/44/528286f187d8be35839382251407a3b5ec7ecf5fc9b1fafd7e4197b436cb/praisonaiagents-0.0.62.tar.gz", hash = "sha256:d461553d7dc64441544a6ad92e6e446531f1be8e5cbb95261407540756b2cb8a", size = 104636 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/1b/5656b79ae6c9f4c4e9e7b22db6800841c3bbab7fbd73c6c7f3381c936e64/praisonaiagents-0.0.61-py3-none-any.whl", hash = "sha256:835ec8a6fb1b2805a150db3d38bd4a1fe88333f078ee00190b102a3a4b2687a6", size = 121044 },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/34/c389ba1a9a45b0f7637b073e25717d2f9e0e6e727cc4946182fcf86e8c12/praisonaiagents-0.0.62-py3-none-any.whl", hash = "sha256:f6883ba12642bb3a7af277bfc1c0020276c3ac31bc251ed381f0388b3a61e73b", size = 123702 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user