Compare commits

...
5 Commits
Author SHA1 Message Date
MervinPraison 911e991f84 chore: bump praisonai and praisonaiagents package versions 2025-06-28 09:44:34 +01:00
Mervin Praison 8164dfd1cf Merge pull request #708 from MervinPraison/claude/issue-482-20250628_070041
fix: Add base_url and api_key parameters to Agent class for Ollama support
2025-06-28 09:37:36 +01:00
Mervin Praison 461bac900d Merge pull request #707 from MervinPraison/claude/issue-67-20250628_070140
feat: add --file/-f flag for file input support
2025-06-28 09:34:01 +01:00
claude[bot]andMervin Praison 67e02cd88a feat: add --file/-f flag for file input support
Implements file input functionality allowing users to pass file contents
as input to PraisonAI commands. The file content is appended to the
prompt similar to how stdin input is handled.

Key changes:
- Added --file/-f argument to CLI parser
- Added read_file_if_provided() method for safe file reading
- Updated prompt handling to combine file input with commands/stdin
- Maintains full backward compatibility

Usage examples:
- praisonai "Analyze this" --file data.txt
- praisonai -f prompt.txt
- echo "context" | praisonai "Process" --file input.txt

Closes #67

Co-authored-by: Mervin Praison <[email protected]>
2025-06-28 07:12:52 +00:00
claude[bot]andMervin Praison 4f7b6ed9f8 fix: Add base_url and api_key parameters to Agent class for Ollama support
This allows users to connect to remote Ollama instances without relying on environment variables.

Usage:
```python
agent = Agent(
    llm="ollama/llama3.2",
    base_url="http://remote-host:11434"
)
```

Fixes #482

