Compare commits

...
2 Commits
Author SHA1 Message Date
Mervin Praison 5d67f0fdcf Release v3.9.3 2026-01-16 05:23:44 +00:00
github-actions[bot] fb93b7d6d5 docs: auto-update api.md [skip ci] 2026-01-16 04:38:07 +00:00
10 changed files with 307 additions and 237 deletions

No files matched your search

+4
View File
@@ -148,6 +148,10 @@ Methods:
* <code title="cli">praisonai agents info <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <code title="cli">praisonai agents list <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <code title="cli">praisonai audit agent-centric <a href="./src/praisonai/praisonai/cli/commands/audit.py">--help</a></code>
* <code title="cli">praisonai batch batch-run <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch list <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch report <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai batch stats <a href="./src/praisonai/praisonai/cli/commands/batch.py">--help</a></code>
* <code title="cli">praisonai benchmark agent <a href="./src/praisonai/praisonai/cli/commands/benchmark.py">--help</a></code>
* <code title="cli">praisonai benchmark benchmark-callback <a href="./src/praisonai/praisonai/cli/commands/benchmark.py">--help</a></code>
* <code title="cli">praisonai benchmark cli <a href="./src/praisonai/praisonai/cli/commands/benchmark.py">--help</a></code>
+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>=3.9.2" \
"praisonai>=3.9.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>=3.9.2" \
"praisonai>=3.9.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>=3.9.2" \
"praisonai>=3.9.3" \
"praisonai[ui]" \
"praisonai[crewai]"
@@ -1,155 +1,155 @@
{
"timestamp": "2025-12-31T18:45:38.187539Z",
"timestamp": "2026-01-16T05:23:04.919943Z",
"metadata": {
"python_version": "3.12.11",
"platform": "macOS-15.7.4-arm64-arm-64bit",
"praisonai_version": "2.9.2",
"timestamp": "2025-12-31T18:45:38.226558Z"
"platform": "macOS-26.3-arm64-arm-64bit",
"praisonai_version": "3.9.2",
"timestamp": "2026-01-16T05:23:04.932474Z"
},
"startup": {
"cold_ms": 80.9305000002496,
"warm_ms": 55.266541996388696
"cold_ms": 27.866583000104583,
"warm_ms": 27.75670800019725
},
"import_analysis": [
{
"module": "praisonaiagents",
"self_us": 2419,
"cumulative_us": 1645390,
"self_ms": 2.419,
"cumulative_ms": 1645.39
"self_us": 403,
"cumulative_us": 188329,
"self_ms": 0.403,
"cumulative_ms": 188.329
},
{
"module": "praisonaiagents.workflows",
"self_us": 670,
"cumulative_us": 1375919,
"self_ms": 0.67,
"cumulative_ms": 1375.919
"self_us": 106,
"cumulative_us": 112180,
"self_ms": 0.106,
"cumulative_ms": 112.18
},
{
"module": "praisonaiagents.workflows.yaml_parser",
"self_us": 775,
"cumulative_us": 1369435,
"self_ms": 0.775,
"cumulative_ms": 1369.435
"self_us": 217,
"cumulative_us": 107210,
"self_ms": 0.217,
"cumulative_ms": 107.21
},
{
"module": "praisonaiagents.agent.agent",
"self_us": 19,
"cumulative_us": 1349174,
"self_ms": 0.019,
"cumulative_ms": 1349.174
"self_us": 15,
"cumulative_us": 98811,
"self_ms": 0.015,
"cumulative_ms": 98.811
},
{
"module": "praisonaiagents.agent",
"self_us": 537,
"cumulative_us": 1349155,
"self_ms": 0.537,
"cumulative_ms": 1349.155
"self_us": 146,
"cumulative_us": 98796,
"self_ms": 0.146,
"cumulative_ms": 98.796
},
{
"module": "praisonaiagents.agent.agent",
"self_us": 1453,
"cumulative_us": 1285299,
"self_ms": 1.453,
"cumulative_ms": 1285.299
},
{
"module": "praisonaiagents.llm.openai_client",
"self_us": 7887,
"cumulative_us": 1123578,
"self_ms": 7.887,
"cumulative_ms": 1123.578
},
{
"module": "openai",
"self_us": 1083,
"cumulative_us": 1115692,
"self_ms": 1.083,
"cumulative_ms": 1115.692
},
{
"module": "openai.types",
"self_us": 2606,
"cumulative_us": 957990,
"self_ms": 2.606,
"cumulative_ms": 957.99
},
{
"module": "openai.types.batch",
"self_us": 1787,
"cumulative_us": 485667,
"self_ms": 1.787,
"cumulative_ms": 485.667
},
{
"module": "openai._models",
"self_us": 13423,
"cumulative_us": 450798,
"self_ms": 13.423,
"cumulative_ms": 450.798
},
{
"module": "openai.types.eval_create_params",
"self_us": 644,
"cumulative_us": 393275,
"self_ms": 0.644,
"cumulative_ms": 393.275
},
{
"module": "openai.types.graders.grader_inputs_param",
"self_us": 17,
"cumulative_us": 359515,
"self_ms": 0.017,
"cumulative_ms": 359.515
},
{
"module": "openai.types.graders",
"self_us": 392,
"cumulative_us": 359498,
"self_ms": 0.392,
"cumulative_ms": 359.498
},
{
"module": "openai.types.graders.multi_grader",
"self_us": 3847,
"cumulative_us": 351251,
"self_ms": 3.847,
"cumulative_ms": 351.251
},
{
"module": "openai.types.graders.label_model_grader",
"self_us": 5450,
"cumulative_us": 337291,
"self_ms": 5.45,
"cumulative_ms": 337.291
},
{
"module": "openai.types.graders.grader_inputs",
"self_us": 545,
"cumulative_us": 331841,
"self_ms": 0.545,
"cumulative_ms": 331.841
},
{
"module": "openai.types.responses.response_input_text",
"self_us": 21,
"cumulative_us": 331297,
"self_ms": 0.021,
"cumulative_ms": 331.297
},
{
"module": "openai.types.responses",
"self_us": 1301,
"cumulative_us": 331277,
"self_ms": 1.301,
"cumulative_ms": 331.277
"self_us": 1027,
"cumulative_us": 86223,
"self_ms": 1.027,
"cumulative_ms": 86.223
},
{
"module": "rich.logging",
"self_us": 1066,
"cumulative_us": 238256,
"self_ms": 1.066,
"cumulative_ms": 238.256
"self_us": 282,
"cumulative_us": 53009,
"self_ms": 0.282,
"cumulative_ms": 53.009
},
{
"module": "praisonaiagents.main",
"self_us": 18784,
"cumulative_us": 45759,
"self_ms": 18.784,
"cumulative_ms": 45.759
},
{
"module": "praisonaiagents.llm.openai_client",
"self_us": 2470,
"cumulative_us": 28251,
"self_ms": 2.47,
"cumulative_ms": 28.251
},
{
"module": "rich.traceback",
"self_us": 1478,
"cumulative_us": 18788,
"self_ms": 1.478,
"cumulative_ms": 18.788
},
{
"module": "rich.console",
"self_us": 1737,
"cumulative_us": 18587,
"self_ms": 1.737,
"cumulative_ms": 18.587
},
{
"module": "pygments.lexers",
"self_us": 242,
"cumulative_us": 15427,
"self_ms": 0.242,
"cumulative_ms": 15.427
},
{
"module": "rich.markdown",
"self_us": 409,
"cumulative_us": 13987,
"self_ms": 0.409,
"cumulative_ms": 13.987
},
{
"module": "pygments.plugin",
"self_us": 85,
"cumulative_us": 13503,
"self_ms": 0.085,
"cumulative_ms": 13.503
},
{
"module": "importlib.metadata",
"self_us": 637,
"cumulative_us": 13419,
"self_ms": 0.637,
"cumulative_ms": 13.419
},
{
"module": "rich._log_render",
"self_us": 145,
"cumulative_us": 13380,
"self_ms": 0.145,
"cumulative_ms": 13.38
},
{
"module": "markdown_it",
"self_us": 172,
"cumulative_us": 13339,
"self_ms": 0.172,
"cumulative_ms": 13.339
},
{
"module": "rich.text",
"self_us": 582,
"cumulative_us": 13236,
"self_ms": 0.582,
"cumulative_ms": 13.236
},
{
"module": "markdown_it.main",
"self_us": 298,
"cumulative_us": 13168,
"self_ms": 0.298,
"cumulative_ms": 13.168
},
{
"module": "pydantic",
"self_us": 223,
"cumulative_us": 11119,
"self_ms": 0.223,
"cumulative_ms": 11.119
}
],
"scenarios": [
@@ -162,28 +162,22 @@
"iterations": 2
},
"import_time_stats": {
"mean": 0.008833496394800022,
"min": 0.005916997906751931,
"max": 0.011749994882848114,
"stdev": 0.0041245517164382375,
"median": 0.008833496394800022
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"total_time_stats": {
"mean": 1727.4090415012324,
"min": 1367.2264170018025,
"max": 2087.5916660006624,
"stdev": 509.3751524982296,
"median": 1727.4090415012324
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"first_token_stats": null,
"raw_import_times_ms": [
0.011749994882848114,
0.005916997906751931
],
"raw_total_times_ms": [
1367.2264170018025,
2087.5916660006624
]
"raw_import_times_ms": [],
"raw_total_times_ms": []
},
{
"name": "simple_stream",
@@ -194,34 +188,22 @@
"iterations": 2
},
"import_time_stats": {
"mean": 0.008792001608526334,
"min": 0.006375004886649549,
"max": 0.01120899833040312,
"stdev": 0.0034181495442894613,
"median": 0.008792001608526334
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"total_time_stats": {
"mean": 2321.0982290001994,
"min": 1697.1089580038097,
"max": 2945.087499996589,
"stdev": 882.4540898183949,
"median": 2321.0982290001994
"mean": 0,
"min": 0,
"max": 0,
"stdev": 0,
"median": 0
},
"first_token_stats": {
"mean": 2312.8511880022415,
"min": 1681.2440420035273,
"max": 2944.458334000956,
"stdev": 893.2273919631452,
"median": 2312.8511880022415
},
"raw_import_times_ms": [
0.01120899833040312,
0.006375004886649549
],
"raw_total_times_ms": [
2945.087499996589,
1697.1089580038097
]
"first_token_stats": null,
"raw_import_times_ms": [],
"raw_total_times_ms": []
}
]
}
+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/v3.9.2.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.9.2.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.9.3.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.9.3.tar.gz | shasum -a 256`.split.first
license "MIT"
depends_on "[email protected]"
+73 -31
View File
@@ -482,6 +482,11 @@ def docs_run_all(
"--quiet", "-q",
help="Minimal output",
),
ci: bool = typer.Option(
False,
"--ci",
help="CI-friendly output (no colors, proper exit codes)",
),
):
"""
Run all documentation code blocks group-by-group.
@@ -495,7 +500,7 @@ def docs_run_all(
praisonai docs run-all --workers 8 --timeout 120
"""
import json
from concurrent.futures import ProcessPoolExecutor, as_completed
from concurrent.futures import ThreadPoolExecutor, as_completed
from praisonai.suite_runner import DocsSource, SuiteExecutor, RunResult
docs = docs_path or _get_default_docs_path()
@@ -509,9 +514,16 @@ def docs_run_all(
source = DocsSource(root=docs)
all_groups = sorted(source.get_groups())
typer.echo(f"Found {len(all_groups)} groups to process")
typer.echo(f"Mode: {'parallel' if parallel else 'sequential'}")
typer.echo("=" * 60)
if ci:
print(f"Found {len(all_groups)} groups to process")
print(f"Mode: {'parallel' if parallel else 'sequential'}")
print(f"Workers: {max_workers}")
print(f"Timeout: {timeout}s")
print("=" * 60)
else:
typer.echo(f"Found {len(all_groups)} groups to process")
typer.echo(f"Mode: {'parallel' if parallel else 'sequential'}")
typer.echo("=" * 60)
# Track overall results
overall_results = {
@@ -556,8 +568,8 @@ def docs_run_all(
}
if parallel and len(all_groups) > 1:
# Parallel execution
with ProcessPoolExecutor(max_workers=min(max_workers, len(all_groups))) as pool:
# Parallel execution using ThreadPoolExecutor (ProcessPoolExecutor cannot pickle nested functions)
with ThreadPoolExecutor(max_workers=min(max_workers, len(all_groups))) as pool:
futures = {pool.submit(run_group, g): g for g in all_groups}
for future in as_completed(futures):
@@ -572,13 +584,23 @@ def docs_run_all(
overall_results[key] += summary[key]
if not quiet:
typer.echo(
f"✅ {group_name}: "
f"✅{summary['passed']} ❌{summary['failed']} "
f"⏭️{summary['skipped']} ⏱️{summary['timeout']} 📝{summary['not_run']}"
)
if ci:
print(
f"PASS {group_name}: "
f"PASS:{summary['passed']} FAIL:{summary['failed']} "
f"SKIP:{summary['skipped']} TIMEOUT:{summary['timeout']} NOT_RUN:{summary['not_run']}"
)
else:
typer.echo(
f"✅ {group_name}: "
f"✅{summary['passed']} ❌{summary['failed']} "
f"⏭️{summary['skipped']} ⏱️{summary['timeout']} 📝{summary['not_run']}"
)
except Exception as e:
typer.echo(f"❌ {group_name}: Error - {e}")
if ci:
print(f"FAIL {group_name}: Error - {e}")
else:
typer.echo(f"❌ {group_name}: Error - {e}")
else:
# Sequential execution with real-time output
for group_name in all_groups:
@@ -642,25 +664,41 @@ def docs_run_all(
)
# Final report
typer.echo("\n" + "=" * 80)
typer.echo("FINAL REPORT - ALL GROUPS")
typer.echo("=" * 80)
typer.echo(f"\n{'Group':<20} {'Total':>8} {'Passed':>8} {'Failed':>8} {'Skip':>8} {'Timeout':>8} {'NotRun':>8}")
typer.echo("-" * 80)
for gs in sorted(group_summaries, key=lambda x: x['group']):
if ci:
print("")
print("=" * 80)
print("FINAL REPORT - ALL GROUPS")
print("=" * 80)
print(f"{'Group':<20} {'Total':>8} {'Passed':>8} {'Failed':>8} {'Skip':>8} {'Timeout':>8} {'NotRun':>8}")
print("-" * 80)
for gs in sorted(group_summaries, key=lambda x: x['group']):
print(
f"{gs['group']:<20} {gs['total']:>8} {gs['passed']:>8} {gs['failed']:>8} "
f"{gs['skipped']:>8} {gs['timeout']:>8} {gs['not_run']:>8}"
)
print("-" * 80)
print(
f"{'TOTAL':<20} {overall_results['total']:>8} {overall_results['passed']:>8} "
f"{overall_results['failed']:>8} {overall_results['skipped']:>8} "
f"{overall_results['timeout']:>8} {overall_results['not_run']:>8}"
)
else:
typer.echo("\n" + "=" * 80)
typer.echo("FINAL REPORT - ALL GROUPS")
typer.echo("=" * 80)
typer.echo(f"\n{'Group':<20} {'Total':>8} {'Passed':>8} {'Failed':>8} {'Skip':>8} {'Timeout':>8} {'NotRun':>8}")
typer.echo("-" * 80)
for gs in sorted(group_summaries, key=lambda x: x['group']):
typer.echo(
f"{gs['group']:<20} {gs['total']:>8} {gs['passed']:>8} {gs['failed']:>8} "
f"{gs['skipped']:>8} {gs['timeout']:>8} {gs['not_run']:>8}"
)
typer.echo("-" * 80)
typer.echo(
f"{gs['group']:<20} {gs['total']:>8} {gs['passed']:>8} {gs['failed']:>8} "
f"{gs['skipped']:>8} {gs['timeout']:>8} {gs['not_run']:>8}"
f"{'TOTAL':<20} {overall_results['total']:>8} {overall_results['passed']:>8} "
f"{overall_results['failed']:>8} {overall_results['skipped']:>8} "
f"{overall_results['timeout']:>8} {overall_results['not_run']:>8}"
)
typer.echo("-" * 80)
typer.echo(
f"{'TOTAL':<20} {overall_results['total']:>8} {overall_results['passed']:>8} "
f"{overall_results['failed']:>8} {overall_results['skipped']:>8} "
f"{overall_results['timeout']:>8} {overall_results['not_run']:>8}"
)
# Save final summary
output_dir.mkdir(parents=True, exist_ok=True)
@@ -672,8 +710,12 @@ def docs_run_all(
'groups': group_summaries,
}, f, indent=2)
typer.echo(f"\n📁 Final summary saved to: {summary_path}")
typer.echo(f"📁 Individual group reports in: {output_dir}")
if ci:
print(f"Final summary: {summary_path}")
print(f"Group reports: {output_dir}")
else:
typer.echo(f"\n📁 Final summary saved to: {summary_path}")
typer.echo(f"📁 Individual group reports in: {output_dir}")
# Exit code
if overall_results['failed'] > 0 or overall_results['timeout'] > 0:
@@ -492,6 +492,11 @@ def examples_run_all(
"--quiet", "-q",
help="Minimal output",
),
ci: bool = typer.Option(
False,
"--ci",
help="CI-friendly output (no colors, proper exit codes)",
),
):
"""
Run all examples group-by-group.
@@ -505,7 +510,7 @@ def examples_run_all(
praisonai examples run-all --workers 8 --timeout 120
"""
import json
from concurrent.futures import ProcessPoolExecutor, as_completed
from concurrent.futures import ThreadPoolExecutor, as_completed
from praisonai.suite_runner import ExamplesSource, SuiteExecutor, RunResult
examples_path = path or _get_default_examples_path()
@@ -519,9 +524,16 @@ def examples_run_all(
source = ExamplesSource(root=examples_path)
all_groups = sorted(source.get_groups())
typer.echo(f"Found {len(all_groups)} groups to process")
typer.echo(f"Mode: {'parallel' if parallel else 'sequential'}")
typer.echo("=" * 60)
if ci:
print(f"Found {len(all_groups)} groups to process")
print(f"Mode: {'parallel' if parallel else 'sequential'}")
print(f"Workers: {max_workers}")
print(f"Timeout: {timeout}s")
print("=" * 60)
else:
typer.echo(f"Found {len(all_groups)} groups to process")
typer.echo(f"Mode: {'parallel' if parallel else 'sequential'}")
typer.echo("=" * 60)
# Track overall results
overall_results = {
@@ -566,8 +578,8 @@ def examples_run_all(
}
if parallel and len(all_groups) > 1:
# Parallel execution
with ProcessPoolExecutor(max_workers=min(max_workers, len(all_groups))) as pool:
# Parallel execution using ThreadPoolExecutor (ProcessPoolExecutor cannot pickle nested functions)
with ThreadPoolExecutor(max_workers=min(max_workers, len(all_groups))) as pool:
futures = {pool.submit(run_group, g): g for g in all_groups}
for future in as_completed(futures):
@@ -582,13 +594,23 @@ def examples_run_all(
overall_results[key] += summary[key]
if not quiet:
typer.echo(
f"✅ {group_name}: "
f"✅{summary['passed']} ❌{summary['failed']} "
f"⏭️{summary['skipped']} ⏱️{summary['timeout']} ⚠️{summary.get('xfail', 0)}"
)
if ci:
print(
f"PASS {group_name}: "
f"PASS:{summary['passed']} FAIL:{summary['failed']} "
f"SKIP:{summary['skipped']} TIMEOUT:{summary['timeout']} XFAIL:{summary.get('xfail', 0)}"
)
else:
typer.echo(
f"✅ {group_name}: "
f"✅{summary['passed']} ❌{summary['failed']} "
f"⏭️{summary['skipped']} ⏱️{summary['timeout']} ⚠️{summary.get('xfail', 0)}"
)
except Exception as e:
typer.echo(f"❌ {group_name}: Error - {e}")
if ci:
print(f"FAIL {group_name}: Error - {e}")
else:
typer.echo(f"❌ {group_name}: Error - {e}")
else:
# Sequential execution with real-time output
for group_name in all_groups:
@@ -652,25 +674,41 @@ def examples_run_all(
)
# Final report
typer.echo("\n" + "=" * 80)
typer.echo("FINAL REPORT - ALL GROUPS")
typer.echo("=" * 80)
typer.echo(f"\n{'Group':<20} {'Total':>8} {'Passed':>8} {'Failed':>8} {'Skip':>8} {'Timeout':>8} {'XFail':>8}")
typer.echo("-" * 80)
for gs in sorted(group_summaries, key=lambda x: x['group']):
if ci:
print("")
print("=" * 80)
print("FINAL REPORT - ALL GROUPS")
print("=" * 80)
print(f"{'Group':<20} {'Total':>8} {'Passed':>8} {'Failed':>8} {'Skip':>8} {'Timeout':>8} {'XFail':>8}")
print("-" * 80)
for gs in sorted(group_summaries, key=lambda x: x['group']):
print(
f"{gs['group']:<20} {gs['total']:>8} {gs['passed']:>8} {gs['failed']:>8} "
f"{gs['skipped']:>8} {gs['timeout']:>8} {gs.get('xfail', 0):>8}"
)
print("-" * 80)
print(
f"{'TOTAL':<20} {overall_results['total']:>8} {overall_results['passed']:>8} "
f"{overall_results['failed']:>8} {overall_results['skipped']:>8} "
f"{overall_results['timeout']:>8} {overall_results.get('xfail', 0):>8}"
)
else:
typer.echo("\n" + "=" * 80)
typer.echo("FINAL REPORT - ALL GROUPS")
typer.echo("=" * 80)
typer.echo(f"\n{'Group':<20} {'Total':>8} {'Passed':>8} {'Failed':>8} {'Skip':>8} {'Timeout':>8} {'XFail':>8}")
typer.echo("-" * 80)
for gs in sorted(group_summaries, key=lambda x: x['group']):
typer.echo(
f"{gs['group']:<20} {gs['total']:>8} {gs['passed']:>8} {gs['failed']:>8} "
f"{gs['skipped']:>8} {gs['timeout']:>8} {gs.get('xfail', 0):>8}"
)
typer.echo("-" * 80)
typer.echo(
f"{gs['group']:<20} {gs['total']:>8} {gs['passed']:>8} {gs['failed']:>8} "
f"{gs['skipped']:>8} {gs['timeout']:>8} {gs.get('xfail', 0):>8}"
f"{'TOTAL':<20} {overall_results['total']:>8} {overall_results['passed']:>8} "
f"{overall_results['failed']:>8} {overall_results['skipped']:>8} "
f"{overall_results['timeout']:>8} {overall_results.get('xfail', 0):>8}"
)
typer.echo("-" * 80)
typer.echo(
f"{'TOTAL':<20} {overall_results['total']:>8} {overall_results['passed']:>8} "
f"{overall_results['failed']:>8} {overall_results['skipped']:>8} "
f"{overall_results['timeout']:>8} {overall_results.get('xfail', 0):>8}"
)
# Save final summary
output_dir.mkdir(parents=True, exist_ok=True)
@@ -682,8 +720,12 @@ def examples_run_all(
'groups': group_summaries,
}, f, indent=2)
typer.echo(f"\n📁 Final summary saved to: {summary_path}")
typer.echo(f"📁 Individual group reports in: {output_dir}")
if ci:
print(f"Final summary: {summary_path}")
print(f"Group reports: {output_dir}")
else:
typer.echo(f"\n📁 Final summary saved to: {summary_path}")
typer.echo(f"📁 Individual group reports in: {output_dir}")
# Exit code
if overall_results['failed'] > 0 or overall_results['timeout'] > 0:
+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==3.9.2 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==3.9.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__ = "3.9.2"
__version__ = "3.9.3"