Compare commits

...
3 Commits
Author SHA1 Message Date
Mervin Praison 79b2b88f72 Release v2.5.3 2025-12-26 05:54:09 +00:00
Mervin Praison f5dfbd5670 fix: resolve Core Tests failures in knowledge/reranker integration
- Fix #1: MemoryConfig Pydantic validation - add _prepare_mem0_config() to strip
  PraisonAI-specific reranker fields (enabled, default_rerank) before passing to
  mem0, which only accepts provider and config fields

- Fix #2: LLM Reranker 'Chat' object not callable - check if client.chat is
  callable, not just if it exists. OpenAI client has chat as namespace object.
  Also handle max_completion_tokens vs max_tokens for different model versions.

- Fix #3: Chroma vector store metadata validation - ensure non-empty metadata
  dicts by adding placeholder key when metadata is empty or None

- Fix test file issues: add user_id parameter (required by mem0 API), handle
  dict return type from knowledge.search, use gpt-4o-mini for reranker test
  (gpt-5-nano returns empty responses with max_completion_tokens=10)
2025-12-26 05:52:46 +00:00
Mervin Praison aa6aa5933d fix: update test files to use gpt-5-nano for cheaper CI runs
- Update test_workflow_patterns.py to use gpt-5-nano instead of gpt-4o/gpt-4o-mini
- Also set OPENAI_MODEL_NAME=gpt-5-nano as CI secret
2025-12-26 05:26:40 +00:00
15 changed files with 104 additions and 46 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 \
praisonai_tools \
"praisonai>=2.5.2" \
"praisonai>=2.5.3" \
"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.5.2" \
"praisonai>=2.5.3" \
"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.5.2" \
"praisonai>=2.5.3" \
"praisonai[ui]" \
"praisonai[crewai]"
@@ -500,6 +500,33 @@ class Knowledge:
base_config["graph_store"] = self._config["graph_store"]
return base_config
def _prepare_mem0_config(self, config):
"""Prepare config for mem0 by removing PraisonAI-specific fields.
mem0's RerankerConfig only accepts 'provider' and 'config' fields.
PraisonAI adds 'enabled' and 'default_rerank' for internal use.
"""
mem0_config = config.copy()
# Strip PraisonAI-specific reranker fields that mem0 doesn't accept
if "reranker" in mem0_config:
reranker = mem0_config["reranker"]
if isinstance(reranker, dict):
# Keep only mem0-compatible fields: provider, config
mem0_reranker = {}
if "provider" in reranker:
mem0_reranker["provider"] = reranker["provider"]
if "config" in reranker:
mem0_reranker["config"] = reranker["config"]
# If no valid mem0 fields, remove reranker entirely
if mem0_reranker:
mem0_config["reranker"] = mem0_reranker
else:
del mem0_config["reranker"]
return mem0_config
@cached_property
def memory(self):
# Check if MongoDB provider is specified
@@ -511,19 +538,22 @@ class Knowledge:
# Fall back to default memory
pass
# Prepare config for mem0 (strip PraisonAI-specific fields)
mem0_config = self._prepare_mem0_config(self.config)
# Default Mem0 memory
try:
return CustomMemory.from_config(self.config)
return CustomMemory.from_config(mem0_config)
except (NotImplementedError, ValueError) as e:
if "list_collections" in str(e) or "Extra fields not allowed" in str(e):
# Keep only allowed fields
vector_store_config = {
"collection_name": self.config["vector_store"]["config"]["collection_name"],
"path": self.config["vector_store"]["config"]["path"]
"collection_name": mem0_config["vector_store"]["config"]["collection_name"],
"path": mem0_config["vector_store"]["config"]["path"]
}
self.config["vector_store"]["config"] = vector_store_config
mem0_config["vector_store"]["config"] = vector_store_config
from mem0 import Memory
return Memory.from_config(self.config)
return Memory.from_config(mem0_config)
raise
@cached_property
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.3.2"
version = "0.3.3"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
readme = "README.md"
requires-python = ">=3.10"
@@ -1189,16 +1189,16 @@ class TestWorkflowConfiguration:
"""Test that workflow planning setting is stored."""
def step1(ctx): return StepResult(output="Done")
workflow = Workflow(steps=[step1], planning=True, planning_llm="gpt-4o")
workflow = Workflow(steps=[step1], planning=True, planning_llm="gpt-5-nano")
assert workflow.planning == True
assert workflow.planning_llm == "gpt-4o"
assert workflow.planning_llm == "gpt-5-nano"
def test_workflow_default_llm(self):
"""Test default LLM setting."""
def step1(ctx): return StepResult(output="Done")
workflow = Workflow(steps=[step1], default_llm="gpt-4o-mini")
assert workflow.default_llm == "gpt-4o-mini"
workflow = Workflow(steps=[step1], default_llm="gpt-5-nano")
assert workflow.default_llm == "gpt-5-nano"
def test_workflow_default_agent_config(self):
"""Test default agent config."""
@@ -1393,10 +1393,10 @@ class TestPlanningAndReasoning:
workflow = Workflow(
steps=[lambda ctx: StepResult(output="test")],
planning=True,
planning_llm="gpt-4o"
planning_llm="gpt-5-nano"
)
assert workflow.planning == True
assert workflow.planning_llm == "gpt-4o"
assert workflow.planning_llm == "gpt-5-nano"
def test_workflow_reasoning_parameter_exists(self):
"""Test that Workflow accepts reasoning parameter."""
+1 -1
View File
@@ -2422,7 +2422,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.3.2"
version = "0.3.3"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
+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.5.2.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.5.2.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.5.3.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.5.3.tar.gz | shasum -a 256`.split.first
license "MIT"
depends_on "[email protected]"
+26 -10
View File
@@ -131,18 +131,34 @@ Document: {document[:1000]}
Relevance score (0-10):"""
if hasattr(client, 'chat'):
# PraisonAI Agent or similar
# Check if client has a callable 'chat' method (PraisonAI Agent)
# Note: OpenAI client has 'chat' attribute but it's a namespace, not callable
if hasattr(client, 'chat') and callable(getattr(client, 'chat', None)):
# PraisonAI Agent or similar with callable chat method
response = client.chat(prompt)
else:
# OpenAI client
response = client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
max_tokens=10,
temperature=0
)
response = response.choices[0].message.content
# OpenAI client - use chat.completions.create
# Try max_completion_tokens first (newer models), then max_tokens (older)
# Note: Some models (e.g., gpt-5-nano) don't support temperature=0
api_error = None
for token_param in [{"max_completion_tokens": 10}, {"max_tokens": 10}]:
try:
response = client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
**token_param
)
response = response.choices[0].message.content
api_error = None
break
except Exception as e:
api_error = e
err_str = str(e).lower()
if "max_completion_tokens" not in err_str and "max_tokens" not in err_str:
raise # Re-raise if not a token param error
continue
if api_error:
raise api_error
# Parse score
if response:
@@ -107,7 +107,17 @@ class ChromaVectorStore:
) -> List[str]:
"""Add vectors to ChromaDB."""
ids = ids or [str(uuid_module.uuid4()) for _ in texts]
metadatas = metadatas or [{} for _ in texts]
# ChromaDB requires non-empty metadata dicts
# Ensure each metadata dict has at least one key
if metadatas is None:
metadatas = [{"_source": "praisonai"} for _ in texts]
else:
# Ensure no empty dicts - add placeholder if empty
metadatas = [
m if m else {"_source": "praisonai"}
for m in metadatas
]
# Use different collection if namespace specified
if namespace and namespace != self.namespace:
+1 -1
View File
@@ -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==2.5.2 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.5.3 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+1 -1
View File
@@ -1 +1 @@
__version__ = "2.5.2"
__version__ = "2.5.3"
+1 -1
View File
@@ -12,7 +12,7 @@ dependencies = [
"rich>=13.7",
"markdown>=3.5",
"pyparsing>=3.0.0",
"praisonaiagents>=0.3.2",
"praisonaiagents>=0.3.3",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -59,12 +59,12 @@ class TestKnowledgeIntegration:
}
})
# Add document
result = knowledge.add(test_file)
# Add document (mem0 requires user_id, agent_id, or run_id)
result = knowledge.add(test_file, user_id="test_user")
assert result is not None
# Search
results = knowledge.search("Who created Python?")
results = knowledge.search("Who created Python?", user_id="test_user")
assert len(results) > 0
@requires_openai
@@ -83,15 +83,17 @@ class TestKnowledgeIntegration:
}
})
# Store text
knowledge.store("The capital of France is Paris.")
knowledge.store("The capital of Germany is Berlin.")
# Store text (mem0 requires user_id, agent_id, or run_id)
knowledge.store("The capital of France is Paris.", user_id="test_user")
knowledge.store("The capital of Germany is Berlin.", user_id="test_user")
# Search
results = knowledge.search("What is the capital of France?")
assert len(results) > 0
results = knowledge.search("What is the capital of France?", user_id="test_user")
# Results is a dict with 'results' key containing list of memories
result_list = results.get("results", results) if isinstance(results, dict) else results
assert len(result_list) > 0
# Check that Paris is mentioned in results
result_text = str(results[0]).lower()
result_text = str(result_list[0]).lower()
assert "paris" in result_text or "france" in result_text
@@ -187,7 +189,7 @@ class TestRerankerIntegration:
except ImportError:
pytest.skip("Reranker not available")
reranker = LLMReranker(model="gpt-5-nano")
reranker = LLMReranker(model="gpt-4o-mini")
documents = [
"Python is a programming language.",
+4 -4
View File
@@ -4197,7 +4197,7 @@ requires-dist = [
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.22" },
{ name = "praisonai-tools", marker = "extra == 'autogen-v4'", specifier = ">=0.0.22" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.22" },
{ name = "praisonaiagents", specifier = ">=0.3.2" },
{ name = "praisonaiagents", specifier = ">=0.3.3" },
{ 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" },
@@ -4252,7 +4252,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.3.2"
version = "0.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
@@ -4261,9 +4261,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/30/8a/a33381c4f69e4ca1b639ad6b32d516c1adcd1d521ed78c8cdb2a947249fc/praisonaiagents-0.3.2.tar.gz", hash = "sha256:569fed4d865491cb67e7f3b6e2a603902f15bbeea1e104eb1691f634280b3155", size = 757144, upload-time = "2025-12-26T05:20:13.557Z" }
sdist = { url = "https://files.pythonhosted.org/packages/85/c2/04d0415e75c53a066ef42861c4699c0502885ab41aebe70e02f098075a83/praisonaiagents-0.3.3.tar.gz", hash = "sha256:82a6941e4bb7178ced495e4e0ae595a86e5e808b0998a96c89ebc45ce25d68db", size = 757530, upload-time = "2025-12-26T05:53:45.798Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/30/57526915881e5a9d1c3c24f6369c055df93613e4e5f64db6a3fc32579ce2/praisonaiagents-0.3.2-py3-none-any.whl", hash = "sha256:50f7b6aa0a825b16f001e7a09b7eb120b89f36b010743eca6b1ec6322d9662ca", size = 707842, upload-time = "2025-12-26T05:20:11.181Z" },
{ url = "https://files.pythonhosted.org/packages/d2/90/d9c035ebfcd341aef2f47dbc393feb902f817dd781333f5436da38449087/praisonaiagents-0.3.3-py3-none-any.whl", hash = "sha256:4dd787c9b77d159b13a2fd9043ddf21c8b16e69a708ea315e42c21e998a0d921", size = 708130, upload-time = "2025-12-26T05:53:43.826Z" },
]
[[package]]