Co-authored-by: Mervin Praison <[email protected]>
2025-06-28 07:06:54 +00:00
14 changed files with 2196 additions and 2082 deletions

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
flask \
"praisonai>=2.2.35" \
"praisonai>=2.2.36" \
"praisonai[api]" \
gunicorn \
markdown
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.35" \
"praisonai>=2.2.36" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.35" \
"praisonai>=2.2.36" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.35" \
"praisonai>=2.2.36" \
"praisonai[ui]" \
"praisonai[crewai]"
+2 -2
View File
@@ -121,7 +121,7 @@ healthcheck:
## 📦 Package Versions
All Docker images use consistent, up-to-date versions:
- PraisonAI: `>=2.2.35`
- PraisonAI: `>=2.2.36`
- PraisonAI Agents: `>=0.0.92`
- Python: `3.11-slim`
@@ -218,7 +218,7 @@ docker-compose up -d
### Version Pinning
To use specific versions, update the Dockerfile:
```dockerfile
RUN pip install "praisonai==2.2.35" "praisonaiagents==0.0.92"
RUN pip install "praisonai==2.2.36" "praisonaiagents==0.0.92"
```
## 🌐 Production Deployment
@@ -0,0 +1,34 @@
# Example: Using Ollama with a remote host via base_url parameter
# pip install praisonaiagents
from praisonaiagents import Agent
# Method 1: Using base_url parameter directly (NEW)
agent = Agent(
instructions="You are a helpful assistant",
llm="ollama/llama3.2",
base_url="http://remote-host:11434"
)
response = agent.start("Why is the sky blue?")
print(response)
# Method 2: Using dict configuration
agent2 = Agent(
instructions="You are a helpful assistant",
llm={
"model": "ollama/llama3.2",
"base_url": "http://remote-host:11434"
}
)
response2 = agent2.start("What is the capital of France?")
print(response2)
# Method 3: Using environment variable (existing method)
# export OPENAI_BASE_URL=http://remote-host:11434
# Then just:
# agent3 = Agent(
# instructions="You are a helpful assistant",
# llm="ollama/llama3.2"
# )
@@ -373,7 +373,9 @@ class Agent:
user_id: Optional[str] = None,
reasoning_steps: bool = False,
guardrail: Optional[Union[Callable[['TaskOutput'], Tuple[bool, Any]], str]] = None,
max_guardrail_retries: int = 3
max_guardrail_retries: int = 3,
base_url: Optional[str] = None,
api_key: Optional[str] = None
):
"""Initialize an Agent instance.
@@ -457,6 +459,10 @@ class Agent:
description string for LLM-based validation. Defaults to None.
max_guardrail_retries (int, optional): Maximum number of retry attempts when guardrail
validation fails before giving up. Defaults to 3.
base_url (Optional[str], optional): Base URL for custom LLM endpoints (e.g., Ollama).
If provided, automatically creates a custom LLM instance. Defaults to None.
api_key (Optional[str], optional): API key for LLM provider. If not provided,
falls back to environment variables. Defaults to None.
Raises:
ValueError: If all of name, role, goal, backstory, and instructions are None.
@@ -503,10 +509,40 @@ class Agent:
# Check for model name in environment variable if not provided
self._using_custom_llm = False
# If the user passes a dictionary (for advanced configuration)
if isinstance(llm, dict) and "model" in llm:
# If base_url is provided, always create a custom LLM instance
if base_url:
try:
from ..llm.llm import LLM
# Handle different llm parameter types with base_url
if isinstance(llm, dict):
# Merge base_url and api_key into the dict
llm_config = llm.copy()
llm_config['base_url'] = base_url
if api_key:
llm_config['api_key'] = api_key
self.llm_instance = LLM(**llm_config)
else:
# Create LLM with model string and base_url
model_name = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
self.llm_instance = LLM(
model=model_name,
base_url=base_url,
api_key=api_key
)
self._using_custom_llm = True
except ImportError as e:
raise ImportError(
"LLM features requested but dependencies not installed. "
"Please install with: pip install \"praisonaiagents[llm]\""
) from e
# If the user passes a dictionary (for advanced configuration)
elif isinstance(llm, dict) and "model" in llm:
try:
from ..llm.llm import LLM
# Add api_key if provided and not in dict
if api_key and 'api_key' not in llm:
llm = llm.copy()
llm['api_key'] = api_key
self.llm_instance = LLM(**llm) # Pass all dict items as kwargs
self._using_custom_llm = True
except ImportError as e:
@@ -519,7 +555,10 @@ class Agent:
try:
from ..llm.llm import LLM
# Pass the entire string so LiteLLM can parse provider/model
self.llm_instance = LLM(model=llm)
llm_params = {'model': llm}
if api_key:
llm_params['api_key'] = api_key
self.llm_instance = LLM(**llm_params)
self._using_custom_llm = True
# Ensure tools are properly accessible when using custom LLM
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.108"
version = "0.0.109"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
requires-python = ">=3.10"
authors = [
+2052 -2050
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -3,8 +3,8 @@ 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/v2.2.35.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.35.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.36.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.36.tar.gz | shasum -a 256`.split.first
license "MIT"
depends_on "[email protected]"
+48 -9
View File
@@ -170,6 +170,25 @@ class PraisonAI:
pass
return None
def read_file_if_provided(self, file_path):
"""
Read content from a file if the file path is provided.
Returns the file content or None if file cannot be read.
"""
if not file_path:
return None
try:
with open(file_path, 'r', encoding='utf-8') as f:
file_content = f.read().strip()
return file_content if file_content else None
except FileNotFoundError:
print(f"[red]ERROR: File not found: {file_path}[/red]")
sys.exit(1)
except Exception as e:
print(f"[red]ERROR: Failed to read file: {e}[/red]")
sys.exit(1)
def main(self):
"""
The main function of the PraisonAI object. It parses the command-line arguments,
@@ -189,15 +208,24 @@ class PraisonAI:
# Check for piped input from stdin
stdin_input = self.read_stdin_if_available()
# Check for file input if --file is provided
file_input = self.read_file_if_provided(getattr(args, 'file', None))
if args.command:
if args.command.startswith("tests.test") or args.command.startswith("tests/test"): # Argument used for testing purposes
print("test")
return "test"
else:
# If stdin input is available, append it to the command
# Combine command with any available inputs (stdin and/or file)
combined_inputs = []
if stdin_input:
combined_prompt = f"{args.command} {stdin_input}"
combined_inputs.append(stdin_input)
if file_input:
combined_inputs.append(file_input)
if combined_inputs:
combined_prompt = f"{args.command} {' '.join(combined_inputs)}"
result = self.handle_direct_prompt(combined_prompt)
print(result)
return result
@@ -206,20 +234,30 @@ class PraisonAI:
elif hasattr(args, 'direct_prompt') and args.direct_prompt:
# Only handle direct prompt if agent_file wasn't explicitly set in constructor
if original_agent_file == "agents.yaml": # Default value, so safe to use direct prompt
# If stdin input is available, append it to the direct prompt
prompt = args.direct_prompt
# Combine direct prompt with any available inputs (stdin and/or file)
prompt_parts = [args.direct_prompt]
if stdin_input:
prompt = f"{args.direct_prompt} {stdin_input}"
prompt_parts.append(stdin_input)
if file_input:
prompt_parts.append(file_input)
prompt = ' '.join(prompt_parts)
result = self.handle_direct_prompt(prompt)
print(result)
return result
else:
# Agent file was explicitly set, ignore direct prompt and use the file
pass
elif stdin_input:
# If only stdin input is provided (no command), use it as direct prompt
if original_agent_file == "agents.yaml": # Default value, so safe to use stdin as prompt
result = self.handle_direct_prompt(stdin_input)
elif stdin_input or file_input:
# If only stdin/file input is provided (no command), use it as direct prompt
if original_agent_file == "agents.yaml": # Default value, so safe to use input as prompt
# Combine any available inputs
inputs = []
if stdin_input:
inputs.append(stdin_input)
if file_input:
inputs.append(file_input)
combined_input = ' '.join(inputs)
result = self.handle_direct_prompt(combined_input)
print(result)
return result
# If no command or direct_prompt, preserve agent_file from constructor (don't overwrite)
@@ -530,6 +568,7 @@ class PraisonAI:
parser.add_argument("--public", action="store_true", help="Use ngrok to expose the server publicly (only with --call)")
parser.add_argument("--merge", action="store_true", help="Merge existing agents.yaml with auto-generated agents instead of overwriting")
parser.add_argument("--claudecode", action="store_true", help="Enable Claude Code integration for file modifications and coding tasks")
parser.add_argument("--file", "-f", type=str, help="Read input from a file and append it to the prompt")
# If we're in a test environment, parse with empty args to avoid pytest interference
if in_test_env:
+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.2.35 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.2.36 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.2.35"
version = "2.2.36"
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.108",
"praisonaiagents>=0.0.109",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -95,7 +95,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
[tool.poetry]
name = "PraisonAI"
version = "2.2.35"
version = "2.2.36"
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 = ""
@@ -113,7 +113,7 @@ python = ">=3.10,<3.13"
rich = ">=13.7"
markdown = ">=3.5"
pyparsing = ">=3.0.0"
praisonaiagents = ">=0.0.108"
praisonaiagents = ">=0.0.109"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
+5 -5
View File
@@ -3932,7 +3932,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.2.35"
version = "2.2.36"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -4074,7 +4074,7 @@ requires-dist = [
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.15" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.15" },
{ name = "praisonaiagents", specifier = ">=0.0.108" },
{ name = "praisonaiagents", specifier = ">=0.0.109" },
{ 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" },
@@ -4132,7 +4132,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.108"
version = "0.0.109"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mcp" },
@@ -4141,9 +4141,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f3/48/577f09adfacbfe45367c038483350f125268e8adb8ed48ebc5e0d2ef93f3/praisonaiagents-0.0.108.tar.gz", hash = "sha256:c8e29f7ed5228ba2b6334c9dc5eff9c72abf5fcba10489888f7ac6f2a73764f9", size = 157136, upload-time = "2025-06-27T12:24:00.945Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c4/1e/e99db01581df3eb819d5f140433eea4cf99b7b152c288b9e60e121c4f676/praisonaiagents-0.0.109.tar.gz", hash = "sha256:133b9c0070a858cf5af46683e04fc8245543e02d411956e4c2a8cc894a55a7b1", size = 157598, upload-time = "2025-06-28T08:43:43.388Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/55/fb139f711ce2653bc0a67d9891ad83558427cc9e3c1becbf766aac6bfd25/praisonaiagents-0.0.108-py3-none-any.whl", hash = "sha256:ea0fe4bf9d8fa5ed293997bb035476369d70b46878ba4976a35087adfe0e8575", size = 177749, upload-time = "2025-06-27T12:23:59.646Z" },
{ url = "https://files.pythonhosted.org/packages/7c/dc/5a88261b4879c9b8961b066909ab4f128cfcf81cc951d09c10ae9a1ad44b/praisonaiagents-0.0.109-py3-none-any.whl", hash = "sha256:d94cdea8d868dbe1baadb422c225a7e71efd2706c08c1a88d556c398ae57d042", size = 178224, upload-time = "2025-06-28T08:43:41.627Z" },
]
[[package]]