mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-13 05:01:35 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6df3e32640 | ||
|
|
fb36e1c50c |
No files matched your search
@@ -315,6 +315,8 @@ Methods:
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">compute_quality_score</a>(completeness: float, relevance: float, clarity: float, accuracy: float, weights: Dict[str, float] = None) -> float</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">finalize_task_output</a>(content: str, agent_name: str, quality_score: float, threshold: float = 0.7, metrics: Dict[str, Any] = None, task_id: str = None)</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">get_all_memories</a>() -> List[Dict[str, Any]]</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">get_learn_context</a>() -> str</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">learn</a>()</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">reset_all</a>()</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">reset_entity_only</a>()</code>
|
||||
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">reset_long_term</a>()</code>
|
||||
@@ -616,8 +618,11 @@ Methods:
|
||||
* <code title="cli">praisonai context clear <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai context compact <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai context export <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai context grep <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai context list <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai context show <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai context stats <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai context tail <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
|
||||
* <code title="cli">praisonai debug acp <a href="./src/praisonai/praisonai/cli/commands/debug.py">--help</a></code>
|
||||
* <code title="cli">praisonai debug debug-callback <a href="./src/praisonai/praisonai/cli/commands/debug.py">--help</a></code>
|
||||
* <code title="cli">praisonai debug interactive <a href="./src/praisonai/praisonai/cli/commands/debug.py">--help</a></code>
|
||||
@@ -672,14 +677,19 @@ Methods:
|
||||
* <code title="cli">praisonai lsp status <a href="./src/praisonai/praisonai/cli/commands/lsp.py">--help</a></code>
|
||||
* <code title="cli">praisonai lsp stop <a href="./src/praisonai/praisonai/cli/commands/lsp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp add <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp describe <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp list <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp mcp-callback <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp remove <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp status <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp sync <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp test <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai mcp tools <a href="./src/praisonai/praisonai/cli/commands/mcp.py">--help</a></code>
|
||||
* <code title="cli">praisonai memory add <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
|
||||
* <code title="cli">praisonai memory clear <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
|
||||
* <code title="cli">praisonai memory search <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
|
||||
* <code title="cli">praisonai memory show <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
|
||||
* <code title="cli">praisonai memory status <a href="./src/praisonai/praisonai/cli/commands/memory.py">--help</a></code>
|
||||
* <code title="cli">praisonai package install <a href="./src/praisonai/praisonai/cli/commands/package.py">--help</a></code>
|
||||
* <code title="cli">praisonai package list <a href="./src/praisonai/praisonai/cli/commands/package.py">--help</a></code>
|
||||
* <code title="cli">praisonai package uninstall <a href="./src/praisonai/praisonai/cli/commands/package.py">--help</a></code>
|
||||
@@ -730,8 +740,10 @@ Methods:
|
||||
* <code title="cli">praisonai session resume <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
|
||||
* <code title="cli">praisonai session show <a href="./src/praisonai/praisonai/cli/commands/session.py">--help</a></code>
|
||||
* <code title="cli">praisonai skills create <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
|
||||
* <code title="cli">praisonai skills info <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
|
||||
* <code title="cli">praisonai skills install <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
|
||||
* <code title="cli">praisonai skills list <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
|
||||
* <code title="cli">praisonai skills search <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
|
||||
* <code title="cli">praisonai skills validate <a href="./src/praisonai/praisonai/cli/commands/skills.py">--help</a></code>
|
||||
* <code title="cli">praisonai standardise check <a href="./src/praisonai/praisonai/cli/commands/standardise.py">--help</a></code>
|
||||
* <code title="cli">praisonai standardise fix <a href="./src/praisonai/praisonai/cli/commands/standardise.py">--help</a></code>
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=3.8.1" \
|
||||
"praisonai>=3.8.2" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=3.8.1" \
|
||||
"praisonai>=3.8.2" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=3.8.1" \
|
||||
"praisonai>=3.8.2" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from praisonaiagents import Agent
|
||||
|
||||
print("Creating agent...")
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant"
|
||||
)
|
||||
|
||||
print("Starting agent...")
|
||||
agent = Agent(instructions="You are a helpful assistant")
|
||||
result = agent.start("Why is the sky blue?")
|
||||
|
||||
print("Agent completed successfully!")
|
||||
print(f"Result: {result}")
|
||||
print(result)
|
||||
@@ -297,7 +297,7 @@ class Agent:
|
||||
backstory: Background context shaping personality and decisions.
|
||||
instructions: Direct instructions (overrides role/goal/backstory). Recommended for simple agents.
|
||||
llm: Model name string ("gpt-4o", "anthropic/claude-3-sonnet") or LLM object.
|
||||
Defaults to OPENAI_MODEL_NAME env var or "gpt-5-nano".
|
||||
Defaults to OPENAI_MODEL_NAME env var or "gpt-4o-mini".
|
||||
model: Alias for llm parameter.
|
||||
function_calling_llm: Dedicated LLM for function calling. Deprecated: use llm=.
|
||||
llm_config: LLM configuration dict. Deprecated: use llm=.
|
||||
@@ -844,7 +844,7 @@ class Agent:
|
||||
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-5-nano')
|
||||
model_name = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
||||
self.llm_instance = LLM(
|
||||
model=model_name,
|
||||
base_url=base_url,
|
||||
@@ -907,7 +907,7 @@ class Agent:
|
||||
) from e
|
||||
# Otherwise, fall back to OpenAI environment/name
|
||||
else:
|
||||
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-5-nano')
|
||||
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
||||
# Handle tools parameter - ensure it's always a list
|
||||
if callable(tools):
|
||||
# If a single function/callable is passed, wrap it in a list
|
||||
@@ -953,7 +953,7 @@ class Agent:
|
||||
self.min_reflect = min_reflect
|
||||
self.reflect_prompt = reflect_prompt
|
||||
# Use the same model selection logic for reflect_llm
|
||||
self.reflect_llm = reflect_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-5-nano')
|
||||
self.reflect_llm = reflect_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
||||
self._console = None # Lazy load console when needed
|
||||
|
||||
# Initialize system prompt
|
||||
@@ -1743,7 +1743,7 @@ Your Goal: {self.goal}
|
||||
elif hasattr(self, 'llm') and self.llm:
|
||||
model_name = self.llm
|
||||
else:
|
||||
model_name = "gpt-5-nano"
|
||||
model_name = "gpt-4o-mini"
|
||||
|
||||
return supports_web_search(model_name)
|
||||
|
||||
@@ -1765,7 +1765,7 @@ Your Goal: {self.goal}
|
||||
elif hasattr(self, 'llm') and self.llm:
|
||||
model_name = self.llm
|
||||
else:
|
||||
model_name = "gpt-5-nano"
|
||||
model_name = "gpt-4o-mini"
|
||||
|
||||
return supports_web_fetch(model_name)
|
||||
|
||||
@@ -2009,7 +2009,7 @@ Your Goal: {self.goal}
|
||||
|
||||
Returns:
|
||||
The LLM model/instance being used by this agent.
|
||||
- For standard models: returns the model string (e.g., "gpt-5-nano")
|
||||
- For standard models: returns the model string (e.g., "gpt-4o-mini")
|
||||
- For custom LLM instances: returns the LLM instance object
|
||||
- For provider models: returns the LLM instance object
|
||||
"""
|
||||
@@ -2019,7 +2019,7 @@ Your Goal: {self.goal}
|
||||
return self.llm
|
||||
else:
|
||||
# Default fallback
|
||||
return "gpt-5-nano"
|
||||
return "gpt-4o-mini"
|
||||
|
||||
def _ensure_knowledge_processed(self):
|
||||
"""Ensure knowledge is initialized and processed when first accessed."""
|
||||
@@ -3719,7 +3719,7 @@ Your Goal: {self.goal}"""
|
||||
if self.verbose:
|
||||
logging.debug(f"Agent {self.name} final response: {response_text}")
|
||||
# Return only reasoning content if reasoning_steps is True
|
||||
if reasoning_steps and hasattr(response.choices[0].message, 'reasoning_content'):
|
||||
if reasoning_steps and hasattr(response.choices[0].message, 'reasoning_content') and response.choices[0].message.reasoning_content:
|
||||
# Apply guardrail to reasoning content
|
||||
try:
|
||||
validated_reasoning = self._apply_guardrail_with_retry(response.choices[0].message.reasoning_content, original_prompt, temperature, tools, task_name, task_description, task_id)
|
||||
@@ -4244,7 +4244,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
|
||||
final_response = process_stream_chunks(chunks)
|
||||
# Return only reasoning content if reasoning_steps is True
|
||||
if reasoning_steps and hasattr(final_response.choices[0].message, 'reasoning_content'):
|
||||
if reasoning_steps and hasattr(final_response.choices[0].message, 'reasoning_content') and final_response.choices[0].message.reasoning_content:
|
||||
return final_response.choices[0].message.reasoning_content
|
||||
return final_response.choices[0].message.content if final_response else full_response_text
|
||||
|
||||
|
||||
@@ -434,7 +434,7 @@ This report contains all agent interactions and outputs from a complete ContextA
|
||||
|
||||
Provide comprehensive analysis that follows the PRD template principles and enables
|
||||
AI assistants to implement features that perfectly match existing codebase patterns.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -515,7 +515,7 @@ codebase style and architecture following PRD template principles."""
|
||||
role="Expert Manual Codebase Analysis Specialist",
|
||||
goal="Perform comprehensive manual codebase analysis following PRD methodology",
|
||||
instructions="""Analyze the codebase samples following PRD template methodology for complete understanding.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -566,7 +566,7 @@ Analyze following PRD principles to extract patterns, conventions, and architect
|
||||
6. Design pattern implementations
|
||||
7. Code complexity metrics
|
||||
8. API and interface patterns""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -624,7 +624,7 @@ Extract comprehensive patterns that follow PRD template principles for implement
|
||||
|
||||
For each pattern, provide the pattern name, where it's used, and how to replicate it
|
||||
following PRD template principles.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -674,7 +674,7 @@ patterns and best practices for first-try success."""
|
||||
goal="Analyze testing patterns for comprehensive validation framework design",
|
||||
instructions="""Analyze testing patterns to understand validation approaches and create
|
||||
comprehensive test frameworks following PRD methodology.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -801,7 +801,7 @@ Extract testing patterns for validation framework creation following PRD princip
|
||||
Confidence level for one-pass implementation
|
||||
|
||||
Generate PRPs following this EXACT structure for first-try implementation success.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -850,7 +850,7 @@ on the first try following PRD template principles."""
|
||||
6. CODE QUALITY: Complexity analysis, maintainability
|
||||
7. DOCUMENTATION VALIDATION: Documentation completeness
|
||||
8. DEPENDENCY VALIDATION: Dependency analysis and security""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -897,7 +897,7 @@ following PRD template principles."""
|
||||
instructions="""Compile all available documentation following PRD methodology including:
|
||||
README files, API documentation, setup guides, architecture docs, and any other
|
||||
relevant documentation that provides context for implementation.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -943,7 +943,7 @@ following PRD template principles."""
|
||||
instructions="""Analyze integration points following PRD methodology including:
|
||||
APIs, databases, external services, configuration points, and any other
|
||||
integration requirements that affect implementation.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -1004,7 +1004,7 @@ following PRD template principles."""
|
||||
8. DOCUMENTATION UPDATES: Documentation to create/update
|
||||
9. INTEGRATION STEPS: How to integrate with existing systems
|
||||
10. VALIDATION CHECKPOINTS: Validation steps at each phase""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -1302,7 +1302,7 @@ Every agent interaction has been saved for full audit trail and reproducibility.
|
||||
GOAL: [extracted implementation goal]
|
||||
|
||||
Be precise and extract only what is explicitly mentioned or clearly implied.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -1536,7 +1536,7 @@ Note: Detailed function/class metadata not available due to content access limit
|
||||
5. Documentation topics
|
||||
|
||||
Make the output easy for a file selection agent to understand which files contain what functionality.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -1770,7 +1770,7 @@ Focus on creating clear, structured metadata that will help with intelligent fil
|
||||
["README.md", "src/auth/login.py", "config/settings.py", ...]
|
||||
|
||||
Maximum 50 files for efficient analysis.""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -1923,7 +1923,7 @@ Maximum 50 files.""".format(goal=goal)
|
||||
8. EXAMPLES: Similar features that can guide {goal} implementation
|
||||
|
||||
Since these files were pre-selected for relevance, provide deep analysis of how each contributes to implementing: {goal}""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -2024,7 +2024,7 @@ Since these files were pre-selected for relevance, explain how each contributes
|
||||
- Success criteria for {goal}
|
||||
|
||||
Focus everything on successfully implementing: {goal}""",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-5-nano",
|
||||
llm=self.llm if hasattr(self, 'llm') else "gpt-4o-mini",
|
||||
verbose=getattr(self, 'verbose', True)
|
||||
)
|
||||
|
||||
@@ -2303,13 +2303,13 @@ def create_context_agent(llm: Optional[Union[str, Any]] = None, **kwargs) -> Con
|
||||
Factory function to create a ContextAgent following Context Engineering and PRD methodology.
|
||||
|
||||
Args:
|
||||
llm: Language model to use (e.g., "gpt-5-nano", "claude-3-haiku")
|
||||
llm: Language model to use (e.g., "gpt-4o-mini", "claude-3-haiku")
|
||||
**kwargs: Additional arguments to pass to ContextAgent constructor
|
||||
|
||||
Returns:
|
||||
ContextAgent: Configured ContextAgent for comprehensive context generation following PRD principles
|
||||
"""
|
||||
if llm is None:
|
||||
llm = "gpt-5-nano"
|
||||
llm = "gpt-4o-mini"
|
||||
|
||||
return ContextAgent(llm=llm, **kwargs)
|
||||
@@ -44,7 +44,7 @@ class RouterAgent(Agent):
|
||||
# Initialize model router
|
||||
self.model_router = model_router or ModelRouter()
|
||||
self.routing_strategy = routing_strategy
|
||||
self.fallback_model = fallback_model or os.getenv('OPENAI_MODEL_NAME', 'gpt-5-nano')
|
||||
self.fallback_model = fallback_model or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
||||
|
||||
# Process models configuration
|
||||
self.available_models = self._process_models_config(models)
|
||||
|
||||
@@ -363,7 +363,7 @@ class Agents:
|
||||
self.variables = variables if variables else {}
|
||||
|
||||
# Check for manager_llm in environment variable if not provided
|
||||
self.manager_llm = manager_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-5-nano')
|
||||
self.manager_llm = manager_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
||||
|
||||
# Set logger level based on verbose
|
||||
if _verbose >= 5:
|
||||
|
||||
@@ -103,7 +103,7 @@ class AutoAgents(Agents):
|
||||
self.self_reflect = self_reflect
|
||||
self.max_reflect = max_reflect
|
||||
self.min_reflect = min_reflect
|
||||
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-5-nano')
|
||||
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
||||
self.function_calling_llm = function_calling_llm
|
||||
self.code_execution_mode = code_execution_mode
|
||||
self.embedder_config = embedder_config
|
||||
|
||||
@@ -45,11 +45,13 @@ class MemoryBackend(str, Enum):
|
||||
|
||||
|
||||
class LearnScope(str, Enum):
|
||||
"""Scope for continuous learning."""
|
||||
USER = "user" # Per-user learning
|
||||
TEAM = "team" # Team-shared learning
|
||||
ORG = "org" # Organization-wide learning
|
||||
GLOBAL = "global" # Global learning
|
||||
"""Scope for learning data visibility.
|
||||
|
||||
PRIVATE: Learning data is private to this user/agent (default, safest)
|
||||
SHARED: Learning data is shared with all agents
|
||||
"""
|
||||
PRIVATE = "private" # Private to this user/agent
|
||||
SHARED = "shared" # Shared with all agents
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -83,7 +85,7 @@ class LearnConfig:
|
||||
improvements: bool = False # Self-improvement proposals
|
||||
|
||||
# Scope configuration
|
||||
scope: Union[str, LearnScope] = LearnScope.USER
|
||||
scope: Union[str, LearnScope] = LearnScope.PRIVATE
|
||||
|
||||
# Storage configuration
|
||||
store_path: Optional[str] = None # Custom storage path
|
||||
|
||||
@@ -68,8 +68,8 @@ class LLM:
|
||||
MODEL_WINDOWS = {
|
||||
# OpenAI
|
||||
"gpt-4": 6144, # 8,192 actual
|
||||
"gpt-5-nano": 96000, # 128,000 actual
|
||||
"gpt-5-nano": 96000, # 128,000 actual
|
||||
"gpt-4o-mini": 96000, # 128,000 actual
|
||||
"gpt-4o-mini": 96000, # 128,000 actual
|
||||
"gpt-4-turbo": 96000, # 128,000 actual
|
||||
"o1-preview": 96000, # 128,000 actual
|
||||
"o1-mini": 96000, # 128,000 actual
|
||||
|
||||
@@ -51,7 +51,7 @@ class ModelRouter:
|
||||
DEFAULT_MODELS = [
|
||||
# Lightweight/cheap models for simple tasks
|
||||
ModelProfile(
|
||||
name="gpt-5-nano",
|
||||
name="gpt-4o-mini",
|
||||
provider="openai",
|
||||
complexity_range=(TaskComplexity.SIMPLE, TaskComplexity.MODERATE),
|
||||
cost_per_1k_tokens=0.00075, # Average of $0.00015 input, $0.0006 output
|
||||
@@ -80,7 +80,7 @@ class ModelRouter:
|
||||
|
||||
# Mid-tier models for moderate complexity
|
||||
ModelProfile(
|
||||
name="gpt-5-nano",
|
||||
name="gpt-4o-mini",
|
||||
provider="openai",
|
||||
complexity_range=(TaskComplexity.MODERATE, TaskComplexity.COMPLEX),
|
||||
cost_per_1k_tokens=0.0075, # Average of $0.0025 input, $0.01 output
|
||||
@@ -145,7 +145,7 @@ class ModelRouter:
|
||||
preferred_providers: List of preferred providers in order
|
||||
"""
|
||||
self.models = models or self.DEFAULT_MODELS
|
||||
self.default_model = default_model or os.getenv('OPENAI_MODEL_NAME', 'gpt-5-nano')
|
||||
self.default_model = default_model or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
||||
self.cost_threshold = cost_threshold
|
||||
self.preferred_providers = preferred_providers or []
|
||||
|
||||
|
||||
@@ -988,7 +988,7 @@ class OpenAIClient:
|
||||
def create_completion(
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
temperature: float = 1.0,
|
||||
stream: bool = False,
|
||||
tools: Optional[List[Dict[str, Any]]] = None,
|
||||
@@ -1033,7 +1033,7 @@ class OpenAIClient:
|
||||
async def acreate_completion(
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
temperature: float = 1.0,
|
||||
stream: bool = False,
|
||||
tools: Optional[List[Dict[str, Any]]] = None,
|
||||
@@ -1078,7 +1078,7 @@ class OpenAIClient:
|
||||
def chat_completion_with_tools(
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
temperature: float = 1.0,
|
||||
tools: Optional[List[Any]] = None,
|
||||
execute_tool_fn: Optional[Callable] = None,
|
||||
@@ -1266,7 +1266,7 @@ class OpenAIClient:
|
||||
async def achat_completion_with_tools(
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
temperature: float = 1.0,
|
||||
tools: Optional[List[Any]] = None,
|
||||
execute_tool_fn: Optional[Callable] = None,
|
||||
@@ -1450,7 +1450,7 @@ class OpenAIClient:
|
||||
def chat_completion_with_tools_stream(
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
temperature: float = 1.0,
|
||||
tools: Optional[List[Any]] = None,
|
||||
execute_tool_fn: Optional[Callable] = None,
|
||||
@@ -1602,7 +1602,7 @@ class OpenAIClient:
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
response_format: BaseModel,
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
temperature: float = 1.0,
|
||||
**kwargs
|
||||
) -> Any:
|
||||
@@ -1636,7 +1636,7 @@ class OpenAIClient:
|
||||
self,
|
||||
messages: List[Dict[str, Any]],
|
||||
response_format: BaseModel,
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
temperature: float = 1.0,
|
||||
**kwargs
|
||||
) -> Any:
|
||||
|
||||
@@ -162,7 +162,7 @@ class MCP:
|
||||
# Method 1: Using command and args separately
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant...",
|
||||
llm="gpt-5-nano",
|
||||
llm="gpt-4o-mini",
|
||||
tools=MCP(
|
||||
command="/path/to/python",
|
||||
args=["/path/to/app.py"]
|
||||
@@ -172,14 +172,14 @@ class MCP:
|
||||
# Method 2: Using a single command string
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant...",
|
||||
llm="gpt-5-nano",
|
||||
llm="gpt-4o-mini",
|
||||
tools=MCP("/path/to/python /path/to/app.py")
|
||||
)
|
||||
|
||||
# Method 3: Using an SSE endpoint
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant...",
|
||||
llm="gpt-5-nano",
|
||||
llm="gpt-4o-mini",
|
||||
tools=MCP("http://localhost:8080/sse")
|
||||
)
|
||||
|
||||
@@ -548,7 +548,7 @@ class MCP:
|
||||
"""Convert the MCP tool to an OpenAI-compatible tool definition.
|
||||
|
||||
This method is specifically invoked by the Agent class when using
|
||||
provider/model format (e.g., "openai/gpt-5-nano").
|
||||
provider/model format (e.g., "openai/gpt-4o-mini").
|
||||
|
||||
Returns:
|
||||
dict or list: OpenAI-compatible tool definition(s)
|
||||
|
||||
@@ -56,7 +56,7 @@ class BaseStore(ABC):
|
||||
self,
|
||||
store_path: Optional[str] = None,
|
||||
user_id: Optional[str] = None,
|
||||
scope: str = "user",
|
||||
scope: str = "private",
|
||||
):
|
||||
self.user_id = user_id or "default"
|
||||
self.scope = scope
|
||||
|
||||
@@ -109,7 +109,7 @@ class Memory:
|
||||
},
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {"model": "gpt-5-nano", "api_key": "..."}
|
||||
"config": {"model": "gpt-4o-mini", "api_key": "..."}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai",
|
||||
@@ -1444,7 +1444,7 @@ class Memory:
|
||||
import litellm
|
||||
|
||||
# Convert model name if it's in litellm format
|
||||
model_name = llm or "gpt-5-nano"
|
||||
model_name = llm or "gpt-4o-mini"
|
||||
|
||||
response = litellm.completion(
|
||||
model=model_name,
|
||||
@@ -1461,7 +1461,7 @@ class Memory:
|
||||
client = OpenAI()
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=llm or "gpt-5-nano",
|
||||
model=llm or "gpt-4o-mini",
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": custom_prompt or default_prompt
|
||||
|
||||
@@ -326,7 +326,7 @@ class Task:
|
||||
if hasattr(self.agent.llm_instance, 'model'):
|
||||
llm_model = self.agent.llm_instance.model
|
||||
else:
|
||||
llm_model = "gpt-5-nano" # Default fallback
|
||||
llm_model = "gpt-4o-mini" # Default fallback
|
||||
elif hasattr(self.agent, 'llm') and self.agent.llm:
|
||||
# For standard model strings
|
||||
llm_model = self.agent.llm
|
||||
|
||||
@@ -27,7 +27,7 @@ class GenerateCOT:
|
||||
def __init__(
|
||||
self,
|
||||
qa_pairs: Optional[Dict[str, str]] = None,
|
||||
model: str = "gpt-5-nano",
|
||||
model: str = "gpt-4o-mini",
|
||||
api_key: Optional[str] = None,
|
||||
max_attempts: int = 3,
|
||||
verbose: bool = True,
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.11.7"
|
||||
version = "0.11.8"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
Unit tests for Agent output parameter with verbose preset.
|
||||
|
||||
Tests the fix for the bug where output="verbose" would return None
|
||||
because reasoning_steps=True in the verbose preset was causing the code
|
||||
to try to return reasoning_content, but this attribute exists as None
|
||||
for non-reasoning models.
|
||||
"""
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
|
||||
|
||||
class TestReasoningContentLogic:
|
||||
"""Test the reasoning_content check logic directly."""
|
||||
|
||||
def test_condition_with_none_reasoning_content(self):
|
||||
"""Verify the fix: condition should be False when reasoning_content is None."""
|
||||
mock_message = Mock()
|
||||
mock_message.reasoning_content = None
|
||||
|
||||
reasoning_steps = True
|
||||
|
||||
# Old buggy condition:
|
||||
# if reasoning_steps and hasattr(mock_message, 'reasoning_content'):
|
||||
old_condition = reasoning_steps and hasattr(mock_message, 'reasoning_content')
|
||||
assert old_condition == True, "Old condition would be True (the bug)"
|
||||
|
||||
# Fixed condition:
|
||||
# if reasoning_steps and hasattr(mock_message, 'reasoning_content') and mock_message.reasoning_content:
|
||||
# The truthiness of the whole expression should be False
|
||||
fixed_condition = reasoning_steps and hasattr(mock_message, 'reasoning_content') and mock_message.reasoning_content
|
||||
assert not fixed_condition, "Fixed condition should be falsy when reasoning_content is None"
|
||||
|
||||
def test_condition_with_empty_string_reasoning_content(self):
|
||||
"""Verify the fix: condition should be False when reasoning_content is empty string."""
|
||||
mock_message = Mock()
|
||||
mock_message.reasoning_content = ""
|
||||
|
||||
reasoning_steps = True
|
||||
|
||||
# Old condition would True (bug)
|
||||
old_condition = reasoning_steps and hasattr(mock_message, 'reasoning_content')
|
||||
assert old_condition == True
|
||||
|
||||
# Fixed condition should be falsy
|
||||
fixed_condition = reasoning_steps and hasattr(mock_message, 'reasoning_content') and mock_message.reasoning_content
|
||||
assert not fixed_condition, "Fixed condition should be falsy when reasoning_content is empty string"
|
||||
|
||||
def test_condition_with_actual_reasoning_content(self):
|
||||
"""Verify the fix: condition should be True when reasoning_content has content."""
|
||||
mock_message = Mock()
|
||||
mock_message.reasoning_content = "This is my reasoning process..."
|
||||
|
||||
reasoning_steps = True
|
||||
|
||||
# Both old and new condition should be truthy
|
||||
old_condition = reasoning_steps and hasattr(mock_message, 'reasoning_content')
|
||||
assert old_condition
|
||||
|
||||
fixed_condition = reasoning_steps and hasattr(mock_message, 'reasoning_content') and mock_message.reasoning_content
|
||||
assert fixed_condition, "Fixed condition should be truthy when reasoning_content has content"
|
||||
|
||||
def test_condition_when_reasoning_steps_false(self):
|
||||
"""Verify that when reasoning_steps is False, condition is always False."""
|
||||
mock_message = Mock()
|
||||
mock_message.reasoning_content = "This is my reasoning process..."
|
||||
|
||||
reasoning_steps = False
|
||||
|
||||
fixed_condition = reasoning_steps and hasattr(mock_message, 'reasoning_content') and mock_message.reasoning_content
|
||||
assert not fixed_condition, "Condition should be falsy when reasoning_steps is False"
|
||||
|
||||
def test_if_statement_behavior_with_none(self):
|
||||
"""Verify that an if statement using the condition skips when reasoning_content is None."""
|
||||
mock_message = Mock()
|
||||
mock_message.reasoning_content = None
|
||||
|
||||
reasoning_steps = True
|
||||
execution_path = "not_taken"
|
||||
|
||||
# This is how the fixed code behaves
|
||||
if reasoning_steps and hasattr(mock_message, 'reasoning_content') and mock_message.reasoning_content:
|
||||
execution_path = "reasoning_path"
|
||||
else:
|
||||
execution_path = "regular_path"
|
||||
|
||||
assert execution_path == "regular_path", "Should take regular path when reasoning_content is None"
|
||||
|
||||
def test_if_statement_behavior_with_reasoning(self):
|
||||
"""Verify that an if statement using the condition enters when reasoning_content has value."""
|
||||
mock_message = Mock()
|
||||
mock_message.reasoning_content = "Reasoning steps here..."
|
||||
|
||||
reasoning_steps = True
|
||||
execution_path = "not_taken"
|
||||
|
||||
if reasoning_steps and hasattr(mock_message, 'reasoning_content') and mock_message.reasoning_content:
|
||||
execution_path = "reasoning_path"
|
||||
else:
|
||||
execution_path = "regular_path"
|
||||
|
||||
assert execution_path == "reasoning_path", "Should take reasoning path when reasoning_content has value"
|
||||
|
||||
|
||||
class TestVerbosePresetConfig:
|
||||
"""Test that the verbose preset has the expected configuration."""
|
||||
|
||||
def test_verbose_preset_has_reasoning_steps_true(self):
|
||||
"""Verify that the verbose preset enables reasoning_steps."""
|
||||
from praisonaiagents.config.presets import OUTPUT_PRESETS
|
||||
|
||||
verbose_preset = OUTPUT_PRESETS.get("verbose")
|
||||
assert verbose_preset is not None, "verbose preset should exist"
|
||||
assert verbose_preset.get("reasoning_steps") == True, "verbose preset should have reasoning_steps=True"
|
||||
|
||||
def test_verbose_preset_has_verbose_true(self):
|
||||
"""Verify that the verbose preset enables verbose output."""
|
||||
from praisonaiagents.config.presets import OUTPUT_PRESETS
|
||||
|
||||
verbose_preset = OUTPUT_PRESETS.get("verbose")
|
||||
assert verbose_preset.get("verbose") == True, "verbose preset should have verbose=True"
|
||||
|
||||
def test_verbose_preset_has_stream_false(self):
|
||||
"""Verify that the verbose preset does not enable streaming by default."""
|
||||
from praisonaiagents.config.presets import OUTPUT_PRESETS
|
||||
|
||||
verbose_preset = OUTPUT_PRESETS.get("verbose")
|
||||
assert verbose_preset.get("stream") == False, "verbose preset should have stream=False"
|
||||
|
||||
|
||||
class TestChatCompletionMessageDataclass:
|
||||
"""Test the ChatCompletionMessage dataclass has reasoning_content attribute."""
|
||||
|
||||
def test_chat_completion_message_has_reasoning_content(self):
|
||||
"""Verify ChatCompletionMessage has reasoning_content attribute that defaults to None."""
|
||||
from praisonaiagents.llm.openai_client import ChatCompletionMessage
|
||||
|
||||
# Create without reasoning_content
|
||||
message = ChatCompletionMessage(content="Test", role="assistant")
|
||||
|
||||
# Should have the attribute
|
||||
assert hasattr(message, 'reasoning_content')
|
||||
# Should default to None
|
||||
assert message.reasoning_content is None
|
||||
|
||||
def test_chat_completion_message_with_reasoning(self):
|
||||
"""Verify ChatCompletionMessage can have reasoning_content set."""
|
||||
from praisonaiagents.llm.openai_client import ChatCompletionMessage
|
||||
|
||||
message = ChatCompletionMessage(
|
||||
content="Test",
|
||||
role="assistant",
|
||||
reasoning_content="This is my reasoning..."
|
||||
)
|
||||
|
||||
assert message.reasoning_content == "This is my reasoning..."
|
||||
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
Tests for LearnScope enum - verifying PRIVATE/SHARED values only.
|
||||
|
||||
These tests verify that:
|
||||
1. LearnScope only has PRIVATE and SHARED values
|
||||
2. TEAM and ORG values do NOT exist
|
||||
3. LearnConfig defaults to PRIVATE scope
|
||||
4. BaseStore defaults to "private" scope
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class TestLearnScopeValues:
|
||||
"""Test LearnScope enum has exactly the right values."""
|
||||
|
||||
def test_learn_scope_has_private(self):
|
||||
"""LearnScope should have PRIVATE value."""
|
||||
from praisonaiagents.config.feature_configs import LearnScope
|
||||
|
||||
assert hasattr(LearnScope, 'PRIVATE')
|
||||
assert LearnScope.PRIVATE.value == "private"
|
||||
|
||||
def test_learn_scope_has_shared(self):
|
||||
"""LearnScope should have SHARED value."""
|
||||
from praisonaiagents.config.feature_configs import LearnScope
|
||||
|
||||
assert hasattr(LearnScope, 'SHARED')
|
||||
assert LearnScope.SHARED.value == "shared"
|
||||
|
||||
def test_learn_scope_does_not_have_team(self):
|
||||
"""LearnScope should NOT have TEAM value."""
|
||||
from praisonaiagents.config.feature_configs import LearnScope
|
||||
|
||||
assert not hasattr(LearnScope, 'TEAM'), "TEAM should be removed from LearnScope"
|
||||
|
||||
def test_learn_scope_does_not_have_org(self):
|
||||
"""LearnScope should NOT have ORG value."""
|
||||
from praisonaiagents.config.feature_configs import LearnScope
|
||||
|
||||
assert not hasattr(LearnScope, 'ORG'), "ORG should be removed from LearnScope"
|
||||
|
||||
def test_learn_scope_does_not_have_user(self):
|
||||
"""LearnScope should NOT have USER value (replaced by PRIVATE)."""
|
||||
from praisonaiagents.config.feature_configs import LearnScope
|
||||
|
||||
assert not hasattr(LearnScope, 'USER'), "USER should be replaced by PRIVATE"
|
||||
|
||||
def test_learn_scope_does_not_have_global(self):
|
||||
"""LearnScope should NOT have GLOBAL value (replaced by SHARED)."""
|
||||
from praisonaiagents.config.feature_configs import LearnScope
|
||||
|
||||
assert not hasattr(LearnScope, 'GLOBAL'), "GLOBAL should be replaced by SHARED"
|
||||
|
||||
def test_learn_scope_has_exactly_two_values(self):
|
||||
"""LearnScope should have exactly 2 values: PRIVATE and SHARED."""
|
||||
from praisonaiagents.config.feature_configs import LearnScope
|
||||
|
||||
all_values = list(LearnScope)
|
||||
assert len(all_values) == 2, f"Expected 2 values, got {len(all_values)}: {all_values}"
|
||||
|
||||
|
||||
class TestLearnConfigDefaults:
|
||||
"""Test LearnConfig uses correct default scope."""
|
||||
|
||||
def test_learn_config_default_scope_is_private(self):
|
||||
"""LearnConfig should default to PRIVATE scope."""
|
||||
from praisonaiagents.config.feature_configs import LearnConfig, LearnScope
|
||||
|
||||
config = LearnConfig()
|
||||
assert config.scope == LearnScope.PRIVATE
|
||||
|
||||
def test_learn_config_scope_can_be_set_to_shared(self):
|
||||
"""LearnConfig scope can be set to SHARED."""
|
||||
from praisonaiagents.config.feature_configs import LearnConfig, LearnScope
|
||||
|
||||
config = LearnConfig(scope=LearnScope.SHARED)
|
||||
assert config.scope == LearnScope.SHARED
|
||||
|
||||
def test_learn_config_to_dict_includes_scope(self):
|
||||
"""LearnConfig.to_dict() should include scope with correct value."""
|
||||
from praisonaiagents.config.feature_configs import LearnConfig, LearnScope
|
||||
|
||||
config = LearnConfig()
|
||||
d = config.to_dict()
|
||||
assert d['scope'] == 'private'
|
||||
|
||||
config2 = LearnConfig(scope=LearnScope.SHARED)
|
||||
d2 = config2.to_dict()
|
||||
assert d2['scope'] == 'shared'
|
||||
|
||||
|
||||
class TestBaseStoreDefaults:
|
||||
"""Test BaseStore uses correct default scope."""
|
||||
|
||||
def test_base_store_default_scope_is_private(self):
|
||||
"""BaseStore should default to 'private' scope."""
|
||||
import inspect
|
||||
from praisonaiagents.memory.learn.stores import BaseStore
|
||||
|
||||
sig = inspect.signature(BaseStore.__init__)
|
||||
scope_param = sig.parameters.get('scope')
|
||||
assert scope_param is not None
|
||||
assert scope_param.default == 'private', f"Expected 'private', got '{scope_param.default}'"
|
||||
Generated
+2
-2
@@ -1,5 +1,5 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
revision = 3
|
||||
requires-python = ">=3.10"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.13'",
|
||||
@@ -2422,7 +2422,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.11.7"
|
||||
version = "0.11.8"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
@@ -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/v3.8.1.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.8.1.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.8.2.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.8.2.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -117,7 +117,7 @@ app.add_typer(learn_app, name="learn")
|
||||
@learn_app.command("status")
|
||||
def learn_status(
|
||||
user_id: str = typer.Option("default", "--user-id", "-u", help="User ID"),
|
||||
scope: str = typer.Option("user", "--scope", "-s", help="Scope (user/team/org)"),
|
||||
scope: str = typer.Option("private", "--scope", "-s", help="Scope (private/shared)"),
|
||||
):
|
||||
"""Show learning status and statistics."""
|
||||
from praisonaiagents.memory.learn import LearnManager
|
||||
|
||||
@@ -57,7 +57,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==3.8.1 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==3.8.2 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "3.8.1"
|
||||
__version__ = "3.8.2"
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.11.7",
|
||||
"praisonaiagents>=0.11.8",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
|
||||
@@ -3,26 +3,35 @@
|
||||
Combined bump version and release script for PraisonAI package.
|
||||
|
||||
Usage:
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99
|
||||
# Basic: bump praisonai to 3.8.2 with agents 0.11.8
|
||||
python scripts/bump_and_release.py --agents 0.11.8 3.8.2
|
||||
|
||||
Or with optional praisonaiagents version:
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99 --agents 0.0.169
|
||||
# Wait for agents version to propagate to PyPI (recommended after publishing agents)
|
||||
python scripts/bump_and_release.py --agents 0.11.8 --wait 3.8.2
|
||||
|
||||
# Auto-detect latest agents version from PyPI
|
||||
python scripts/bump_and_release.py --auto 3.8.2
|
||||
|
||||
This script:
|
||||
1. Bumps version in all required files
|
||||
2. Copies root README.md to package dir
|
||||
3. Runs uv lock & uv build
|
||||
4. Commits changes
|
||||
5. Creates git tag
|
||||
6. Pushes to GitHub
|
||||
7. Creates GitHub release (latest)
|
||||
1. Validates pre-flight conditions (clean git state, versions)
|
||||
2. Optionally waits for praisonaiagents to be available on PyPI
|
||||
3. Bumps version in all required files
|
||||
4. Copies root README.md to package dir
|
||||
5. Runs uv lock & uv build
|
||||
6. Commits changes
|
||||
7. Creates git tag
|
||||
8. Pushes to GitHub (with rebase if needed)
|
||||
9. Creates GitHub release (latest)
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import argparse
|
||||
import subprocess
|
||||
import shutil
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
@@ -37,20 +46,63 @@ def get_praisonai_dir() -> Path:
|
||||
return get_project_root() / "src/praisonai"
|
||||
|
||||
|
||||
def run(cmd: list[str], cwd: Optional[Path] = None, check: bool = True) -> subprocess.CompletedProcess:
|
||||
def run(cmd: list[str], cwd: Optional[Path] = None, check: bool = True, silent: bool = False) -> subprocess.CompletedProcess:
|
||||
"""Run a command and print it."""
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
if not silent:
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True)
|
||||
if result.stdout:
|
||||
print(result.stdout)
|
||||
if result.stderr:
|
||||
print(result.stderr, file=sys.stderr)
|
||||
if not silent:
|
||||
if result.stdout:
|
||||
print(result.stdout)
|
||||
if result.stderr:
|
||||
print(result.stderr, file=sys.stderr)
|
||||
if check and result.returncode != 0:
|
||||
print(f"❌ Command failed with exit code {result.returncode}")
|
||||
sys.exit(1)
|
||||
return result
|
||||
|
||||
|
||||
def get_pypi_version(package: str) -> Optional[str]:
|
||||
"""Get the latest version of a package from PyPI."""
|
||||
try:
|
||||
url = f"https://pypi.org/pypi/{package}/json"
|
||||
with urllib.request.urlopen(url, timeout=10) as response:
|
||||
data = json.loads(response.read().decode())
|
||||
return data["info"]["version"]
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Failed to fetch PyPI version: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def wait_for_pypi_version(package: str, version: str, max_wait: int = 300, interval: int = 30) -> bool:
|
||||
"""Wait for a specific version to be available on PyPI."""
|
||||
print(f"\n⏳ Waiting for {package}=={version} to be available on PyPI...")
|
||||
start = time.time()
|
||||
|
||||
while time.time() - start < max_wait:
|
||||
current = get_pypi_version(package)
|
||||
if current == version:
|
||||
print(f" ✅ {package}=={version} is now available on PyPI")
|
||||
return True
|
||||
|
||||
elapsed = int(time.time() - start)
|
||||
remaining = max_wait - elapsed
|
||||
print(f" ⏳ Current: {current}, waiting for: {version} ({remaining}s remaining...)")
|
||||
time.sleep(interval)
|
||||
|
||||
print(f" ❌ Timeout waiting for {package}=={version} on PyPI")
|
||||
return False
|
||||
|
||||
|
||||
def check_git_status() -> bool:
|
||||
"""Check if there are uncommitted changes."""
|
||||
root = get_project_root()
|
||||
result = run(["git", "status", "--porcelain"], cwd=root, check=False, silent=True)
|
||||
if result.stdout.strip():
|
||||
return True # Has uncommitted changes
|
||||
return False
|
||||
|
||||
|
||||
def update_file(filepath: Path, patterns: list[tuple[str, str]], root: Path) -> bool:
|
||||
"""Update a file with the given regex patterns and replacements."""
|
||||
if not filepath.exists():
|
||||
@@ -130,17 +182,27 @@ def bump_version(new_version: str, agents_version: Optional[str] = None):
|
||||
print("\n✨ Version bump complete!")
|
||||
|
||||
|
||||
def validate_dependencies() -> bool:
|
||||
"""Validate that uv lock will succeed before making any changes."""
|
||||
def validate_dependencies(max_retries: int = 3, retry_interval: int = 60) -> bool:
|
||||
"""Validate that uv lock will succeed, with retry logic for PyPI propagation."""
|
||||
praisonai_dir = get_praisonai_dir()
|
||||
|
||||
print("\n🔍 Validating dependencies (dry-run)...")
|
||||
result = run(["uv", "lock", "--dry-run"], cwd=praisonai_dir, check=False)
|
||||
if result.returncode != 0:
|
||||
print("\n❌ Dependency validation failed. Fix the issues above before releasing.")
|
||||
return False
|
||||
print(" ✅ Dependencies validated successfully")
|
||||
return True
|
||||
for attempt in range(max_retries):
|
||||
print(f"\n🔍 Validating dependencies (attempt {attempt + 1}/{max_retries})...")
|
||||
|
||||
# Clear cache before validation
|
||||
run(["uv", "cache", "clean"], cwd=praisonai_dir, silent=True)
|
||||
|
||||
result = run(["uv", "lock", "--dry-run"], cwd=praisonai_dir, check=False)
|
||||
if result.returncode == 0:
|
||||
print(" ✅ Dependencies validated successfully")
|
||||
return True
|
||||
|
||||
if attempt < max_retries - 1:
|
||||
print(f"\n⏳ Waiting {retry_interval}s for PyPI propagation before retry...")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
print("\n❌ Dependency validation failed after all retries.")
|
||||
return False
|
||||
|
||||
|
||||
def release(version: str):
|
||||
@@ -179,8 +241,17 @@ def release(version: str):
|
||||
print(f"\n🏷️ Creating tag {tag}...")
|
||||
run(["git", "tag", "-f", tag], cwd=root)
|
||||
|
||||
# 6. Push to GitHub
|
||||
# 6. Pull rebase and push to GitHub
|
||||
print("\n⬆️ Pushing to GitHub...")
|
||||
# First fetch and rebase to handle any remote changes (e.g., auto-generated api.md)
|
||||
result = run(["git", "pull", "--rebase", "origin", "main"], cwd=root, check=False)
|
||||
if result.returncode != 0:
|
||||
print(" ⚠️ Rebase failed, trying to continue...")
|
||||
|
||||
# Recreate tag after rebase (commit hash may have changed)
|
||||
run(["git", "tag", "-f", tag], cwd=root)
|
||||
|
||||
# Push changes
|
||||
run(["git", "push"], cwd=root)
|
||||
run(["git", "push", "--tags", "-f"], cwd=root)
|
||||
|
||||
@@ -200,37 +271,105 @@ def release(version: str):
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Bump version and release PraisonAI package"
|
||||
description="Bump version and release PraisonAI package",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Bump to 3.8.2 with agents 0.11.8
|
||||
python scripts/bump_and_release.py --agents 0.11.8 3.8.2
|
||||
|
||||
# Wait for agents to propagate to PyPI first (recommended)
|
||||
python scripts/bump_and_release.py --agents 0.11.8 --wait 3.8.2
|
||||
|
||||
# Auto-detect latest agents version from PyPI
|
||||
python scripts/bump_and_release.py --auto 3.8.2
|
||||
"""
|
||||
)
|
||||
parser.add_argument(
|
||||
"version",
|
||||
help="New version number (e.g., 2.2.99)"
|
||||
help="New version number (e.g., 3.8.2)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--agents", "-a",
|
||||
help="Optional: Update praisonaiagents dependency version (e.g., 0.0.169)",
|
||||
help="Update praisonaiagents dependency version (e.g., 0.11.8)",
|
||||
default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"--auto",
|
||||
action="store_true",
|
||||
help="Auto-detect latest praisonaiagents version from PyPI"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--wait", "-w",
|
||||
action="store_true",
|
||||
help="Wait for the specified agents version to be available on PyPI"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-wait",
|
||||
type=int,
|
||||
default=300,
|
||||
help="Maximum seconds to wait for PyPI propagation (default: 300)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--retries", "-r",
|
||||
type=int,
|
||||
default=3,
|
||||
help="Number of retries for dependency validation (default: 3)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--force", "-f",
|
||||
action="store_true",
|
||||
help="Skip pre-flight checks (use with caution)"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate version format
|
||||
if not re.match(r'^\d+\.\d+\.\d+$', args.version):
|
||||
print(f"❌ Invalid version format: {args.version}")
|
||||
print(" Expected format: X.Y.Z (e.g., 2.2.99)")
|
||||
print(" Expected format: X.Y.Z (e.g., 3.8.2)")
|
||||
sys.exit(1)
|
||||
|
||||
# Handle --auto flag
|
||||
if args.auto:
|
||||
print("\n🔍 Auto-detecting latest praisonaiagents version from PyPI...")
|
||||
agents_version = get_pypi_version("praisonaiagents")
|
||||
if not agents_version:
|
||||
print("❌ Failed to auto-detect praisonaiagents version")
|
||||
sys.exit(1)
|
||||
print(f" ✅ Latest version: {agents_version}")
|
||||
args.agents = agents_version
|
||||
|
||||
# Validate agents version format if provided
|
||||
if args.agents and not re.match(r'^\d+\.\d+\.\d+$', args.agents):
|
||||
print(f"❌ Invalid agents version format: {args.agents}")
|
||||
print(" Expected format: X.Y.Z (e.g., 0.0.169)")
|
||||
print(" Expected format: X.Y.Z (e.g., 0.11.8)")
|
||||
sys.exit(1)
|
||||
|
||||
# Pre-flight checks
|
||||
if not args.force:
|
||||
print("\n🔍 Pre-flight checks...")
|
||||
|
||||
# Check for uncommitted changes (warning only)
|
||||
if check_git_status():
|
||||
print(" ⚠️ Warning: You have uncommitted changes")
|
||||
else:
|
||||
print(" ✅ Git working directory is clean")
|
||||
|
||||
# Wait for PyPI propagation if requested
|
||||
if args.wait and args.agents:
|
||||
if not wait_for_pypi_version("praisonaiagents", args.agents, max_wait=args.max_wait):
|
||||
print("\n💡 Tip: Check if the package was published successfully")
|
||||
print("💡 Tip: You can retry without --wait if the package is confirmed published")
|
||||
sys.exit(1)
|
||||
|
||||
# Run bump version
|
||||
bump_version(args.version, args.agents)
|
||||
|
||||
# Validate dependencies after version bump (before git operations)
|
||||
if not validate_dependencies():
|
||||
# Validate dependencies after version bump (with retries)
|
||||
if not validate_dependencies(max_retries=args.retries):
|
||||
print("\n💡 Tip: Revert changes with 'git checkout .' if needed")
|
||||
print("💡 Tip: The package may need more time to propagate to PyPI")
|
||||
sys.exit(1)
|
||||
|
||||
# Run release
|
||||
|
||||
Generated
+4
-4
@@ -4276,7 +4276,7 @@ requires-dist = [
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.1.0" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen-v4'", specifier = ">=0.1.0" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.1.0" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.11.7" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.11.8" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = "==0.2.29" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -4324,7 +4324,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.11.7"
|
||||
version = "0.11.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -4333,9 +4333,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/9a/fb687274c729813f9ce8f7fbfc5effb9a7194f88c3f36f56f276cf0fb43b/praisonaiagents-0.11.7.tar.gz", hash = "sha256:11b9c00b9b7d0eb34ea0faf58fdce524d6c935daf872be9ba6d47bee876c594c", size = 993605, upload-time = "2026-01-12T16:54:06.17Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/92/e8/e0a52140b3c09be369dd48dbb71addf8bb711c3267d05abf352ef6717f55/praisonaiagents-0.11.8.tar.gz", hash = "sha256:16599d391838d456bb4933c5778049ff366eb2fb812f8e5f645b2d60c9c919c5", size = 993464, upload-time = "2026-01-13T04:11:31.609Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/d4/67b6e90e9c949f380ec1cae21005b398865ce927585845364ecfb6e7048e/praisonaiagents-0.11.7-py3-none-any.whl", hash = "sha256:b20653e209b16c3ecd788c348bdec99dc97cad5e85129b1061a44a86fd580bee", size = 977510, upload-time = "2026-01-12T16:54:03.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/4a/edf2cffa71f3f76d3f1d393ce9bff64a4873e54a8a2abed254cd7494d0bf/praisonaiagents-0.11.8-py3-none-any.whl", hash = "sha256:2c5b74d1bbe7f8f835ca698748103f5d7ab97fca2b57ec40eaec597df620e888", size = 977507, upload-time = "2026-01-13T04:11:29.481Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user