Compare commits

..
Author SHA1 Message Date
DhivyaandDhivya-Bharathy 8536325cc1 fix(examples): phase-3 API alignment for consolidated params on v4.6.52 (#1864)
Co-authored-by: Dhivya-Bharathy <[email protected]>
2026-06-08 07:47:16 +01:00
praisonai-triage-agent[bot]andMervin Praison 8b4db44159 fix: resolve Gemini reviewer issues - remove redundant arguments and fix temp file pollution
- Remove redundant argument from workflow.run() in basic_workflow.py since variables are already set in constructor
- Fix temp file pollution by using system temp directory instead of repository directory in auto_rag_agent_basic.py and knowledge-agents-task.py

Co-authored-by: Mervin Praison <[email protected]>
2026-06-05 10:18:45 +00:00
Dhivya-Bharathy 3d3fdd045e fix(examples): align AgentTeam memory and knowledge configs with v4.6.52 API 2026-06-05 15:37:37 +05:30
praisonai-triage-agent[bot]andMervin Praison bfa9d0ca48 fix: correct TypeError in 3 example files by using proper parameter names
- Fix MemoryConfig(memory=...) → user_id=... in agent_centric_api.py
- Fix Session(memory=...) → user_id=... in session-example.py
- Fix build_context_for_task(memory=...) → user_id=... in memory-quality-example.py

Co-authored-by: Mervin Praison <[email protected]>
2026-06-05 11:26:13 +05:30
Dhivya-Bharathy 3f14324036 fix(examples): resolve syntax errors and align with consolidated-params API 2026-06-04 16:30:02 +05:30
682 changed files with 12735 additions and 86431 deletions

No files matched your search

+17 -85
View File
@@ -15,7 +15,7 @@ on:
pull_request_review:
types: [submitted]
pull_request:
types: [opened, synchronize]
types: [opened]
env:
MAX_POLL_ATTEMPTS: 20
@@ -143,12 +143,9 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo
});
const alreadyPosted = comments.data.some(c => {
const body = (c.body || '').toLowerCase();
if (!body.includes('@claude') || !triggerLogins.includes(c.user.login)) return false;
if (body.includes('merge conflict')) return false;
return body.includes('final architecture reviewer') || body.includes('lead engineer');
});
const alreadyPosted = comments.data.some(c =>
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
);
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
core.setOutput('comments_count', comments.data.length.toString());
@@ -189,7 +186,7 @@ jobs:
const claudeBody = '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. ' + reviewSummary + '.\n\n' +
'Review Context: ' + comments.data.length + ' total comments, ' +
(reviews.coderabbit.length + reviews.qodo.length + reviews.gemini.length + reviews.copilot.length + reviews.greptile.length) + ' bot reviews.\n\n' +
'Phase 1: Review per AGENTS.md principles (protocol-driven, backward compatible, no perf impact, SDK value gate — no feature bloat).\n' +
'Phase 1: Review per AGENTS.md principles (protocol-driven, backward compatible, no perf impact).\n' +
'Phase 2: FIX valid issues found by prior reviewers.\n' +
'Phase 3: Provide final verdict (approve or request changes).';
await github.rest.issues.createComment({issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: claudeBody});
@@ -298,12 +295,9 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo
});
const alreadyPosted = comments.data.some(c => {
const body = (c.body || '').toLowerCase();
if (!body.includes('@claude') || !triggerLogins.includes(c.user.login)) return false;
if (body.includes('merge conflict')) return false;
return body.includes('final architecture reviewer') || body.includes('lead engineer');
});
const alreadyPosted = comments.data.some(c =>
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
);
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
- name: Trigger Claude final review
@@ -319,7 +313,7 @@ jobs:
issue_number: pr,
owner: context.repo.owner,
repo: context.repo.repo,
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep\n\n**Phase 2: FIX Valid Issues**\n5. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n6. Push all code fixes directly to THIS branch (do NOT create a new PR)\n7. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n8. If all issues are resolved, approve the PR / close the Issue\n9. If blocking issues remain, request changes / leave clear action items'
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
});
console.log(`Claude triggered on PR #${pr}`);
@@ -405,12 +399,9 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo
});
const alreadyPosted = comments.data.some(c => {
const body = (c.body || '').toLowerCase();
if (!body.includes('@claude') || !triggerLogins.includes(c.user.login)) return false;
if (body.includes('merge conflict')) return false;
return body.includes('final architecture reviewer') || body.includes('lead engineer');
});
const alreadyPosted = comments.data.some(c =>
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
);
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
- name: Trigger Claude final review
@@ -426,7 +417,7 @@ jobs:
issue_number: pr,
owner: context.repo.owner,
repo: context.repo.repo,
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep\n\n**Phase 2: FIX Valid Issues**\n5. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n6. Push all code fixes directly to THIS branch (do NOT create a new PR)\n7. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n8. If all issues are resolved, approve the PR / close the Issue\n9. If blocking issues remain, request changes / leave clear action items'
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
});
console.log(`Claude triggered on PR #${pr}`);
@@ -462,12 +453,9 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo
});
const alreadyPosted = comments.data.some(c => {
const body = (c.body || '').toLowerCase();
if (!body.includes('@claude') || !triggerLogins.includes(c.user.login)) return false;
if (body.includes('merge conflict')) return false;
return body.includes('final architecture reviewer') || body.includes('lead engineer');
});
const alreadyPosted = comments.data.some(c =>
c.body.includes('@claude') && triggerLogins.includes(c.user.login)
);
core.setOutput('already_triggered', alreadyPosted ? 'true' : 'false');
- name: Post Claude analysis request (Lead Engineer)
@@ -480,65 +468,9 @@ jobs:
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '@claude You are the Lead Engineer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL analysis and reviews above carefully (Gemini, CodeRabbit, Qodo, Copilot, etc).\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep\n\n**Phase 2: FIX Valid Issues**\n5. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n6. Push all code fixes directly to THIS branch (do NOT create a new PR)\n7. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n8. If all issues are resolved, approve the PR / close the Issue\n9. If blocking issues remain, request changes / leave clear action items'
body: '@claude You are the Lead Engineer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL analysis and reviews above carefully (Gemini, CodeRabbit, Qodo, Copilot, etc).\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n\n**Phase 2: FIX Valid Issues**\n4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n5. Push all code fixes directly to THIS branch (do NOT create a new PR)\n6. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n7. If all issues are resolved, approve the PR / close the Issue\n8. If blocking issues remain, request changes / leave clear action items'
});
# ================================================================
# Recovery: post FINAL @claude if merge-conflict comment blocked the chain
# (e.g. conflict fired before claude-after-copilot could post review prompt)
# ================================================================
claude-review-recovery:
if: |
github.event_name == 'pull_request' &&
github.event.action == 'synchronize'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: read
steps:
- name: Post missing FINAL Claude review after rebase
uses: actions/github-script@v7
env:
TRIGGER_LOGINS: ${{ env.CLAUDE_TRIGGER_LOGINS }}
with:
github-token: ${{ secrets.GH_TOKEN }}
script: |
const triggerLogins = JSON.parse(process.env.TRIGGER_LOGINS);
const pr = context.payload.pull_request.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
const { data: comments } = await github.rest.issues.listComments({
issue_number: pr, owner, repo, per_page: 100,
});
const hasFinalReview = comments.some(c => {
const body = (c.body || '').toLowerCase();
if (!body.includes('@claude') || !triggerLogins.includes(c.user.login)) return false;
if (body.includes('merge conflict')) return false;
return body.includes('final architecture reviewer') || body.includes('lead engineer');
});
if (hasFinalReview) {
core.info('FINAL Claude review already posted, skipping recovery');
return;
}
const hasReviewerActivity = comments.some(c => {
const login = c.user.login.toLowerCase();
return login.includes('coderabbit') || login.includes('copilot') ||
login.includes('greptile') || login.includes('qodo') || login.includes('gemini');
});
if (!hasReviewerActivity) {
core.info('No bot reviewer activity yet, skipping recovery');
return;
}
await github.rest.issues.createComment({
issue_number: pr, owner, repo,
body: '@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.\n\n**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions\n4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep\n\n**Phase 2: FIX Valid Issues**\n5. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n6. Push all code fixes directly to THIS branch (do NOT create a new PR)\n7. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n8. If all issues are resolved, approve the PR / close the Issue\n9. If blocking issues remain, request changes / leave clear action items'
});
core.info(`Posted recovery FINAL Claude review on PR #${pr}`);
# ================================================================
# BOT PRs: CodeRabbit/Qodo skip bot-authored PRs by default.
# Trigger them explicitly. Do NOT trigger Copilot here — it will
+39 -80
View File
@@ -151,67 +151,26 @@ jobs:
id: check_fork
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const isPR = context.eventName === 'pull_request'
|| context.eventName === 'pull_request_review'
|| context.eventName === 'pull_request_review_comment'
|| (context.payload.issue && context.payload.issue.pull_request);
if (!isPR) {
core.setOutput('is_pr', 'false');
core.setOutput('is_fork', 'false');
core.setOutput('can_push_fork', 'false');
return;
}
const prNumber = context.payload.pull_request
? context.payload.pull_request.number
: context.payload.issue.number;
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
let mergeState = '';
try {
const gql = await github.graphql(
`query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) { mergeStateStatus }
}
}`,
{ owner: context.repo.owner, repo: context.repo.repo, number: prNumber }
);
mergeState = (gql.repository.pullRequest.mergeStateStatus || '').toUpperCase();
} catch (e) {
core.warning(`GraphQL mergeStateStatus failed: ${e.message}`);
}
if (!mergeState) {
mergeState = (pr.data.mergeable_state || pr.data.merge_state_status || '').toUpperCase();
}
core.setOutput('is_pr', 'true');
core.setOutput('pr_number', String(prNumber));
core.setOutput('pr_branch', pr.data.head.ref);
core.setOutput('head_repo', pr.data.head.repo.full_name);
core.setOutput('merge_state', mergeState);
const baseRepo = `${context.repo.owner}/${context.repo.repo}`;
const maintainerCanModify = pr.data.maintainer_can_modify === true;
core.setOutput('maintainer_can_modify', String(maintainerCanModify));
if (pr.data.head.repo && pr.data.head.repo.full_name !== baseRepo) {
core.setOutput('is_fork', 'true');
core.setOutput('can_push_fork', String(maintainerCanModify));
core.setOutput('fork_repo', pr.data.head.repo.full_name);
core.setOutput('clone_url', pr.data.head.repo.clone_url);
core.setOutput('branch', pr.data.head.ref);
return;
const isPR = context.eventName === 'pull_request' || (context.payload.issue && context.payload.issue.pull_request);
if (isPR) {
const prNumber = context.payload.pull_request ? context.payload.pull_request.number : context.payload.issue.number;
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
core.setOutput('pr_branch', pr.data.head.ref);
if (pr.data.head.repo && pr.data.head.repo.full_name !== context.repo.full_name) {
core.setOutput('is_fork', 'true');
core.setOutput('clone_url', pr.data.head.repo.clone_url);
core.setOutput('branch', pr.data.head.ref);
return;
}
}
core.setOutput('is_fork', 'false');
core.setOutput('can_push_fork', 'false');
- name: Checkout repository
uses: actions/checkout@v4
@@ -221,40 +180,31 @@ jobs:
token: ${{ steps.app-token.outputs.token }}
- name: Fetch PR branch and Setup Remote
if: steps.check_fork.outputs.is_pr == 'true'
if: github.event.issue.pull_request
env:
PR_BRANCH: ${{ steps.check_fork.outputs.pr_branch }}
PR_NUMBER: ${{ steps.check_fork.outputs.pr_number }}
IS_FORK: ${{ steps.check_fork.outputs.is_fork }}
CAN_PUSH_FORK: ${{ steps.check_fork.outputs.can_push_fork }}
FORK_REPO: ${{ steps.check_fork.outputs.fork_repo }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
git fetch origin "pull/${PR_NUMBER}/head:${PR_BRANCH}"
git checkout "${PR_BRANCH}"
git fetch origin main
if [ "${IS_FORK}" = "true" ] && [ "${CAN_PUSH_FORK}" = "true" ]; then
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${FORK_REPO}.git"
elif [ "${IS_FORK}" = "true" ]; then
# Block push: trick claude-code-action's `git fetch origin <branch>`
git remote set-url origin "file://$(pwd)"
# Fetch PR head from base repo and put it into local branch
git fetch origin pull/${{ github.event.issue.number }}/head:"${PR_BRANCH}"
# If it's a fork, make origin point to local to trick claude-code-action's `git fetch origin <branch>`
if [ "${IS_FORK}" = "true" ]; then
git remote set-url origin file://$(pwd)
fi
- uses: anthropics/claude-code-action@beta
env:
GH_TOKEN: ${{ steps.check_fork.outputs.can_push_fork == 'true' && secrets.GH_TOKEN || steps.app-token.outputs.token }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
with:
allowed_bots: 'praisonai-triage-agent[bot]'
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ steps.app-token.outputs.token }}
model: claude-opus-4-8
trigger_phrase: "@claude"
label_trigger: "claude"
direct_prompt: |
${{ steps.check_fork.outputs.is_fork == 'true' && steps.check_fork.outputs.can_push_fork != 'true' && 'CRITICAL: THIS IS A PULL REQUEST FROM A FORK WITHOUT MAINTAINER EDITS. YOU DO NOT HAVE PUSH PERMISSIONS. ONLY READ FILES, VALIDATE CODE, AND PROVIDE COMMENTS/FEEDBACK ON THIS PR. DO NOT ATTEMPT TO PUSH OR CREATE PULL REQUESTS.\n\n' || (steps.check_fork.outputs.can_push_fork == 'true' && format('NOTE: Fork PR with maintainer edits allowed. Push directly to fork {0} branch {1}.\n\n', steps.check_fork.outputs.fork_repo, steps.check_fork.outputs.pr_branch) || (steps.check_fork.outputs.is_pr == 'true' && steps.check_fork.outputs.is_fork != 'true' && 'NOTE: The branch is under MervinPraison/PraisonAI (not a fork). You are able to make modifications and push directly to this branch.\n\n' || (steps.check_fork.outputs.is_pr != 'true' && 'NOTE: This is an issue (not a PR). Create a new fix branch per STEP 3 below.\n\n' || ''))) }}
${{ steps.check_fork.outputs.is_pr == 'true' && format('CONTEXT: PR #{0} on branch {1}. head.repo={2}, is_fork={3}, can_push_fork={4}, merge_state={5}. NEVER treat cursor/*, claude/* branch names or bot authors as forks — only is_fork=true means fork.\n\n', steps.check_fork.outputs.pr_number, steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.head_repo, steps.check_fork.outputs.is_fork, steps.check_fork.outputs.can_push_fork, steps.check_fork.outputs.merge_state) || '' }}
${{ steps.check_fork.outputs.is_fork == 'true' && 'CRITICAL: THIS IS A PULL REQUEST FROM A FORK. YOU DO NOT HAVE PUSH PERMISSIONS TO THIS REPOSITORY. ONLY READ FILES, VALIDATE CODE, AND PROVIDE YOUR COMMENTS/FEEDBACK DIRECTLY IN THIS PR VIA COMMENTS. DO NOT ATTEMPT TO PUSH OR CREATE PULL REQUESTS.\n\n' || 'NOTE: The branch is under MervinPraison/PraisonAI (not a fork). You are able to make modifications and push directly to this branch.\n\n' }}
You are working on the PraisonAI SDK. Follow AGENTS.md strictly.
STEP 0 — SETUP GIT IDENTITY & AUTH (GLOBAL — required for all repos):
@@ -283,9 +233,15 @@ jobs:
- Is there a name collision with existing exports in __init__.py?
If ANY of these conceptual checks fail (excluding routing), add a comment to the issue explaining why and close it. Do NOT create a PR.
${{ steps.check_fork.outputs.is_pr == 'true' && (steps.check_fork.outputs.is_fork != 'true' || steps.check_fork.outputs.can_push_fork == 'true') && steps.check_fork.outputs.merge_state == 'DIRTY' && format('STEP 3 — RESOLVE MERGE CONFLICTS ON PR (branch: {0}, PR #{1}{2}):\n- DO NOT create a new branch or PR\n- git fetch origin main && git rebase origin/main\n- Resolve conflicts: keep this PR intent, merge in main newer logic\n- git add -A && git rebase --continue (repeat until done)\n- git push --force-with-lease origin {0}\n- Run targeted pytest on changed paths\n- Comment files resolved and what you skipped\n', steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.pr_number, steps.check_fork.outputs.can_push_fork == 'true' && format(', fork: {0}', steps.check_fork.outputs.fork_repo) || '') || '' }}
${{ steps.check_fork.outputs.is_pr == 'true' && (steps.check_fork.outputs.is_fork != 'true' || steps.check_fork.outputs.can_push_fork == 'true') && steps.check_fork.outputs.merge_state != 'DIRTY' && format('STEP 3 — IMPLEMENT ON EXISTING PR (branch: {0}, PR #{1}{2}):\n- DO NOT create a new branch or PR\n- git checkout {0}\n- Implement minimal, focused fixes from reviewer feedback\n- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper\n- Keep changes small and backward-compatible\n- Commit: git add -A && git commit -m "fix: <description>"\n- Push: git push origin {0}\n- Comment a summary of exact files modified and what you skipped\n', steps.check_fork.outputs.pr_branch, steps.check_fork.outputs.pr_number, steps.check_fork.outputs.can_push_fork == 'true' && format(', fork: {0}', steps.check_fork.outputs.fork_repo) || '') || '' }}
${{ steps.check_fork.outputs.is_pr != 'true' && 'STEP 3 — IMPLEMENT (for changes in THIS repo — PraisonAI):\n- Create a fix branch: git checkout -b claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)\n- Implement a minimal, focused fix\n- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper\n- Keep changes small and backward-compatible\n- Commit: git add -A && git commit -m "fix: <description> (fixes #$ISSUE_NUMBER)"\n- Push: git push origin claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)\nCRITICAL: After pushing, you MUST immediately run: gh pr create --title "fix: <title>" --body "Fixes #$ISSUE_NUMBER" --head claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)\nDo NOT stop after pushing. Do NOT say "manually create a PR". Always create the PR automatically.\n' || '' }}
STEP 3 — IMPLEMENT (for changes in THIS repo — PraisonAI):
- Create a fix branch: git checkout -b claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
- Implement a minimal, focused fix
- Follow protocol-driven design: protocols in core SDK, heavy implementations in wrapper
- Keep changes small and backward-compatible
- Commit: git add -A && git commit -m "fix: <description> (fixes #$ISSUE_NUMBER)"
- Push: git push origin claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
CRITICAL: After pushing, you MUST immediately run: gh pr create --title "fix: <title>" --body "Fixes #$ISSUE_NUMBER" --head claude/issue-$ISSUE_NUMBER-$(date +%Y%m%d)
Do NOT stop after pushing. Do NOT say "manually create a PR". Always create the PR automatically.
STEP 3-ALT — IMPLEMENT IN EXTERNAL REPO (for PraisonAI-Tools, PraisonAIDocs, PraisonAI-Plugins):
When work must happen in a different repository, follow these steps EXACTLY. Do NOT attempt to use `cd`, as directory state is not preserved. Use `git -C` for all commands.
@@ -311,7 +267,10 @@ jobs:
- For docs changes: verify files exist and are valid markdown/MDX
- Ensure no regressions
${{ steps.check_fork.outputs.is_pr == 'true' && 'STEP 5 — SKIP: Already on an open PR. Do NOT run gh pr create.\n' || 'STEP 5 — CREATE PR:\n- Commit with descriptive message, push, and create PR using `gh pr create`\n- For external repos, use `gh pr create -R MervinPraison/<REPO_NAME>`\nCRITICAL: You MUST create the PR automatically using `gh pr create`. Do NOT just provide a link or say "manual push required".\n' }}
STEP 5 — CREATE PR:
- Commit with descriptive message, push, and create PR using `gh pr create`
- For external repos, use `gh pr create -R MervinPraison/<REPO_NAME>`
CRITICAL: You MUST create the PR automatically using `gh pr create`. Do NOT just provide a link or say "manual push required".
NOTE: If you worked on an external repo, the wrapper action will report "No commits" for the main repo at the end. This is expected, do not worry about it.
allowed_tools: |
Bash(git:*)
@@ -1,52 +0,0 @@
name: Deploy install scripts to praison.ai
# When install.sh or install.ps1 change on main, trigger praison.ai CI which
# syncs scripts at build time and deploys to Azure AKS (existing azure-pipeline.yaml).
on:
push:
branches: [main]
paths:
- 'src/praisonai/scripts/install.sh'
- 'src/praisonai/scripts/install.ps1'
workflow_dispatch:
concurrency:
group: deploy-install-scripts
cancel-in-progress: false
permissions:
contents: read
jobs:
trigger-website-deploy:
runs-on: ubuntu-latest
steps:
- name: Trigger praison.ai CI
env:
GH_TOKEN: ${{ secrets.SECRETS_ADMIN_PAT }}
SOURCE_SHA: ${{ github.sha }}
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "SECRETS_ADMIN_PAT is not configured in this repository"
exit 1
fi
gh workflow run CI \
--repo MervinPraison/praison.ai \
--ref master
echo "Triggered praison.ai CI for PraisonAI commit ${SOURCE_SHA}"
echo "Monitor: https://github.com/MervinPraison/praison.ai/actions/workflows/azure-pipeline.yaml"
sleep 10
RUN_URL=$(gh run list \
--repo MervinPraison/praison.ai \
--workflow CI \
--limit 1 \
--json url,status,createdAt \
--jq '.[0].url' 2>/dev/null || true)
if [ -n "${RUN_URL}" ]; then
echo "Latest praison.ai CI run: ${RUN_URL}"
fi
+2
View File
@@ -22,6 +22,8 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -36,6 +36,9 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Run Gemini Issue Triage
@@ -37,6 +37,9 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Set up Git
+3
View File
@@ -48,6 +48,9 @@ jobs:
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
token: ${{ steps.generate_token.outputs.token }}
ref: ${{ steps.checkout_ref.outputs.ref }}
fetch-depth: 0
+4 -4
View File
@@ -60,16 +60,16 @@ jobs:
- name: Run PraisonAI integration tests
run: |
cd PraisonAI/src/praisonai
python -m pytest tests/integration/test_aiui_* -v
python -m pytest tests/integration/test_aiui_* -v --timeout=300
- name: Run PraisonAIUI integration tests
run: |
cd PraisonAIUI
if [ -f tests/integration/test_agentic_roundtrip.py ]; then
python -m pytest tests/integration/test_agentic_roundtrip.py -v
python -m pytest tests/integration/test_agentic_roundtrip.py -v --timeout=300
fi
if [ -f tests/test_feature_sdk_backends.py ]; then
python -m pytest tests/test_feature_sdk_backends.py -v
python -m pytest tests/test_feature_sdk_backends.py -v --timeout=300
fi
- name: Test Pattern C CLI
@@ -152,7 +152,7 @@ jobs:
run: |
cd PraisonAI/src/praisonai
if [ -n "$OPENAI_API_KEY" ]; then
python -m pytest tests/integration/test_aiui_host_agentic.py -v
python -m pytest tests/integration/test_aiui_host_agentic.py -v --timeout=600
else
echo "OPENAI_API_KEY not available, skipping agentic tests"
fi
-183
View File
@@ -1,183 +0,0 @@
name: Auto Merge Conflict → Claude
# Detects internal PRs with mergeStateStatus=DIRTY and posts @claude to trigger claude.yml.
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
schedule:
- cron: '0 */6 * * *'
concurrency:
group: merge-conflict-scan-${{ github.repository }}
cancel-in-progress: false
env:
# PAT posts as MervinPraison so issue_comment triggers claude.yml (GITHUB_TOKEN cannot chain workflows)
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
permissions:
pull-requests: write
issues: write
jobs:
detect-and-trigger:
runs-on: ubuntu-latest
steps:
- name: Detect conflicting PRs and trigger Claude
uses: actions/github-script@v7
with:
github-token: ${{ env.GH_TOKEN }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const baseRepo = `${owner}/${repo}`;
const isMainPush = context.eventName === 'push';
const COOLDOWN_MS = 12 * 60 * 60 * 1000;
const AUTO_ACTORS = ['github-actions[bot]', 'MervinPraison'];
const LABEL = 'claude-conflict-pending';
const COMMENT_BODY = [
'@claude this PR has merge conflicts with `main`.',
'Please rebase onto latest `main`, resolve conflicts (keep this PR\'s intent, merge in newer main logic),',
'run targeted tests, and force-push with `--force-with-lease`.',
'Comment which files you resolved.'
].join(' ');
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function getMergeState(prNumber) {
const query = `
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
mergeStateStatus
maintainerCanModify
isDraft
headRef { repository { nameWithOwner } }
}
}
}
`;
for (let attempt = 0; attempt < 3; attempt++) {
const result = await github.graphql(query, { owner, repo, number: prNumber });
const prGql = result.repository.pullRequest;
const status = (prGql?.mergeStateStatus || '').toUpperCase();
if (status && status !== 'UNKNOWN') {
return {
status,
isDraft: prGql.isDraft,
headRepo: prGql.headRef?.repository?.nameWithOwner,
maintainerCanModify: prGql.maintainerCanModify === true,
};
}
if (attempt < 2) await sleep(10000);
}
const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
return {
status: (pr.data.mergeable_state || '').toUpperCase(),
isDraft: pr.data.draft,
headRepo: pr.data.head.repo?.full_name,
maintainerCanModify: pr.data.maintainer_can_modify === true,
};
}
function hasFinalClaudeReviewTrigger(comments) {
return comments.some((c) => {
const body = (c.body || '').toLowerCase();
if (!AUTO_ACTORS.includes(c.user.login)) return false;
if (!body.includes('@claude')) return false;
if (body.includes('merge conflict')) return false;
return body.includes('final architecture reviewer') || body.includes('lead engineer');
});
}
function hasRecentAutoComment(comments) {
const cutoff = Date.now() - COOLDOWN_MS;
return comments.some((c) => {
if (!AUTO_ACTORS.includes(c.user.login)) return false;
const body = (c.body || '').toLowerCase();
if (!body.includes('@claude') || !body.includes('merge conflict')) return false;
return new Date(c.created_at).getTime() > cutoff;
});
}
async function processPR(prNumber) {
const { status, isDraft, headRepo, maintainerCanModify } = await getMergeState(prNumber);
core.info(`PR #${prNumber}: mergeState=${status}, head=${headRepo}, draft=${isDraft}, maintainerCanModify=${maintainerCanModify}`);
const { data: issue } = await github.rest.issues.get({
owner,
repo,
issue_number: prNumber,
});
const labels = issue.labels.map((l) => l.name);
if (status !== 'DIRTY' && labels.includes(LABEL)) {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: prNumber,
name: LABEL,
});
core.info(`Removed ${LABEL} from PR #${prNumber}`);
}
if (status !== 'DIRTY') return { skipped: true, reason: 'not dirty' };
if (isDraft) return { skipped: true, reason: 'draft' };
if (headRepo && headRepo !== baseRepo && !maintainerCanModify) {
return { skipped: true, reason: 'fork without maintainer edits' };
}
const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number: prNumber,
per_page: 100,
});
if (!isMainPush && hasRecentAutoComment(comments)) {
return { skipped: true, reason: 'recent auto comment' };
}
if (!hasFinalClaudeReviewTrigger(comments)) {
return { skipped: true, reason: 'awaiting final claude review trigger' };
}
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: COMMENT_BODY,
});
if (!labels.includes(LABEL)) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: [LABEL],
});
}
return { triggered: true };
}
let prNumbers = [];
if (context.eventName === 'pull_request') {
prNumbers = [context.payload.pull_request.number];
} else {
const prs = await github.paginate(github.rest.pulls.list, {
owner,
repo,
state: 'open',
per_page: 100,
});
prNumbers = prs.map((p) => p.number);
}
const results = [];
for (const num of prNumbers) {
results.push({ pr: num, ...(await processPR(num)) });
}
core.summary.addRaw('```json\n' + JSON.stringify(results, null, 2) + '\n```');
await core.summary.write();
+2 -2
View File
@@ -15,8 +15,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
+12 -3
View File
@@ -26,8 +26,11 @@ jobs:
# First checkout the upstream repo at the specified tag
- name: Checkout upstream repo
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
repository: MervinPraison/PraisonAI
ref: ${{ github.event.inputs.tag }}
fetch-depth: 0
@@ -45,8 +48,11 @@ jobs:
# Now checkout the fork at main branch
- name: Checkout fork
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
ref: main
fetch-depth: 0
@@ -443,8 +449,11 @@ jobs:
# First checkout the fork's main branch
- name: Checkout fork repo
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
@@ -1,31 +0,0 @@
name: Sync Claude Secrets to PraisonAIUI
on:
workflow_dispatch:
permissions:
contents: read
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Sync secrets to PraisonAIUI
env:
GH_TOKEN: ${{ secrets.SECRETS_ADMIN_PAT }}
TARGET: MervinPraison/PraisonAIUI
CLAUDE_APP_ID: ${{ secrets.CLAUDE_APP_ID }}
CLAUDE_APP_PRIVATE_KEY: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GH_TOKEN_VALUE: ${{ secrets.GH_TOKEN }}
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "SECRETS_ADMIN_PAT is not configured in this repository"
exit 1
fi
echo -n "$CLAUDE_APP_ID" | gh secret set CLAUDE_APP_ID --repo "$TARGET"
echo -n "$CLAUDE_APP_PRIVATE_KEY" | gh secret set CLAUDE_APP_PRIVATE_KEY --repo "$TARGET"
echo -n "$CLAUDE_CODE_OAUTH_TOKEN" | gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo "$TARGET"
echo -n "$GH_TOKEN_VALUE" | gh secret set GH_TOKEN --repo "$TARGET"
echo "Synced CLAUDE_APP_ID, CLAUDE_APP_PRIVATE_KEY, CLAUDE_CODE_OAUTH_TOKEN, GH_TOKEN → $TARGET"
+4 -2
View File
@@ -37,8 +37,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+2 -2
View File
@@ -22,8 +22,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
+8 -4
View File
@@ -13,8 +13,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
@@ -79,8 +81,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
+4 -2
View File
@@ -33,8 +33,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
+16 -16
View File
@@ -45,8 +45,8 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
@@ -105,8 +105,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
@@ -187,8 +187,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -259,8 +259,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -318,8 +318,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -377,8 +377,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -436,8 +436,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
@@ -495,8 +495,8 @@ jobs:
fi
- uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
if: steps.check-key.outputs.has_key == 'true'
- name: Set up Python 3.11
+4 -2
View File
@@ -45,8 +45,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
with:
persist-credentials: false
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
-3
View File
@@ -88,9 +88,6 @@ issue_details.json
.internal
.praisonai
# Backup files
*.bak
# Rust / Cargo
target/
Cargo.lock
-10
View File
@@ -1,10 +0,0 @@
# Agent Instructions
You are working on the PraisonAI project.
## Project Guidelines
- Follow the existing code style and conventions
- Be concise and helpful in responses
- Test implementation thoroughly
- Ensure backward compatibility with existing APIs
- Follow protocol-driven design: core protocols in praisonaiagents/, heavy implementations in praisonai/
-14
View File
@@ -1,14 +0,0 @@
# Contributing to PraisonAI
Thank you for your interest! Here's how to get started:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
## Guidelines
- Follow existing code style
- Add tests for new features
- Update documentation as needed
+2 -2
View File
@@ -770,11 +770,11 @@ agent.start('Write a movie script about a robot in Mars');
## ⚡ Performance
PraisonAI is built for speed, with agent instantiation in around 14μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
PraisonAI is built for speed, with agent instantiation in under 4μs. This reduces overhead, improves responsiveness, and helps multi-agent systems scale efficiently in real-world production workloads.
| Performance Metric | PraisonAI |
|--------------------|-----------|
| Avg Instantiation Time | **14 μs** |
| Avg Instantiation Time | **3.77 μs** |
---
+18 -91
View File
@@ -15,7 +15,6 @@ Methods:
* <code title="class GuardrailResult">GuardrailResult.<a href="./src/praisonai-agents/praisonaiagents/guardrails/guardrail_result.py">from_tuple</a>(result: Tuple[bool, Any]) -> 'GuardrailResult'</code>
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_dict</a>(data: Dict[str, Any]) -> 'HandoffConfig'</code>
* <code title="class HandoffConfig">HandoffConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class HandoffResult">HandoffResult.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">from_outcome</a>(outcome: AgentRunOutcome, target_agent: Optional[str] = None, source_agent: Optional[str] = None, handoff_depth: int = 0) -> 'HandoffResult'</code>
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">json</a>() -> Optional[str]</code>
* <code title="class TaskOutput">TaskOutput.<a href="./src/praisonai-agents/praisonaiagents/main.py">to_dict</a>() -> dict</code>
* <code title="class ToolResult">ToolResult.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -59,8 +58,6 @@ Methods:
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">handoff_to_async</a>(target_agent: 'Agent', prompt: str, context: Optional[Dict[str, Any]] = None, config: Optional['HandoffConfig'] = None) -> 'HandoffResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">is_closed</a>() -> bool</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_instance</a>()</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_instance</a>(value)</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">llm_model</a>() -> Optional[str]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>() -> Optional[str]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">output_style</a>(value: Optional[str]) -> None</code>
@@ -78,7 +75,7 @@ Methods:
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">run_until_async</a>(prompt: str, criteria: str, threshold: float = 8.0, max_iterations: int = 5, mode: str = 'optimize', on_iteration: Optional[Callable[[Any], None]] = None, verbose: bool = False) -> 'EvaluationLoopResult'</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">skill_manager</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', action: str = 'add', **kwargs: Any) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">store_memory</a>(content: str, memory_type: str = 'short_term', **kwargs: Any) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>() -> Optional[Any]</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">stream_emitter</a>(value: Optional[Any]) -> None</code>
* <code title="class Agent">Agent.<a href="./src/praisonai-agents/praisonaiagents/agent/agent.py">thinking_budget</a>() -> Optional[int]</code>
@@ -178,7 +175,6 @@ Methods:
* <code title="class BaseTool">BaseTool.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_schema_roundtrip</a>() -> bool</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">__call__</a>(*args, **kwargs) -> Any</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">check_availability</a>() -> tuple[bool, str]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">get_schema</a>() -> Dict[str, Any]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">injected_params</a>() -> Dict[str, Any]</code>
* <code title="class FunctionTool">FunctionTool.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">run</a>(**kwargs) -> Any</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">clear</a>() -> None</code>
@@ -186,18 +182,16 @@ Methods:
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">discover_single_file_plugins</a>() -> int</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_all</a>() -> Dict[str, Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool_definitions</a>() -> List[Dict[str, Any]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_trust_level</a>(name: str) -> Optional[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None, ttl_seconds: Optional[float] = None) -> List[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_available_tools</a>(context: Optional[Dict[str, Any]] = None) -> List[Union[BaseTool, Callable]]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_base_tools</a>() -> List[BaseTool]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools</a>() -> List[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">list_tools_with_allowed_filter</a>(context: Optional[Dict[str, Any]] = None) -> List[str]</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, overwrite: bool = False) -> None</code>
* <code title="class ToolRegistry">ToolRegistry.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">unregister</a>(name: str) -> bool</code>
* <code title="class Tools">Tools.<a href="./src/praisonai-agents/praisonaiagents/tools/tools.py">internet_search</a>(*args, **kwargs)</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_registry</a>() -> ToolRegistry</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">get_tool</a>(name: str) -> Optional[Union[BaseTool, Callable]]</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None, trust_level: Optional[str] = None, dynamic_schema_overrides: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None) -> None</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/registry.py">register_tool</a>(tool: Union[BaseTool, Callable], name: Optional[str] = None) -> None</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/decorator.py">tool</a>(func: Optional[Callable] = None) -> Union[FunctionTool, Callable[[Callable], FunctionTool]]</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/tools/base.py">validate_tool</a>(tool: Any) -> bool</code>
@@ -242,7 +236,6 @@ from praisonaiagents import Memory, MemoryBackend, MemoryConfig
Methods:
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_cache_optimized_context</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_cache_boundary: bool = True, include_in_output: Optional[bool] = None) -> Dict[str, str]</code>
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">build_context_for_task</a>(task_descr: str, user_id: Optional[str] = None, additional: str = '', max_items: int = 3, include_in_output: Optional[bool] = None) -> str</code>
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">calculate_quality_metrics</a>(output: str, expected_output: str, llm: Optional[str] = None, custom_prompt: Optional[str] = None) -> Dict[str, float]</code>
* <code title="class Memory">Memory.<a href="./src/praisonai-agents/praisonaiagents/memory/memory.py">close_connections</a>()</code>
@@ -360,7 +353,7 @@ Methods:
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">to_tool_function</a>(source_agent: 'Agent') -> Callable</code>
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_description</a>() -> str</code>
* <code title="class Handoff">Handoff.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">tool_name</a>() -> str</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None, tool_policy_mode: Optional[Literal['intersect', 'passthrough']] = None, blocked_tools: Optional[List[str]] = None) -> Handoff</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">handoff</a>(agent: 'Agent', tool_name_override: Optional[str] = None, tool_description_override: Optional[str] = None, on_handoff: Optional[Callable] = None, input_type: Optional[type] = None, input_filter: Optional[Callable[[HandoffInputData], HandoffInputData]] = None, config: Optional[HandoffConfig] = None, context_policy: Optional[str] = None, timeout_seconds: Optional[float] = None, max_concurrent: Optional[int] = None, detect_cycles: Optional[bool] = None, max_depth: Optional[int] = None) -> Handoff</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">compress_history</a>(data: HandoffInputData) -> HandoffInputData</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">keep_last_n_messages</a>(n: int) -> Callable[[HandoffInputData], HandoffInputData]</code>
* <code title="class handoff_filters">handoff_filters.<a href="./src/praisonai-agents/praisonaiagents/agent/handoff.py">remove_all_tools</a>(data: HandoffInputData) -> HandoffInputData</code>
@@ -479,16 +472,11 @@ Methods:
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">activate</a>(skill: LoadedSkill) -> bool</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">activate_by_name</a>(name: str) -> bool</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">add_skill</a>(skill_path: str) -> Optional[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">approve</a>(identifier: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">archive_skill</a>(name: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">clear</a>() -> None</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">create</a>(name: str, content: str, category: Optional[str] = None, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">create_skill</a>(name: str, content: str, category: str = None, agent_created: bool = True, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">delete</a>(name: str, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">delete_skill</a>(name: str, hard: bool = False, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">create_skill</a>(name: str, content: str, category: str = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">delete_skill</a>(name: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">discover</a>(skill_dirs: Optional[List[str]] = None, include_defaults: bool = True) -> int</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">edit</a>(name: str, content: str, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">edit_skill</a>(name: str, content: str, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">edit_skill</a>(name: str, content: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_allowed_tools</a>(name: str) -> List[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_available_skills</a>() -> List[SkillMetadata]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_available_skills_by_state</a>(state: SkillState) -> List[LoadedSkill]</code>
@@ -497,24 +485,15 @@ Methods:
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_skills_diagnostics</a>() -> Dict[str, ValidationResult]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">get_user_invocable_skills</a>() -> List[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">invoke</a>(name: str, raw_args: str = '', session_id: Optional[str] = None, shell_exec: bool = False) -> Optional[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">list_archived_skills</a>() -> List[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">list_pending</a>() -> List[dict]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">load_resources</a>(name: str) -> bool</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">patch</a>(name: str, old_string: str, new_string: str, file_path: Optional[str] = None, replace_all: bool = False, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">patch_skill</a>(name: str, old_string: str, new_string: str, file_path: str = None, replace_all: bool = False, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">reject</a>(identifier: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">remove_file</a>(name: str, file_path: str, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">remove_skill_file</a>(name: str, file_path: str, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">restore_skill</a>(name: str, skill_dir: Optional[str] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">rollback_skill</a>(name: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">patch_skill</a>(name: str, old_string: str, new_string: str, file_path: str = None, replace_all: bool = False) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">remove_skill_file</a>(name: str, file_path: str) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">skill_names</a>() -> List[str]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">skills</a>() -> List[LoadedSkill]</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">to_prompt</a>() -> str</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">validate_skill_capabilities</a>(skill_name: str, force_refresh: bool = False) -> ValidationResult</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">write_file</a>(name: str, file_path: str, file_content: str, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">write_skill_file</a>(name: str, file_path: str, file_content: str, propose: Optional[bool] = None) -> dict</code>
* <code title="class SkillManager">SkillManager.<a href="./src/praisonai-agents/praisonaiagents/skills/manager.py">write_skill_file</a>(name: str, file_path: str, file_content: str) -> dict</code>
* <code title="class SkillMetadata">SkillMetadata.<a href="./src/praisonai-agents/praisonaiagents/skills/models.py">from_properties</a>(props: SkillProperties) -> 'SkillMetadata'</code>
* <code title="class SkillProperties">SkillProperties.<a href="./src/praisonai-agents/praisonaiagents/skills/models.py">idle_days</a>() -> Optional[float]</code>
* <code title="class SkillProperties">SkillProperties.<a href="./src/praisonai-agents/praisonaiagents/skills/models.py">to_dict</a>() -> dict</code>
* <code title="class SkillsConfig">SkillsConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -532,12 +511,10 @@ Methods:
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">add_memory</a>(text: str, memory_type: str = 'long', **metadata) -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">chat</a>(message: str, **kwargs) -> str</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">clear_memory</a>(memory_type: str = 'all') -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">close</a>() -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">create_agent</a>(*args, **kwargs) -> 'Agent'</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">get_context</a>(query: str, max_items: int = 3) -> str</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">get_state</a>(key: str, default: Any = None) -> Any</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">increment_state</a>(key: str, increment: int = 1, default: int = 0) -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">is_expired</a>() -> bool</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">knowledge</a>() -> 'Knowledge'</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">memory</a>() -> 'Memory'</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">restore_state</a>() -> Dict[str, Any]</code>
@@ -546,7 +523,6 @@ Methods:
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">search_memory</a>(query: str, memory_type: str = 'long', limit: int = 5) -> List[Dict[str, Any]]</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">send_message</a>(message: str, **kwargs) -> str</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">set_state</a>(key: str, value: Any) -> None</code>
* <code title="class Session">Session.<a href="./src/praisonai-agents/praisonaiagents/session/api.py">time_to_expiry</a>() -> Optional[float]</code>
# MCP
@@ -629,7 +605,7 @@ Methods:
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">register_snapshot_callback</a>(callback: Callable[[SnapshotHookData], None]) -> None</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">reset</a>() -> None</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">set_tool_budget</a>(tool_name: str, max_tokens: int, protected: bool = False) -> None</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">truncate_tool_output</a>(tool_name: str, output: str, tool_call_id: str = None, run_id: str = None) -> str</code>
* <code title="class ContextManager">ContextManager.<a href="./src/praisonai-agents/praisonaiagents/context/manager.py">truncate_tool_output</a>(tool_name: str, output: str) -> str</code>
# UI
@@ -650,7 +626,6 @@ Methods:
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">effective_track_changes</a>() -> bool</code>
* <code title="class AutonomyConfig">AutonomyConfig.<a href="./src/praisonai-agents/praisonaiagents/agent/autonomy.py">from_dict</a>(data: Dict[str, Any]) -> 'AutonomyConfig'</code>
* <code title="class CachingConfig">CachingConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">from_dict</a>(data: Dict[str, Any]) -> 'ExecutionConfig'</code>
* <code title="class ExecutionConfig">ExecutionConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class GuardrailConfig">GuardrailConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
* <code title="class HooksConfig">HooksConfig.<a href="./src/praisonai-agents/praisonaiagents/config/feature_configs.py">to_dict</a>() -> Dict[str, Any]</code>
@@ -718,7 +693,7 @@ Methods:
Types:
```python
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, RetryBackoffConfig, __version__, aembedding, aembeddings, embedding, embeddings, get_dimensions
from praisonaiagents import AgentAppConfig, AgentAppProtocol, AgentFlow, AgentManager, AgentOSConfig, AgentOSProtocol, AgentTeam, AutoApproveBackend, EmbeddingResult, aembedding, aembeddings, embedding, embeddings, get_dimensions
```
Methods:
@@ -743,16 +718,12 @@ Methods:
* <code title="class AgentFlow">AgentFlow.<a href="./src/praisonai-agents/praisonaiagents/workflows/workflows.py">verbose</a>(value: bool)</code>
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">get_app</a>() -> Any</code>
* <code title="class AgentOSProtocol">AgentOSProtocol.<a href="./src/praisonai-agents/praisonaiagents/app/protocols.py">serve</a>(host: Optional[str] = None, port: Optional[int] = None, reload: bool = False, **kwargs: Any) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aannounce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">add_task</a>(task)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aexecute_task</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">announce_completion</a>(agent_id: str, task_id: str, result: Any, success: bool = True, error: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">append_to_state</a>(key: str, value: Any, max_length: Optional[int] = None) -> List[Any]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_all_tasks</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">arun_task</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">aspawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">astart</a>(content = None, return_dict = False, **kwargs)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">await_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clean_json_output</a>(output: str) -> str</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">clear_state</a>() -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">close</a>() -> None</code>
@@ -767,7 +738,6 @@ Methods:
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_all_tasks_status</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_detailed_token_report</a>() -> Dict[str, Any]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_plan_markdown</a>() -> str</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_spawned_agents</a>() -> List[SpawnedSubAgent]</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_state</a>(key: str, default: Any = None) -> Any</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_details</a>(task_id)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">get_task_result</a>(task_id)</code>
@@ -784,12 +754,10 @@ Methods:
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_output_to_file</a>(task, task_output)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">save_session_state</a>(session_id: str, include_memory: bool = True) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">set_state</a>(key: str, value: Any) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">spawn_sub_agent</a>(agent: Agent, task: Any, completion_callback: Optional[Callable[[SubAgentCompletionEvent], Any]] = None, metadata: Optional[Dict[str, Any]] = None) -> SpawnedSubAgent</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">start</a>(content = None, return_dict = False, output = None, **kwargs)</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">todo_list</a>()</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_plan_step_status</a>(step_id: str, status: str) -> bool</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">update_state</a>(updates: Dict) -> None</code>
* <code title="class AgentTeam">AgentTeam.<a href="./src/praisonai-agents/praisonaiagents/agents/agents.py">wait_for_completions</a>(timeout: Optional[float] = None, agent_ids: Optional[List[str]] = None) -> List[SubAgentCompletionEvent]</code>
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval</a>(request: ApprovalRequest) -> ApprovalDecision</code>
* <code title="class AutoApproveBackend">AutoApproveBackend.<a href="./src/praisonai-agents/praisonaiagents/approval/backends.py">request_approval_sync</a>(request: ApprovalRequest) -> ApprovalDecision</code>
* <code title="function">praisonaiagents.<a href="./src/praisonai-agents/praisonaiagents/embedding/embed.py">aembedding</a>(input: Union[str, List[str]], model: str = 'text-embedding-3-small', dimensions: Optional[int] = None, encoding_format: str = 'float', timeout: float = 600.0, api_key: Optional[str] = None, api_base: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs) -> EmbeddingResult</code>
@@ -809,17 +777,11 @@ Methods:
* <code title="cli">praisonai <a href="./src/praisonai/praisonai/cli/main.py">--help</a></code>
* <code title="cli">praisonai acp acp-main <a href="./src/praisonai/praisonai/cli/commands/acp.py">--help</a></code>
* <code title="cli">praisonai agent list <a href="./src/praisonai/praisonai/cli/commands/agent.py">--help</a></code>
* <code title="cli">praisonai agent show <a href="./src/praisonai/praisonai/cli/commands/agent.py">--help</a></code>
* <code title="cli">praisonai agents create <a href="./src/praisonai/praisonai/cli/commands/agents.py">--help</a></code>
* <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 app app <a href="./src/praisonai/praisonai/cli/commands/app.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 auth list <a href="./src/praisonai/praisonai/cli/commands/auth.py">--help</a></code>
* <code title="cli">praisonai auth login <a href="./src/praisonai/praisonai/cli/commands/auth.py">--help</a></code>
* <code title="cli">praisonai auth logout <a href="./src/praisonai/praisonai/cli/commands/auth.py">--help</a></code>
* <code title="cli">praisonai auth status <a href="./src/praisonai/praisonai/cli/commands/auth.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>
@@ -860,8 +822,6 @@ Methods:
* <code title="cli">praisonai claw claw <a href="./src/praisonai/praisonai/cli/commands/claw.py">--help</a></code>
* <code title="cli">praisonai code <a href="./src/praisonai/praisonai/cli/main.py">--help</a></code>
* <code title="cli">praisonai code code-main <a href="./src/praisonai/praisonai/cli/commands/code.py">--help</a></code>
* <code title="cli">praisonai command list <a href="./src/praisonai/praisonai/cli/commands/command.py">--help</a></code>
* <code title="cli">praisonai command show <a href="./src/praisonai/praisonai/cli/commands/command.py">--help</a></code>
* <code title="cli">praisonai commit commit-main <a href="./src/praisonai/praisonai/cli/commands/commit.py">--help</a></code>
* <code title="cli">praisonai completion bash <a href="./src/praisonai/praisonai/cli/commands/completion.py">--help</a></code>
* <code title="cli">praisonai completion completion-callback <a href="./src/praisonai/praisonai/cli/commands/completion.py">--help</a></code>
@@ -874,9 +834,6 @@ Methods:
* <code title="cli">praisonai config path <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config reset <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config set <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config show <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config sources <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai config validate <a href="./src/praisonai/praisonai/cli/commands/config.py">--help</a></code>
* <code title="cli">praisonai context add <a href="./src/praisonai/praisonai/cli/commands/context.py">--help</a></code>
* <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>
@@ -907,18 +864,17 @@ Methods:
* <code title="cli">praisonai docs serve <a href="./src/praisonai/praisonai/cli/commands/docs.py">--help</a></code>
* <code title="cli">praisonai docs stats <a href="./src/praisonai/praisonai/cli/commands/docs.py">--help</a></code>
* <code title="cli">praisonai doctor cleanup <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor docker <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor config <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor db <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor doctor-callback <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor env <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor llm-providers <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor mcp <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor memory-store <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor metadata-store <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor network <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor packaging <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor performance <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor runtime <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor selftest <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor skills <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor tools <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai doctor troubleshoot <a href="./src/praisonai/praisonai/cli/commands/doctor.py">--help</a></code>
* <code title="cli">praisonai endpoints list <a href="./src/praisonai/praisonai/cli/commands/endpoints.py">--help</a></code>
* <code title="cli">praisonai endpoints test <a href="./src/praisonai/praisonai/cli/commands/endpoints.py">--help</a></code>
@@ -946,9 +902,6 @@ Methods:
* <code title="cli">praisonai gateway install <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway logs <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway mint-link <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway pause <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway reconnect <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway resume <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway send <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway start <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
* <code title="cli">praisonai gateway status <a href="./src/praisonai/praisonai/cli/commands/gateway.py">--help</a></code>
@@ -958,11 +911,6 @@ Methods:
* <code title="cli">praisonai hooks add <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
* <code title="cli">praisonai hooks list <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
* <code title="cli">praisonai hooks remove <a href="./src/praisonai/praisonai/cli/commands/hooks.py">--help</a></code>
* <code title="cli">praisonai identity import <a href="./src/praisonai/praisonai/cli/commands/identity.py">--help</a></code>
* <code title="cli">praisonai identity link <a href="./src/praisonai/praisonai/cli/commands/identity.py">--help</a></code>
* <code title="cli">praisonai identity list <a href="./src/praisonai/praisonai/cli/commands/identity.py">--help</a></code>
* <code title="cli">praisonai identity unlink <a href="./src/praisonai/praisonai/cli/commands/identity.py">--help</a></code>
* <code title="cli">praisonai init init <a href="./src/praisonai/praisonai/cli/commands/init.py">--help</a></code>
* <code title="cli">praisonai kanban block <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban boards <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
* <code title="cli">praisonai kanban comment <a href="./src/praisonai/praisonai/cli/commands/kanban.py">--help</a></code>
@@ -1027,9 +975,6 @@ Methods:
* <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 models describe <a href="./src/praisonai/praisonai/cli/commands/models.py">--help</a></code>
* <code title="cli">praisonai models list <a href="./src/praisonai/praisonai/cli/commands/models.py">--help</a></code>
* <code title="cli">praisonai models validate <a href="./src/praisonai/praisonai/cli/commands/models.py">--help</a></code>
* <code title="cli">praisonai n8n export <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n import <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
* <code title="cli">praisonai n8n list <a href="./src/praisonai/praisonai/cli/commands/n8n.py">--help</a></code>
@@ -1047,14 +992,6 @@ Methods:
* <code title="cli">praisonai pairing list <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai pairing revoke <a href="./src/praisonai/praisonai/cli/commands/pairing.py">--help</a></code>
* <code title="cli">praisonai paths show <a href="./src/praisonai/praisonai/cli/commands/paths.py">--help</a></code>
* <code title="cli">praisonai permissions allow <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions ask <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions deny <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions export <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions import-rules <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions list <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions remove <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai permissions reset <a href="./src/praisonai/praisonai/cli/commands/permissions.py">--help</a></code>
* <code title="cli">praisonai plugins create <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai plugins disable <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
* <code title="cli">praisonai plugins discover <a href="./src/praisonai/praisonai/cli/commands/plugins.py">--help</a></code>
@@ -1117,8 +1054,6 @@ Methods:
* <code title="cli">praisonai sandbox sandbox-callback <a href="./src/praisonai/praisonai/cli/commands/sandbox.py">--help</a></code>
* <code title="cli">praisonai sandbox status <a href="./src/praisonai/praisonai/cli/commands/sandbox.py">--help</a></code>
* <code title="cli">praisonai schedule add <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule blueprint <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule blueprint-list <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule delete <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule describe <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule list <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
@@ -1128,10 +1063,6 @@ Methods:
* <code title="cli">praisonai schedule start <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule stats <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule stop <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule suggestion-accept <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule suggestion-dismiss <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule suggestion-propose <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai schedule suggestions <a href="./src/praisonai/praisonai/cli/commands/schedule.py">--help</a></code>
* <code title="cli">praisonai serve a2a <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve a2u <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve acp <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
@@ -1140,7 +1071,6 @@ Methods:
* <code title="cli">praisonai serve gateway <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve lsp <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve mcp <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve openai <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve rag <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve recipe <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
* <code title="cli">praisonai serve registry <a href="./src/praisonai/praisonai/cli/commands/serve.py">--help</a></code>
@@ -1208,9 +1138,6 @@ Methods:
* <code title="cli">praisonai up logs <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai up status <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai up up-start <a href="./src/praisonai/praisonai/cli/commands/up.py">--help</a></code>
* <code title="cli">praisonai validate check <a href="./src/praisonai/praisonai/cli/commands/validate.py">--help</a></code>
* <code title="cli">praisonai validate schema <a href="./src/praisonai/praisonai/cli/commands/validate.py">--help</a></code>
* <code title="cli">praisonai validate validate <a href="./src/praisonai/praisonai/cli/commands/validate.py">--help</a></code>
* <code title="cli">praisonai version check <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
* <code title="cli">praisonai version show <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
* <code title="cli">praisonai version version-callback <a href="./src/praisonai/praisonai/cli/commands/version.py">--help</a></code>
@@ -1363,7 +1290,7 @@ export { // Python parity additions
export { // Subagent Tool (agent-as-tool pattern)
SubagentTool, BaseTool, FunctionTool, ToolRegistry, ToolResult, ToolValidationError, createDelegator, createSubagentTool, createSubagentTools, createTool, getRegistry, getTool, registerTool, tool, validateTool } from "./tools";
export { airweaveSearch, bedrockBrowserClick, bedrockBrowserFill, bedrockBrowserNavigate, bedrockCodeInterpreter, codeExecution, codeMode, createCustomTool, exaSearch, firecrawlCrawl, firecrawlScrape, parallelSearch, perplexitySearch, registerCustomTool, registerLocalTool, registerNpmTool, superagentGuard, superagentRedact, superagentVerify, tavilyCrawl, tavilyExtract, tavilySearch, valyuBioSearch, valyuCompanyResearch, valyuEconomicsSearch, valyuFinanceSearch, valyuPaperSearch, valyuPatentSearch, valyuSecSearch, valyuWebSearch } from "./tools/builtins";
export { BudgetExceededError, MissingDependencyError, MissingEnvVarError, ToolsRegistry, composeMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRedactionMiddleware, createRetryMiddleware, createTimeoutMiddleware, createToolsRegistry, createTracingMiddleware, createValidationMiddleware, getToolsRegistry, get_registry, get_tool, register_tool, resetToolsRegistry, validate_tool } from "./tools/registry";
export { BudgetExceededError, MissingDependencyError, MissingEnvVarError, ToolsRegistry, composeMiddleware, createLoggingMiddleware, createRateLimitMiddleware, createRedactionMiddleware, createRetryMiddleware, createTimeoutMiddleware, createToolsRegistry, createTracingMiddleware, createValidationMiddleware, getRegistry, getTool, getToolsRegistry, get_registry, get_tool, registerTool, register_tool, resetToolsRegistry, validateTool, validate_tool } from "./tools/registry";
export type { InstallHints, PraisonTool, RedactionHooks, RegisteredTool, ToolCapabilities, ToolExecutionContext, ToolExecutionResult, ToolFactory, ToolHooks, ToolInstallStatus, ToolLimits, ToolLogger, ToolMetadata, ToolMiddleware, ToolParameterProperty, ToolParameterSchema } from "./tools/registry";
export { registerBuiltinTools, tools } from "./tools/tools";
export { // Classes
+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>=4.6.74" \
"praisonai>=4.6.52" \
"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>=4.6.74" \
"praisonai>=4.6.52" \
"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>=4.6.74" \
"praisonai>=4.6.52" \
"praisonai[ui]" \
"praisonai[crewai]"
+1 -6
View File
@@ -12,13 +12,8 @@ from praisonaiagents import Agent
from praisonaiagents.approval import AgentApproval
from praisonaiagents.tools.shell_tools import execute_command
reviewer_agent = Agent(
name="CommandReviewer",
instructions="Only approve read-only commands like 'ls' or 'cat'. Deny destructive commands.",
)
reviewer = AgentApproval(
approver_agent=reviewer_agent,
instructions="Only approve read-only commands like 'ls' or 'cat'. Deny destructive commands.",
)
agent = Agent(
+3 -3
View File
@@ -5,12 +5,12 @@ Demonstrates web search with consolidated params.
Presets: duckduckgo, tavily, google, bing, serper, search_only, fetch_only
"""
from praisonaiagents import Agent, WebConfig
from praisonaiagents import Agent
# Basic: Enable web search with preset (search only — fetch needs Claude)
# Basic: Enable web search with preset
agent = Agent(
instructions="You are a research assistant with web access.",
web="search_only", # Compatible across models without web_fetch
web="duckduckgo", # Presets: duckduckgo, tavily, google, bing, serper
)
if __name__ == "__main__":
@@ -30,5 +30,5 @@ if __name__ == "__main__":
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
result = workflow.run("AI agents")
result = workflow.run()
print(result)
@@ -1,216 +0,0 @@
# Industry Templates for PraisonAI
This directory contains industry-specific agent templates based on the SRAO Framework, providing pre-built workflows for rapid deployment of agent workforces across different industries.
## Overview
These templates demonstrate how PraisonAI can be used to create specialized agent teams for various industries, with **70% code reuse** across different sectors through shared patterns and abstractions.
## Available Templates
### 1. Manufacturing (`manufacturing_template.py`)
**Key Agents:**
- **ParseOrder**: Extracts and structures order information from various formats
- **CheckInventory**: Validates material availability and supply chain status
- **OptimizeSchedule**: Optimizes production scheduling based on constraints
- **DefectDetect**: Quality control and defect detection
**Features:**
- Order processing with multiple input formats
- Real-time inventory management
- Production optimization algorithms
- Quality inspection with defect detection
- Fallback strategies for each critical step
### 2. Energy (`energy_template.py`)
**Key Agents:**
- **SCADAReader**: Processes SCADA telemetry data from wind turbines
- **VibrationAnalyzer**: Analyzes vibration patterns for predictive maintenance
- **PowerForecaster**: Predicts power generation based on weather and history
- **MaintenanceScheduler**: Schedules predictive maintenance
**Features:**
- Real-time wind farm monitoring
- Vibration-based fault detection
- Power generation forecasting
- Grid integration and load balancing
- Predictive maintenance scheduling
### 3. Healthcare (`healthcare_template.py`)
**Key Agents:**
- **VitalSignsCapture**: Processes patient vital signs from monitoring devices
- **EMRRetrieval**: Retrieves electronic medical records with HIPAA compliance
- **TriageRecommendation**: Provides ESI-based triage recommendations
- **ResourceAllocator**: Manages hospital resource allocation
**Features:**
- Emergency triage workflow
- HIPAA-compliant data handling
- Patient safety protocols
- Resource optimization
- Cross-department coordination
### 4. Agriculture (`agriculture_template.py`)
**Key Agents:**
- **MultispectralAnalyzer**: Analyzes multispectral imagery for crop health
- **DiseaseIdentifier**: Identifies crop diseases and pests using AI
- **SprayRecommender**: Recommends targeted spraying strategies
- **YieldPredictor**: Predicts crop yield based on conditions
**Features:**
- Precision agriculture with drone/satellite imagery
- Pest and disease early warning system
- Optimized chemical application
- Yield forecasting
- Sustainable farming patterns
### 5. Transportation (`transportation_template.py`)
**Key Agents:**
- **LiDARFusion**: Processes LiDAR point cloud data for infrastructure
- **DisplacementCalculator**: Calculates structural displacement
- **HeatmapGenerator**: Generates safety visualization heatmaps
- **MaintenancePlanner**: Plans infrastructure maintenance
**Features:**
- Tunnel/bridge safety monitoring
- LiDAR-based structural analysis
- Real-time safety assessment
- Traffic flow optimization
- Predictive maintenance scheduling
## Key Features Across All Templates
### 1. I/O Schemas
Each template includes Pydantic models for structured data handling:
- Input validation
- Type safety
- Clear data contracts between agents
### 2. SLA Requirements
Every agent has defined Service Level Agreements:
- Processing time requirements
- Response time guarantees
- Performance benchmarks
### 3. Fallback Strategies
Robust error handling with fallback mechanisms:
- Graceful degradation
- Manual intervention queues
- Conservative estimates when systems fail
### 4. Cross-Industry Patterns (70% Reuse)
Reusable base patterns that work across industries:
- Data parsing agents
- Resource availability checkers
- Optimization engines
- Inspection/quality control agents
## Usage Example
```python
from manufacturing_template import (
order_parser,
inventory_checker,
schedule_optimizer,
quality_inspector,
manufacturing_workflow
)
# Process a manufacturing order
order_text = "Customer ABC needs 100 units of Product-XYZ by March 15th, high priority"
result = manufacturing_workflow(order_text)
print(result)
```
## Adapting Templates for Your Industry
### Using Base Patterns
Each template includes reusable pattern classes that can be adapted:
```python
from manufacturing_template import IndustryAgentPattern
# Create a custom data parser for any domain
custom_parser = IndustryAgentPattern.create_data_parser(
name="CustomParser",
domain="retail",
sla_seconds=20
)
# Create a resource checker for any resource type
resource_checker = IndustryAgentPattern.create_resource_checker(
name="StaffAvailability",
resource_type="customer_service_staff",
sla_seconds=3
)
```
### Creating Custom Workflows
Combine agents from different templates or create new ones:
```python
from praisonaiagents import Agent, tool
# Define custom tools
@tool
def custom_analysis(data: str) -> Dict:
"""Your custom analysis logic"""
return {"result": "analyzed"}
# Create custom agent
custom_agent = Agent(
name="CustomAnalyzer",
instructions="Your specific instructions",
tools=[custom_analysis]
)
```
## Integration with PraisonAI Core
These templates are built on top of PraisonAI's core Agent framework:
```python
from praisonaiagents import Agent, tool, AgentTeam, Task
# Combine multiple industry agents into a team
team = AgentTeam(
agents=[order_parser, inventory_checker, schedule_optimizer],
tasks=[
Task(name="parse", agent=order_parser),
Task(name="check", agent=inventory_checker),
Task(name="optimize", agent=schedule_optimizer)
]
)
```
## Best Practices
1. **Start with Templates**: Use these as starting points and customize for your specific needs
2. **Maintain SLAs**: Define clear performance requirements for each agent
3. **Implement Fallbacks**: Always have fallback strategies for critical operations
4. **Use Type Safety**: Leverage Pydantic models for data validation
5. **Monitor Performance**: Track agent performance against SLAs
6. **Iterate and Improve**: Continuously refine based on real-world usage
## Contributing
To add a new industry template:
1. Follow the existing template structure
2. Include all key components:
- I/O Schemas (Pydantic models)
- Agent definitions with SLAs
- Tools for agent capabilities
- Complete workflow function
- Fallback strategies
- Cross-industry patterns
3. Add comprehensive documentation
4. Include usage examples
## License
These templates are part of the PraisonAI project and follow the same licensing terms.
## References
Based on the [SRAO Framework](https://github.com/beixuan577/SRAO-Framework) (MIT License)
@@ -1,38 +0,0 @@
"""
Industry Templates for PraisonAI
================================
Pre-built agent templates for rapid deployment across different industries.
Based on SRAO Framework with 70% cross-industry code reuse.
Available templates:
- Manufacturing: Order processing, inventory, scheduling, quality control
- Energy: Wind farm monitoring, predictive maintenance, power forecasting
- Healthcare: Emergency triage, EMR retrieval, resource allocation
- Agriculture: Precision farming, pest detection, yield prediction
- Transportation: Infrastructure monitoring, safety assessment, maintenance
Example usage:
from Industry_Templates.manufacturing_template import manufacturing_workflow
result = manufacturing_workflow("Customer order text")
"""
__version__ = "1.0.0"
__author__ = "PraisonAI Team"
# Import the modules so they're accessible via the package
from . import manufacturing_template
from . import energy_template
from . import healthcare_template
from . import agriculture_template
from . import transportation_template
# Export commonly used items for convenience
__all__ = [
"manufacturing_template",
"energy_template",
"healthcare_template",
"agriculture_template",
"transportation_template"
]
@@ -1,551 +0,0 @@
"""
Agriculture Industry Template
=============================
Precision agriculture and pest management workflow
Based on SRAO Framework with IoT sensor integration
Key agents:
- MultispectralAnalyzer: Analyzes multispectral imagery
- DiseaseIdentifier: Identifies crop diseases and pests
- SprayRecommender: Recommends targeted spraying strategies
- YieldPredictor: Predicts crop yield based on conditions
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Tuple, Optional
from pydantic import BaseModel
from datetime import datetime, timedelta
from enum import Enum
# Crop health levels
class CropHealthLevel(str, Enum):
EXCELLENT = "excellent"
GOOD = "good"
FAIR = "fair"
POOR = "poor"
CRITICAL = "critical"
# Pest/Disease severity
class SeverityLevel(str, Enum):
NONE = "none"
LOW = "low"
MODERATE = "moderate"
HIGH = "high"
SEVERE = "severe"
# I/O Schemas
class MultispectralData(BaseModel):
"""Multispectral imagery analysis data"""
field_id: str
capture_time: str
ndvi_index: float # Normalized Difference Vegetation Index
ndre_index: float # Normalized Difference Red Edge
ccci_index: float # Canopy Chlorophyll Content Index
moisture_level: float # Soil moisture percentage
temperature: float # Celsius
affected_area: float # Hectares
gps_coordinates: Tuple[float, float]
class PestDiseaseReport(BaseModel):
"""Pest and disease identification report"""
report_id: str
field_id: str
detection_time: str
pest_type: Optional[str]
disease_type: Optional[str]
severity: SeverityLevel
affected_crops: List[str]
spread_rate: float # % per day
environmental_factors: Dict[str, Any]
confidence_score: float
class SprayRecommendation(BaseModel):
"""Targeted spraying recommendation"""
recommendation_id: str
field_id: str
treatment_type: str # pesticide, fungicide, herbicide, fertilizer
product_name: str
application_rate: float # L/hectare
target_zones: List[Dict[str, Any]] # GPS zones
optimal_time: str
weather_window: Dict[str, Any]
cost_estimate: float
environmental_impact: str # low, medium, high
class YieldForecast(BaseModel):
"""Crop yield prediction"""
forecast_id: str
field_id: str
crop_type: str
predicted_yield: float # tons/hectare
confidence_interval: Tuple[float, float]
harvest_window: Tuple[str, str]
quality_grade: str # A, B, C
market_price_estimate: float
risk_factors: List[str]
# Agriculture-specific tools
@tool
def analyze_multispectral_imagery(field_id: str, image_data: Dict) -> Dict:
"""Analyze multispectral drone/satellite imagery for crop health"""
# Simulate multispectral analysis
ndvi = 0.75 # Healthy vegetation typically 0.6-0.9
# Determine health based on NDVI
if ndvi > 0.8:
health = CropHealthLevel.EXCELLENT
affected_area = 0.0
elif ndvi > 0.6:
health = CropHealthLevel.GOOD
affected_area = 2.5
elif ndvi > 0.4:
health = CropHealthLevel.FAIR
affected_area = 5.0
else:
health = CropHealthLevel.POOR
affected_area = 10.0
return {
"field_id": field_id,
"capture_time": datetime.now().isoformat(),
"ndvi_index": ndvi,
"ndre_index": 0.68,
"ccci_index": 0.72,
"moisture_level": 35.0,
"temperature": 24.5,
"affected_area": affected_area,
"gps_coordinates": (40.7128, -74.0060),
"health_level": health.value
}
@tool
def identify_pest_disease(spectral_data: Dict, historical_data: List) -> Dict:
"""Identify pests and diseases using AI image recognition"""
# Simulate pest/disease detection
ndvi = spectral_data.get("ndvi_index", 0.75)
affected_area = spectral_data.get("affected_area", 0)
if affected_area > 8.0:
pest_type = "aphids"
disease_type = "powdery_mildew"
severity = SeverityLevel.HIGH
spread_rate = 2.5
elif affected_area > 4.0:
pest_type = "whiteflies"
disease_type = "leaf_spot"
severity = SeverityLevel.MODERATE
spread_rate = 1.5
elif affected_area > 0:
pest_type = None
disease_type = "early_blight"
severity = SeverityLevel.LOW
spread_rate = 0.5
else:
pest_type = None
disease_type = None
severity = SeverityLevel.NONE
spread_rate = 0.0
return {
"report_id": f"PDR-{datetime.now().strftime('%Y%m%d%H%M')}",
"field_id": spectral_data["field_id"],
"detection_time": datetime.now().isoformat(),
"pest_type": pest_type,
"disease_type": disease_type,
"severity": severity.value,
"affected_crops": ["wheat", "corn"],
"spread_rate": spread_rate,
"environmental_factors": {
"humidity": 65,
"temperature": spectral_data.get("temperature", 25),
"rainfall_last_week": 15 # mm
},
"confidence_score": 0.85
}
@tool
def calculate_spray_recommendation(pest_report: Dict, field_data: Dict, weather: Dict) -> Dict:
"""Calculate optimal spraying strategy for pest/disease control"""
severity = pest_report.get("severity", "none")
if severity == "high" or severity == "severe":
treatment = "pesticide"
product = "Imidacloprid"
rate = 0.5
impact = "medium"
elif severity == "moderate":
treatment = "fungicide"
product = "Azoxystrobin"
rate = 0.3
impact = "low"
elif severity == "low":
treatment = "organic_treatment"
product = "Neem Oil"
rate = 0.2
impact = "minimal"
else:
treatment = "fertilizer"
product = "NPK 20-20-20"
rate = 0.4
impact = "positive"
# Calculate target zones based on affected area
affected_area = field_data.get("affected_area", 0)
if affected_area > 0:
target_zones = [
{
"zone_id": "A1",
"gps_boundary": [(40.71, -74.00), (40.72, -74.01)],
"priority": "high"
}
]
else:
target_zones = []
return {
"recommendation_id": f"SPR-{datetime.now().strftime('%Y%m%d%H%M')}",
"field_id": field_data["field_id"],
"treatment_type": treatment,
"product_name": product,
"application_rate": rate,
"affected_area": affected_area,
"target_zones": target_zones,
"optimal_time": (datetime.now() + timedelta(days=1)).isoformat(),
"weather_window": {
"wind_speed": weather.get("wind_speed", 5),
"precipitation": weather.get("precipitation", 0),
"suitable": True
},
"cost_estimate": rate * affected_area * 50, # $/hectare
"environmental_impact": impact
}
@tool
def predict_crop_yield(field_id: str, crop_type: str, current_health: Dict, weather_forecast: Dict) -> Dict:
"""Predict crop yield based on current conditions and forecasts"""
# Simulate yield prediction
health_level = current_health.get("health_level", "good")
base_yield = {
"wheat": 3.5,
"corn": 9.5,
"soybeans": 2.8,
"rice": 6.0
}.get(crop_type, 5.0)
# Adjust based on health
health_multiplier = {
"excellent": 1.2,
"good": 1.0,
"fair": 0.8,
"poor": 0.6,
"critical": 0.4
}.get(health_level, 1.0)
predicted_yield = base_yield * health_multiplier
# Quality grade based on conditions
if health_level in ["excellent", "good"]:
quality = "A"
price_multiplier = 1.2
elif health_level == "fair":
quality = "B"
price_multiplier = 1.0
else:
quality = "C"
price_multiplier = 0.8
return {
"forecast_id": f"YLD-{datetime.now().strftime('%Y%m%d%H%M')}",
"field_id": field_id,
"crop_type": crop_type,
"predicted_yield": predicted_yield,
"confidence_interval": (predicted_yield * 0.9, predicted_yield * 1.1),
"harvest_window": (
(datetime.now() + timedelta(days=60)).isoformat(),
(datetime.now() + timedelta(days=75)).isoformat()
),
"quality_grade": quality,
"market_price_estimate": predicted_yield * 250 * price_multiplier, # $/ton
"risk_factors": ["weather_variability", "pest_pressure", "market_volatility"]
}
# Agriculture agent definitions
multispectral_agent = Agent(
name="MultispectralAnalyzer",
instructions="""You are a remote sensing specialist for precision agriculture.
Analyze multispectral imagery from drones and satellites.
Calculate vegetation indices (NDVI, NDRE, CCCI) to assess crop health.
Identify stress zones and anomalies in field conditions.
SLA: Process imagery within 2 minutes per field.""",
tools=[analyze_multispectral_imagery]
)
disease_agent = Agent(
name="DiseaseIdentifier",
instructions="""You are a plant pathology expert with AI vision capabilities.
Identify crop diseases, pests, and nutrient deficiencies.
Assess severity levels and predict spread patterns.
Consider environmental factors affecting disease development.
SLA: Complete identification within 30 seconds.""",
tools=[identify_pest_disease]
)
spray_agent = Agent(
name="SprayRecommender",
instructions="""You are a precision application specialist.
Recommend targeted spraying strategies for pest and disease control.
Optimize chemical usage to minimize environmental impact.
Consider weather windows and application regulations.
SLA: Generate recommendations within 1 minute.""",
tools=[calculate_spray_recommendation]
)
yield_agent = Agent(
name="YieldPredictor",
instructions="""You are a crop yield forecasting specialist.
Predict harvest yields based on current conditions and historical data.
Assess crop quality grades and market value estimates.
Identify risk factors that could impact final yield.
SLA: Generate forecast within 45 seconds.""",
tools=[predict_crop_yield]
)
# Precision agriculture workflow
def precision_agriculture_workflow(field_ids: List[str], crop_type: str, weather_data: Dict):
"""
Complete precision agriculture workflow from monitoring to treatment
Includes early warning system and sustainable farming practices
"""
workflow_results = {
"timestamp": datetime.now().isoformat(),
"fields_monitored": len(field_ids),
"alerts": [],
"treatments_needed": [],
"yield_forecasts": [],
"sustainability_score": 0.0
}
for field_id in field_ids:
try:
# Step 1: Multispectral analysis
spectral_analysis = multispectral_agent.start(
f"Analyze multispectral imagery for field {field_id}"
)
# Step 2: Disease and pest detection
pest_report = disease_agent.start(
f"Identify pests/diseases from spectral data: {spectral_analysis}"
)
# Check for alerts
if pest_report.get("severity") in ["high", "severe"]:
workflow_results["alerts"].append({
"field_id": field_id,
"type": "pest_disease_alert",
"severity": pest_report["severity"],
"pest": pest_report.get("pest_type"),
"disease": pest_report.get("disease_type"),
"action": "immediate_treatment_required"
})
# Step 3: Generate spray recommendation
spray_recommendation = spray_agent.start(
f"Calculate spray strategy for pest report: {pest_report}, "
f"field: {spectral_analysis}, weather: {weather_data}"
)
workflow_results["treatments_needed"].append(spray_recommendation)
# Step 4: Yield prediction
yield_forecast = yield_agent.start(
f"Predict yield for field {field_id}, crop {crop_type}, "
f"health: {spectral_analysis}, weather: {weather_data}"
)
workflow_results["yield_forecasts"].append(yield_forecast)
except Exception as e:
# Fallback: Schedule manual inspection
workflow_results["alerts"].append({
"field_id": field_id,
"type": "monitoring_failure",
"error": str(e),
"action": "schedule_manual_inspection"
})
# Calculate sustainability score
total_chemical = sum(
t.get("application_rate", 0) * t.get("affected_area", 0)
for t in workflow_results["treatments_needed"]
)
workflow_results["sustainability_score"] = max(0, 100 - (total_chemical * 10))
return workflow_results
# Sustainable farming patterns
class SustainableFarmingPatterns:
"""
Patterns for sustainable and regenerative agriculture
"""
@staticmethod
def integrated_pest_management(pest_level: str) -> Dict:
"""IPM strategy based on pest pressure"""
strategies = {
"low": {
"method": "biological_control",
"actions": ["release_beneficial_insects", "plant_trap_crops"],
"chemical_use": 0
},
"moderate": {
"method": "targeted_intervention",
"actions": ["spot_treatment", "pheromone_traps", "minimal_spray"],
"chemical_use": 30
},
"high": {
"method": "integrated_approach",
"actions": ["systematic_spray", "crop_rotation", "resistant_varieties"],
"chemical_use": 70
}
}
return strategies.get(pest_level, strategies["moderate"])
@staticmethod
def precision_irrigation(moisture_level: float, crop_stage: str) -> Dict:
"""Calculate optimal irrigation based on soil moisture and crop stage"""
# Water requirements by growth stage (mm/day)
stage_requirements = {
"germination": 3.0,
"vegetative": 5.0,
"flowering": 7.0,
"grain_filling": 6.0,
"maturation": 2.0
}
required = stage_requirements.get(crop_stage, 5.0)
deficit = max(0, 40 - moisture_level) # Target 40% moisture
return {
"irrigation_needed": deficit > 10,
"amount_mm": deficit * 2,
"method": "drip" if deficit < 20 else "sprinkler",
"schedule": "night_irrigation" if deficit > 15 else "morning_irrigation",
"water_saved": (100 - deficit) / 100 * 1000 # Liters per hectare
}
@staticmethod
def crop_rotation_optimizer(current_crop: str, soil_data: Dict) -> str:
"""Recommend next crop for rotation based on soil health"""
rotations = {
"wheat": ["soybeans", "canola"], # Nitrogen fixation
"corn": ["soybeans", "alfalfa"], # Break disease cycle
"soybeans": ["wheat", "corn"], # Utilize fixed nitrogen
"rice": ["legumes", "vegetables"], # Soil structure improvement
}
return rotations.get(current_crop, ["cover_crops"])[0]
# IoT sensor integration patterns
class IoTSensorPatterns:
"""
Patterns for integrating IoT sensors in smart farming
"""
@staticmethod
def create_sensor_network_agent(sensor_types: List[str]) -> Agent:
"""Create agent for managing IoT sensor networks"""
return Agent(
name="SensorNetworkManager",
instructions=f"""Manage IoT sensor network with {', '.join(sensor_types)}.
Collect real-time data from field sensors.
Detect sensor anomalies and maintenance needs.
Aggregate data for precision agriculture decisions.
SLA: Data collection every 15 minutes."""
)
@staticmethod
def process_sensor_data(sensor_readings: List[Dict]) -> Dict:
"""Process and aggregate IoT sensor data"""
aggregated = {
"timestamp": datetime.now().isoformat(),
"sensor_count": len(sensor_readings),
"averages": {},
"alerts": []
}
# Calculate averages
for reading in sensor_readings:
for key, value in reading.items():
if isinstance(value, (int, float)):
if key not in aggregated["averages"]:
aggregated["averages"][key] = []
aggregated["averages"][key].append(value)
# Convert to means
for key in aggregated["averages"]:
values = aggregated["averages"][key]
aggregated["averages"][key] = sum(values) / len(values)
# Check for anomalies
if key == "soil_ph" and (aggregated["averages"][key] < 6.0 or aggregated["averages"][key] > 7.5):
aggregated["alerts"].append({
"type": "soil_ph_anomaly",
"value": aggregated["averages"][key],
"action": "adjust_soil_treatment"
})
return aggregated
# Example usage
if __name__ == "__main__":
# Monitor multiple fields
fields = ["FIELD-001", "FIELD-002", "FIELD-003"]
weather = {
"temperature": 25,
"humidity": 60,
"wind_speed": 8,
"precipitation": 0,
"forecast": "partly_cloudy"
}
result = precision_agriculture_workflow(fields, "wheat", weather)
print("Agriculture workflow result:", result)
# Sustainable farming example
ipm_strategy = SustainableFarmingPatterns.integrated_pest_management("moderate")
print("IPM Strategy:", ipm_strategy)
irrigation_plan = SustainableFarmingPatterns.precision_irrigation(25.0, "flowering")
print("Irrigation Plan:", irrigation_plan)
next_crop = SustainableFarmingPatterns.crop_rotation_optimizer("wheat", {})
print("Recommended next crop:", next_crop)
# IoT integration example
sensor_agent = IoTSensorPatterns.create_sensor_network_agent(
["soil_moisture", "temperature", "humidity", "light", "ph"]
)
sensor_data = [
{"sensor_id": "S001", "soil_moisture": 35, "temperature": 24, "soil_ph": 6.8},
{"sensor_id": "S002", "soil_moisture": 32, "temperature": 25, "soil_ph": 7.2},
{"sensor_id": "S003", "soil_moisture": 38, "temperature": 23, "soil_ph": 5.8}
]
processed_data = IoTSensorPatterns.process_sensor_data(sensor_data)
print("Processed sensor data:", processed_data)
@@ -1,372 +0,0 @@
"""
Energy Industry Template
========================
Wind farm monitoring and power optimization workflow
Based on SRAO Framework patterns with predictive maintenance
Key agents:
- SCADAReader: Processes SCADA telemetry data
- VibrationAnalyzer: Analyzes turbine vibration patterns
- PowerForecaster: Predicts power generation
- MaintenanceScheduler: Schedules predictive maintenance
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Optional
from pydantic import BaseModel
from datetime import datetime, timedelta
# I/O Schemas
class TurbineData(BaseModel):
"""Wind turbine telemetry data"""
turbine_id: str
timestamp: str
wind_speed: float # m/s
power_output: float # MW
rotor_speed: float # RPM
blade_pitch: float # degrees
nacelle_temperature: float # Celsius
vibration_level: float # mm/s
operational_status: str # running, stopped, maintenance
class VibrationAnalysis(BaseModel):
"""Vibration analysis results"""
turbine_id: str
analysis_time: str
vibration_rms: float
frequency_peaks: List[float]
anomaly_detected: bool
failure_probability: float
recommended_action: str
class PowerForecast(BaseModel):
"""Power generation forecast"""
forecast_id: str
start_time: str
end_time: str
predicted_output: List[float] # MW per hour
confidence_interval: float
weather_factors: Dict[str, Any]
grid_demand: float
class MaintenancePlan(BaseModel):
"""Maintenance scheduling plan"""
plan_id: str
turbine_id: str
maintenance_type: str # preventive, predictive, corrective
scheduled_date: str
estimated_duration: int # hours
required_parts: List[str]
technician_team: str
production_loss: float # MW
# Tools for energy management
@tool
def read_scada_telemetry(turbine_id: str, time_range: str = "last_hour") -> Dict:
"""Read SCADA system telemetry for wind turbines"""
# Simulate SCADA data reading
return {
"turbine_id": turbine_id,
"timestamp": datetime.now().isoformat(),
"wind_speed": 12.5,
"power_output": 2.3,
"rotor_speed": 16.8,
"blade_pitch": 8.5,
"nacelle_temperature": 45.2,
"vibration_level": 3.2,
"operational_status": "running"
}
@tool
def analyze_vibration_patterns(turbine_data: Dict) -> Dict:
"""Analyze vibration patterns for early fault detection"""
# Simulate vibration analysis
vibration_level = turbine_data.get("vibration_level", 0)
anomaly = vibration_level > 5.0 # Threshold for anomaly
return {
"turbine_id": turbine_data["turbine_id"],
"analysis_time": datetime.now().isoformat(),
"vibration_rms": vibration_level,
"frequency_peaks": [10.5, 25.3, 50.0], # Hz
"anomaly_detected": anomaly,
"failure_probability": min(vibration_level / 10.0, 1.0),
"recommended_action": "immediate_inspection" if anomaly else "continue_monitoring"
}
@tool
def forecast_power_generation(weather_data: Dict, historical_data: List) -> Dict:
"""Forecast power generation based on weather and historical patterns"""
# Simulate power forecasting
base_forecast = [2.0 + (i * 0.1) for i in range(24)] # 24-hour forecast
return {
"forecast_id": f"FCST-{datetime.now().strftime('%Y%m%d%H%M')}",
"start_time": datetime.now().isoformat(),
"end_time": (datetime.now() + timedelta(hours=24)).isoformat(),
"predicted_output": base_forecast,
"confidence_interval": 0.85,
"weather_factors": weather_data,
"grid_demand": sum(base_forecast)
}
@tool
def schedule_maintenance(turbine_id: str, maintenance_type: str, urgency: str = "normal") -> Dict:
"""Schedule maintenance based on condition monitoring"""
# Calculate maintenance window
if urgency == "urgent":
scheduled_date = (datetime.now() + timedelta(days=1)).isoformat()
else:
scheduled_date = (datetime.now() + timedelta(days=7)).isoformat()
return {
"plan_id": f"MNT-{datetime.now().strftime('%Y%m%d%H%M')}",
"turbine_id": turbine_id,
"maintenance_type": maintenance_type,
"scheduled_date": scheduled_date,
"estimated_duration": 4,
"required_parts": ["bearing_set", "lubricant", "filter"],
"technician_team": "Team-A",
"production_loss": 8.0 # MW lost during maintenance
}
# Agent Definitions with SLA requirements
scada_reader = Agent(
name="SCADAReader",
instructions="""You are a SCADA system specialist for wind farms.
Process real-time telemetry data from wind turbines.
Identify operational anomalies and performance deviations.
SLA: Process telemetry within 1 second for real-time monitoring.""",
tools=[read_scada_telemetry]
)
vibration_analyzer = Agent(
name="VibrationAnalyzer",
instructions="""You are a vibration analysis expert for rotating machinery.
Analyze vibration patterns to detect early signs of mechanical failure.
Use FFT analysis and pattern recognition for fault diagnosis.
SLA: Complete analysis within 5 seconds per turbine.""",
tools=[analyze_vibration_patterns]
)
power_forecaster = Agent(
name="PowerForecaster",
instructions="""You are a power generation forecasting specialist.
Predict power output based on weather forecasts and historical patterns.
Consider grid demand and optimize generation schedules.
SLA: Generate 24-hour forecast within 30 seconds.""",
tools=[forecast_power_generation]
)
maintenance_scheduler = Agent(
name="MaintenanceScheduler",
instructions="""You are a predictive maintenance planning expert.
Schedule maintenance based on condition monitoring and failure predictions.
Minimize production loss while ensuring turbine reliability.
SLA: Create maintenance plan within 1 minute.""",
tools=[schedule_maintenance]
)
# Energy workflow with grid integration
def energy_monitoring_workflow(turbine_ids: List[str], weather_forecast: Dict):
"""
Complete energy monitoring and optimization workflow
Includes predictive maintenance and grid integration
"""
results = {
"timestamp": datetime.now().isoformat(),
"turbines_monitored": len(turbine_ids),
"alerts": [],
"maintenance_required": [],
"power_forecast": None
}
# Step 1: Monitor all turbines
for turbine_id in turbine_ids:
try:
# Read SCADA data
scada_data = scada_reader.start(
f"Read telemetry for turbine {turbine_id}"
)
# Analyze vibrations
vibration_result = vibration_analyzer.start(
f"Analyze vibrations for data: {scada_data}"
)
# Check for anomalies
if vibration_result.get("anomaly_detected"):
results["alerts"].append({
"turbine_id": turbine_id,
"type": "vibration_anomaly",
"severity": "high" if vibration_result.get("failure_probability", 0) > 0.7 else "medium"
})
# Schedule maintenance if needed
maintenance_plan = maintenance_scheduler.start(
f"Schedule maintenance for turbine {turbine_id} with urgency based on {vibration_result}"
)
results["maintenance_required"].append(maintenance_plan)
except Exception as e:
# Fallback: Mark turbine for manual inspection
results["alerts"].append({
"turbine_id": turbine_id,
"type": "monitoring_failure",
"error": str(e),
"action": "manual_inspection_required"
})
# Step 2: Power generation forecast
try:
historical_data = [] # Would be fetched from database
power_forecast = power_forecaster.start(
f"Forecast power with weather {weather_forecast} and history {historical_data}"
)
results["power_forecast"] = power_forecast
# Grid integration check
if power_forecast.get("predicted_output"):
total_predicted = sum(power_forecast["predicted_output"])
grid_demand = power_forecast.get("grid_demand", 0)
if total_predicted < grid_demand * 0.9:
results["alerts"].append({
"type": "supply_shortage",
"predicted": total_predicted,
"demand": grid_demand,
"action": "consider_backup_sources"
})
except Exception as e:
# Fallback: Use conservative forecast
results["power_forecast"] = {
"status": "fallback_mode",
"conservative_estimate": len(turbine_ids) * 1.5, # MW
"error": str(e)
}
return results
# Cross-industry pattern adaptation
class EnergyPatternAdapter:
"""
Adapts manufacturing patterns for energy sector
Demonstrates 70% code reuse across industries
"""
@staticmethod
def adapt_for_solar_farm():
"""Adapt wind farm agents for solar farm monitoring"""
return Agent(
name="SolarMonitor",
instructions="""Monitor solar panel efficiency and temperature.
Track irradiance levels and predict power output.
Detect panel degradation and soiling issues.
SLA: Real-time monitoring with 2-second updates."""
)
@staticmethod
def adapt_for_power_grid():
"""Adapt for power grid management"""
return Agent(
name="GridBalancer",
instructions="""Balance power supply and demand across the grid.
Manage load distribution and prevent blackouts.
Coordinate with renewable and traditional sources.
SLA: Sub-second response for grid stability."""
)
@staticmethod
def adapt_for_battery_storage():
"""Adapt for battery energy storage systems"""
return Agent(
name="BatteryOptimizer",
instructions="""Optimize battery charge/discharge cycles.
Predict battery degradation and schedule replacements.
Maximize energy arbitrage opportunities.
SLA: Optimization decision within 10 seconds."""
)
# Fallback strategies for critical operations
class EnergyFallbackStrategies:
"""
Fallback strategies for maintaining grid stability
"""
@staticmethod
def turbine_failure_fallback(failed_turbine_id: str, available_turbines: List[str]):
"""Handle turbine failure by redistributing load"""
return {
"strategy": "load_redistribution",
"failed_turbine": failed_turbine_id,
"compensating_turbines": available_turbines[:3], # Use top 3 turbines
"increase_output_by": 0.3, # MW per turbine
"duration": "until_maintenance_complete"
}
@staticmethod
def forecast_failure_fallback(last_known_forecast: Optional[Dict]):
"""Handle forecasting system failure"""
if last_known_forecast:
return {
"strategy": "use_last_known",
"forecast": last_known_forecast,
"confidence": 0.6,
"validity": "4_hours"
}
else:
return {
"strategy": "seasonal_average",
"estimated_output": 50.0, # MW based on seasonal average
"confidence": 0.4,
"recommendation": "increase_reserves"
}
@staticmethod
def communication_failure_fallback():
"""Handle SCADA communication failure"""
return {
"strategy": "local_control_mode",
"actions": [
"Switch turbines to autonomous mode",
"Use local safety thresholds",
"Log data locally for later sync",
"Alert maintenance teams"
],
"recovery_check_interval": 300 # seconds
}
# Example usage
if __name__ == "__main__":
# Monitor wind farm
turbine_list = [f"WT-{i:03d}" for i in range(1, 11)] # 10 turbines
weather = {"wind_speed": 14.0, "direction": "NW", "temperature": 15.0}
result = energy_monitoring_workflow(turbine_list, weather)
print("Energy monitoring result:", result)
# Demonstrate cross-industry adaptation
solar_monitor = EnergyPatternAdapter.adapt_for_solar_farm()
grid_balancer = EnergyPatternAdapter.adapt_for_power_grid()
battery_optimizer = EnergyPatternAdapter.adapt_for_battery_storage()
# Fallback examples
turbine_fallback = EnergyFallbackStrategies.turbine_failure_fallback(
"WT-003", ["WT-001", "WT-002", "WT-004", "WT-005"]
)
print("Turbine failure fallback:", turbine_fallback)
@@ -1,478 +0,0 @@
"""
Healthcare Industry Template
============================
Emergency triage and patient care coordination workflow
Based on SRAO Framework with HIPAA-compliant patterns
Key agents:
- VitalSignsCapture: Processes patient vital signs
- EMRRetrieval: Retrieves electronic medical records
- TriageRecommendation: Provides triage recommendations
- ResourceAllocator: Manages hospital resource allocation
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Optional
from pydantic import BaseModel
from datetime import datetime
from enum import Enum
# Triage levels following Emergency Severity Index (ESI)
class TriageLevel(str, Enum):
RESUSCITATION = "1_resuscitation" # Immediate life-saving intervention
EMERGENT = "2_emergent" # High risk, severe pain/distress
URGENT = "3_urgent" # Stable but needs multiple resources
LESS_URGENT = "4_less_urgent" # Stable, one resource needed
NON_URGENT = "5_non_urgent" # Stable, no resources needed
# I/O Schemas
class VitalSigns(BaseModel):
"""Patient vital signs data"""
patient_id: str
timestamp: str
heart_rate: int # bpm
blood_pressure_systolic: int # mmHg
blood_pressure_diastolic: int # mmHg
respiratory_rate: int # breaths/min
temperature: float # Celsius
oxygen_saturation: int # percentage
pain_scale: int # 0-10
consciousness_level: str # alert, verbal, pain, unresponsive (AVPU)
class MedicalHistory(BaseModel):
"""Patient medical history from EMR"""
patient_id: str
allergies: List[str]
chronic_conditions: List[str]
current_medications: List[Dict[str, str]]
recent_visits: List[Dict[str, Any]]
emergency_contacts: List[Dict[str, str]]
insurance_status: str
class TriageAssessment(BaseModel):
"""Triage assessment and recommendations"""
assessment_id: str
patient_id: str
triage_level: TriageLevel
chief_complaint: str
recommended_department: str
estimated_wait_time: int # minutes
required_resources: List[str]
clinical_notes: str
red_flags: List[str]
class ResourceAllocation(BaseModel):
"""Hospital resource allocation"""
allocation_id: str
patient_id: str
assigned_bed: Optional[str]
assigned_staff: List[str]
equipment_needed: List[str]
department: str
priority_score: float
estimated_treatment_time: int # minutes
# Healthcare-specific tools
@tool
def capture_vital_signs(patient_id: str) -> Dict:
"""Capture patient vital signs from monitoring devices"""
# Simulate vital signs capture
return {
"patient_id": patient_id,
"timestamp": datetime.now().isoformat(),
"heart_rate": 78,
"blood_pressure_systolic": 130,
"blood_pressure_diastolic": 85,
"respiratory_rate": 16,
"temperature": 37.2,
"oxygen_saturation": 98,
"pain_scale": 6,
"consciousness_level": "alert"
}
@tool
def retrieve_medical_history(patient_id: str, consent_verified: bool = True) -> Dict:
"""Retrieve patient medical history from EMR system"""
if not consent_verified:
return {"error": "consent_required", "message": "Patient consent required for EMR access"}
# Simulate EMR retrieval
return {
"patient_id": patient_id,
"allergies": ["penicillin", "latex"],
"chronic_conditions": ["hypertension", "diabetes_type2"],
"current_medications": [
{"name": "metformin", "dose": "500mg", "frequency": "twice daily"},
{"name": "lisinopril", "dose": "10mg", "frequency": "once daily"}
],
"recent_visits": [
{"date": "2024-02-15", "reason": "routine_checkup", "department": "primary_care"}
],
"emergency_contacts": [
{"name": "Jane Doe", "relationship": "spouse", "phone": "555-0123"}
],
"insurance_status": "active"
}
@tool
def calculate_triage_priority(vital_signs: Dict, medical_history: Dict, chief_complaint: str) -> Dict:
"""Calculate triage priority based on clinical indicators"""
# Simulate triage algorithm
priority_score = 0
red_flags = []
# Check vital signs
if vital_signs["heart_rate"] > 120 or vital_signs["heart_rate"] < 50:
priority_score += 3
red_flags.append("abnormal_heart_rate")
if vital_signs["oxygen_saturation"] < 92:
priority_score += 4
red_flags.append("low_oxygen")
if vital_signs["consciousness_level"] != "alert":
priority_score += 5
red_flags.append("altered_consciousness")
# Determine triage level
if priority_score >= 8:
triage_level = TriageLevel.RESUSCITATION
department = "emergency"
wait_time = 0
elif priority_score >= 5:
triage_level = TriageLevel.EMERGENT
department = "emergency"
wait_time = 10
elif priority_score >= 3:
triage_level = TriageLevel.URGENT
department = "urgent_care"
wait_time = 30
else:
triage_level = TriageLevel.LESS_URGENT
department = "primary_care"
wait_time = 60
return {
"assessment_id": f"TRG-{datetime.now().strftime('%Y%m%d%H%M%S')}",
"patient_id": vital_signs["patient_id"],
"triage_level": triage_level.value,
"chief_complaint": chief_complaint,
"recommended_department": department,
"estimated_wait_time": wait_time,
"required_resources": ["physician", "nurse", "monitoring"],
"clinical_notes": f"Priority score: {priority_score}",
"red_flags": red_flags
}
@tool
def allocate_resources(patient_id: str, triage_level: str, department: str) -> Dict:
"""Allocate hospital resources based on triage assessment"""
# Simulate resource allocation
if "resuscitation" in triage_level:
bed = "ER-01"
staff = ["Dr. Smith", "Nurse Johnson", "Nurse Williams"]
equipment = ["cardiac_monitor", "defibrillator", "crash_cart"]
priority = 10.0
elif "emergent" in triage_level:
bed = "ER-05"
staff = ["Dr. Brown", "Nurse Davis"]
equipment = ["vital_signs_monitor", "IV_pump"]
priority = 8.0
else:
bed = "UC-12"
staff = ["PA Miller", "Nurse Garcia"]
equipment = ["basic_monitor"]
priority = 5.0
return {
"allocation_id": f"RES-{datetime.now().strftime('%Y%m%d%H%M%S')}",
"patient_id": patient_id,
"assigned_bed": bed,
"assigned_staff": staff,
"equipment_needed": equipment,
"department": department,
"priority_score": priority,
"estimated_treatment_time": 45
}
# Healthcare agent definitions with SLA requirements
vital_signs_agent = Agent(
name="VitalSignsCapture",
instructions="""You are a clinical vital signs specialist.
Capture and interpret patient vital signs from various monitoring devices.
Identify abnormal values and trends that require immediate attention.
Follow HIPAA guidelines for patient data handling.
SLA: Capture and process within 30 seconds.""",
tools=[capture_vital_signs]
)
emr_agent = Agent(
name="EMRRetrieval",
instructions="""You are an EMR system specialist with HIPAA compliance expertise.
Retrieve relevant patient medical history while ensuring data privacy.
Verify patient consent and maintain audit trails.
Highlight relevant allergies and contraindications.
SLA: Retrieve records within 5 seconds.""",
tools=[retrieve_medical_history]
)
triage_agent = Agent(
name="TriageRecommendation",
instructions="""You are an emergency triage specialist following ESI guidelines.
Assess patient severity and provide triage recommendations.
Consider vital signs, medical history, and presenting symptoms.
Identify red flags that require immediate intervention.
SLA: Complete assessment within 1 minute.""",
tools=[calculate_triage_priority]
)
resource_agent = Agent(
name="ResourceAllocator",
instructions="""You are a hospital resource management specialist.
Allocate beds, staff, and equipment based on patient needs and priority.
Optimize resource utilization while maintaining quality of care.
Coordinate between departments for efficient patient flow.
SLA: Allocate resources within 30 seconds.""",
tools=[allocate_resources]
)
# Healthcare workflow with safety protocols
def emergency_triage_workflow(patient_id: str, chief_complaint: str):
"""
Complete emergency triage workflow from arrival to resource allocation
Includes safety checks and fallback protocols
"""
workflow_result = {
"patient_id": patient_id,
"timestamp": datetime.now().isoformat(),
"status": "in_progress",
"safety_checks": [],
"clinical_pathway": []
}
# Step 1: Vital signs capture with validation
try:
vital_signs = vital_signs_agent.start(
f"Capture vital signs for patient {patient_id}"
)
workflow_result["clinical_pathway"].append({
"step": "vital_signs",
"status": "completed",
"data": vital_signs
})
# Safety check: Validate vital signs are within possible ranges
if vital_signs.get("heart_rate", 0) < 20 or vital_signs.get("heart_rate", 0) > 300:
workflow_result["safety_checks"].append({
"type": "vital_signs_validation",
"issue": "impossible_heart_rate",
"action": "manual_verification_required"
})
except Exception as e:
# Fallback: Manual vital signs entry
workflow_result["clinical_pathway"].append({
"step": "vital_signs",
"status": "manual_entry_required",
"error": str(e)
})
vital_signs = {"manual_entry": True}
# Step 2: EMR retrieval with consent check
try:
medical_history = emr_agent.start(
f"Retrieve medical history for patient {patient_id} with verified consent"
)
workflow_result["clinical_pathway"].append({
"step": "emr_retrieval",
"status": "completed",
"records_retrieved": True
})
# Safety check: Allergy alerts
if medical_history.get("allergies"):
workflow_result["safety_checks"].append({
"type": "allergy_alert",
"allergies": medical_history["allergies"],
"action": "notify_all_providers"
})
except Exception as e:
# Fallback: Proceed with limited information
medical_history = {
"patient_id": patient_id,
"allergies": [],
"note": "EMR_unavailable_proceed_with_caution"
}
workflow_result["safety_checks"].append({
"type": "emr_unavailable",
"action": "collect_history_manually"
})
# Step 3: Triage assessment
try:
triage_assessment = triage_agent.start(
f"Assess triage for patient with vitals {vital_signs}, "
f"history {medical_history}, complaint: {chief_complaint}"
)
workflow_result["clinical_pathway"].append({
"step": "triage_assessment",
"status": "completed",
"triage_level": triage_assessment.get("triage_level")
})
# Safety check: Red flags requiring immediate action
if triage_assessment.get("red_flags"):
workflow_result["safety_checks"].append({
"type": "clinical_red_flags",
"flags": triage_assessment["red_flags"],
"action": "immediate_physician_notification"
})
except Exception as e:
# Fallback: Conservative triage (assume urgent)
triage_assessment = {
"triage_level": TriageLevel.URGENT.value,
"recommended_department": "emergency",
"fallback": True
}
# Step 4: Resource allocation
try:
resource_allocation = resource_agent.start(
f"Allocate resources for patient {patient_id} "
f"with triage {triage_assessment.get('triage_level')} "
f"to {triage_assessment.get('recommended_department')}"
)
workflow_result["clinical_pathway"].append({
"step": "resource_allocation",
"status": "completed",
"bed_assigned": resource_allocation.get("assigned_bed"),
"staff_assigned": resource_allocation.get("assigned_staff")
})
except Exception as e:
# Fallback: Queue for next available
workflow_result["clinical_pathway"].append({
"step": "resource_allocation",
"status": "queued",
"queue_position": "next_available",
"error": str(e)
})
workflow_result["status"] = "completed"
return workflow_result
# HIPAA-compliant data handling patterns
class HIPAACompliancePatterns:
"""
Reusable patterns for HIPAA-compliant healthcare workflows
"""
@staticmethod
def anonymize_patient_data(data: Dict) -> Dict:
"""Remove PHI for analytics and reporting"""
safe_fields = ["triage_level", "department", "wait_time", "resource_type"]
return {k: v for k, v in data.items() if k in safe_fields}
@staticmethod
def audit_log_access(user_id: str, patient_id: str, action: str, reason: str):
"""Create audit trail for EMR access"""
return {
"timestamp": datetime.now().isoformat(),
"user_id": user_id,
"patient_id": patient_id,
"action": action,
"reason": reason,
"ip_address": "10.0.0.1", # Would be actual IP
"session_id": "SEC-" + datetime.now().strftime('%Y%m%d%H%M%S')
}
@staticmethod
def encrypt_sensitive_data(data: Dict) -> Dict:
"""Encrypt sensitive fields (placeholder for real encryption)"""
sensitive_fields = ["ssn", "dob", "address", "phone"]
encrypted = data.copy()
for field in sensitive_fields:
if field in encrypted:
encrypted[field] = f"ENCRYPTED_{field.upper()}"
return encrypted
# Cross-department coordination patterns
class HealthcareCoordinationPatterns:
"""
Patterns for coordinating across healthcare departments
"""
@staticmethod
def coordinate_lab_orders(patient_id: str, triage_level: str) -> Agent:
"""Create agent for lab order coordination"""
return Agent(
name="LabCoordinator",
instructions=f"""Coordinate laboratory orders for patient {patient_id}.
Priority level: {triage_level}.
Ensure STAT orders for critical patients.
Track specimen collection and result delivery.
SLA: Order placement within 2 minutes."""
)
@staticmethod
def coordinate_radiology(patient_id: str, imaging_type: str) -> Agent:
"""Create agent for radiology coordination"""
return Agent(
name="RadiologyCoordinator",
instructions=f"""Schedule {imaging_type} imaging for patient {patient_id}.
Check for contrast allergies and pregnancy status.
Coordinate with transport for patient movement.
SLA: Schedule within 5 minutes for urgent cases."""
)
@staticmethod
def coordinate_pharmacy(patient_id: str, medications: List[str]) -> Agent:
"""Create agent for pharmacy coordination"""
return Agent(
name="PharmacyCoordinator",
instructions=f"""Verify and dispense medications for patient {patient_id}.
Check for drug interactions and allergies.
Ensure proper dosing based on patient parameters.
SLA: Medication verification within 3 minutes."""
)
# Example usage
if __name__ == "__main__":
# Process emergency patient
patient_id = "PT-2024-00123"
chief_complaint = "severe chest pain radiating to left arm"
result = emergency_triage_workflow(patient_id, chief_complaint)
print("Triage workflow result:", result)
# Create specialized coordinators
lab_coordinator = HealthcareCoordinationPatterns.coordinate_lab_orders(
patient_id, TriageLevel.EMERGENT.value
)
radiology_coordinator = HealthcareCoordinationPatterns.coordinate_radiology(
patient_id, "chest_xray"
)
pharmacy_coordinator = HealthcareCoordinationPatterns.coordinate_pharmacy(
patient_id, ["aspirin", "nitroglycerin"]
)
# HIPAA compliance example
audit_log = HIPAACompliancePatterns.audit_log_access(
"DR-001", patient_id, "EMR_ACCESS", "emergency_triage"
)
print("Audit log created:", audit_log)
@@ -1,298 +0,0 @@
"""
Manufacturing Industry Template
================================
Order scheduling, inventory management, and quality inspection workflow
Based on SRAO Framework patterns with 70% cross-industry reuse
Key agents:
- ParseOrder: Extracts order details from various formats
- CheckInventory: Validates material availability
- OptimizeSchedule: Optimizes production scheduling
- DefectDetect: Quality control and defect detection
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any
from pydantic import BaseModel
# I/O Schemas
class OrderDetails(BaseModel):
"""Structured order information"""
order_id: str
customer_id: str
product_id: str
quantity: int
priority: str = "normal" # normal, high, urgent
due_date: str
requirements: Dict[str, Any] = {}
class InventoryStatus(BaseModel):
"""Material availability status"""
material_id: str
available_quantity: int
reorder_level: int
supplier_lead_time: int
location: str
class ProductionSchedule(BaseModel):
"""Optimized production schedule"""
schedule_id: str
production_line: str
start_time: str
end_time: str
assigned_workers: List[str]
estimated_completion: str
bottleneck_analysis: Dict[str, Any] = {}
class QualityReport(BaseModel):
"""Quality inspection results"""
batch_id: str
inspection_time: str
defect_rate: float
defect_types: List[str]
passed: bool
recommendations: List[str]
# Tools for agents
@tool
def extract_order_details(text: str) -> Dict:
"""Extract structured order information from unstructured text"""
# Simulate order parsing logic
return {
"order_id": "ORD-2024-001",
"customer_id": "CUST-123",
"product_id": "PROD-456",
"quantity": 100,
"priority": "high",
"due_date": "2024-03-15",
"requirements": {"material": "steel", "finish": "polished"}
}
@tool
def check_material_availability(product_id: str, quantity: int) -> Dict:
"""Check if materials are available for production"""
# Simulate inventory check
return {
"material_id": "MAT-789",
"available_quantity": 500,
"reorder_level": 100,
"supplier_lead_time": 7,
"location": "Warehouse-A"
}
@tool
def calculate_optimal_schedule(order: Dict, inventory: Dict) -> Dict:
"""Calculate optimal production schedule based on constraints"""
# Simulate scheduling optimization
return {
"schedule_id": "SCH-2024-001",
"production_line": "Line-3",
"start_time": "2024-03-10T08:00:00",
"end_time": "2024-03-14T17:00:00",
"assigned_workers": ["Worker-001", "Worker-002"],
"estimated_completion": "2024-03-14T16:30:00",
"bottleneck_analysis": {
"critical_path": "Assembly",
"buffer_time": 2.5
}
}
@tool
def perform_quality_inspection(batch_id: str) -> Dict:
"""Perform automated quality inspection on production batch"""
# Simulate quality control
return {
"batch_id": batch_id,
"inspection_time": "2024-03-14T17:00:00",
"defect_rate": 0.02,
"defect_types": ["surface_scratch", "dimension_variance"],
"passed": True,
"recommendations": [
"Adjust machine calibration on Line-3",
"Increase inspection frequency for next batch"
]
}
# Agent Definitions with SLA requirements
order_parser = Agent(
name="ParseOrder",
instructions="""You are an order parsing specialist. Extract and structure order information
from various formats (email, PDF, API). Ensure all required fields are captured and validated.
SLA: Process within 30 seconds per order.""",
tools=[extract_order_details]
)
inventory_checker = Agent(
name="CheckInventory",
instructions="""You are an inventory management specialist. Verify material availability,
check reorder points, and identify potential supply chain issues.
SLA: Real-time response within 5 seconds.""",
tools=[check_material_availability]
)
schedule_optimizer = Agent(
name="OptimizeSchedule",
instructions="""You are a production scheduling expert. Optimize production schedules
based on order priority, resource availability, and constraints. Minimize idle time
and maximize throughput.
SLA: Generate schedule within 2 minutes for complex orders.""",
tools=[calculate_optimal_schedule]
)
quality_inspector = Agent(
name="DefectDetect",
instructions="""You are a quality control specialist. Perform automated inspections,
detect defects using vision analysis, and provide improvement recommendations.
SLA: Complete inspection within 1 minute per batch.""",
tools=[perform_quality_inspection]
)
# Workflow with fallback strategies
def manufacturing_workflow(order_text: str):
"""
Complete manufacturing workflow from order to quality control
Includes fallback strategies for each critical step
"""
# Step 1: Parse order with fallback
try:
order_details = order_parser.start(f"Parse this order: {order_text}")
if not order_details:
# Fallback: Manual review queue
return {"status": "manual_review_required", "reason": "order_parsing_failed"}
except Exception as e:
return {"status": "error", "stage": "order_parsing", "error": str(e)}
# Step 2: Check inventory with fallback
try:
inventory_status = inventory_checker.start(
f"Check materials for order: {order_details}"
)
if not inventory_status:
# Fallback: Alternative supplier check
return {"status": "alternative_supplier_needed", "order": order_details}
except Exception as e:
# Fallback: Conservative estimate with safety margin
inventory_status = {"estimated": True, "safety_margin": 1.5}
# Step 3: Optimize schedule with fallback
try:
schedule = schedule_optimizer.start(
f"Create schedule for order {order_details} with inventory {inventory_status}"
)
if not schedule:
# Fallback: Standard scheduling template
schedule = {"template": "standard", "priority": "normal"}
except Exception as e:
# Fallback: Queue for next available slot
schedule = {"status": "queued", "estimated_start": "next_available"}
# Step 4: Quality inspection with fallback
try:
quality_report = quality_inspector.start(
f"Inspect batch for schedule {schedule}"
)
if not quality_report:
# Fallback: Manual inspection required
quality_report = {"requires_manual": True, "inspector_assigned": "QC-Team-1"}
except Exception as e:
# Fallback: Quarantine for review
quality_report = {"status": "quarantined", "review_required": True}
return {
"status": "completed",
"order": order_details,
"inventory": inventory_status,
"schedule": schedule,
"quality": quality_report
}
# Cross-industry reusable patterns (70% reuse)
class IndustryAgentPattern:
"""
Base pattern reusable across industries
Provides common agent capabilities that can be specialized
"""
@staticmethod
def create_data_parser(name: str, domain: str, sla_seconds: int = 30):
"""Create a data parsing agent for any domain"""
return Agent(
name=name,
instructions=f"""You are a {domain} data parsing specialist.
Extract and structure information from various formats.
Validate all required fields according to {domain} standards.
SLA: Process within {sla_seconds} seconds."""
)
@staticmethod
def create_resource_checker(name: str, resource_type: str, sla_seconds: int = 5):
"""Create a resource availability checker for any resource type"""
return Agent(
name=name,
instructions=f"""You are a {resource_type} availability specialist.
Verify {resource_type} availability and identify constraints.
Provide real-time status and alternative options if needed.
SLA: Response within {sla_seconds} seconds."""
)
@staticmethod
def create_optimizer(name: str, optimization_target: str, sla_minutes: int = 2):
"""Create an optimization agent for any target metric"""
return Agent(
name=name,
instructions=f"""You are an optimization expert for {optimization_target}.
Find optimal solutions considering all constraints and priorities.
Minimize waste and maximize efficiency.
SLA: Generate solution within {sla_minutes} minutes."""
)
@staticmethod
def create_inspector(name: str, inspection_type: str, sla_minutes: int = 1):
"""Create an inspection agent for any quality metric"""
return Agent(
name=name,
instructions=f"""You are a {inspection_type} inspection specialist.
Perform automated analysis and detect anomalies.
Provide actionable recommendations for improvement.
SLA: Complete inspection within {sla_minutes} minute(s)."""
)
# Example usage
if __name__ == "__main__":
# Create a manufacturing workflow instance
order = "Customer ABC needs 100 units of Product-XYZ by March 15th, high priority"
# Option 1: Use specialized agents
result = manufacturing_workflow(order)
print("Manufacturing workflow result:", result)
# Option 2: Use reusable patterns for other industries
# These patterns can be adapted for Energy, Healthcare, Agriculture, Transportation
energy_parser = IndustryAgentPattern.create_data_parser(
"EnergyDataParser", "wind farm telemetry", sla_seconds=10
)
healthcare_checker = IndustryAgentPattern.create_resource_checker(
"BedAvailability", "hospital bed", sla_seconds=3
)
agriculture_optimizer = IndustryAgentPattern.create_optimizer(
"IrrigationOptimizer", "water usage", sla_minutes=5
)
transport_inspector = IndustryAgentPattern.create_inspector(
"TunnelSafety", "structural integrity", sla_minutes=2
)
@@ -1,599 +0,0 @@
"""
Transportation Industry Template
================================
Tunnel safety monitoring and infrastructure management workflow
Based on SRAO Framework with LiDAR and sensor fusion
Key agents:
- LiDARFusion: Processes LiDAR point cloud data
- DisplacementCalculator: Calculates structural displacement
- HeatmapGenerator: Generates safety heatmaps
- MaintenancePlanner: Plans infrastructure maintenance
"""
from praisonaiagents import Agent, tool
from typing import Dict, List, Any, Tuple, Optional
from pydantic import BaseModel
from datetime import datetime, timedelta
from enum import Enum
import math
# Safety levels for infrastructure
class SafetyLevel(str, Enum):
SAFE = "safe"
CAUTION = "caution"
WARNING = "warning"
DANGER = "danger"
CRITICAL = "critical"
# Infrastructure types
class InfrastructureType(str, Enum):
TUNNEL = "tunnel"
BRIDGE = "bridge"
HIGHWAY = "highway"
RAILWAY = "railway"
AIRPORT = "airport"
# I/O Schemas
class LiDARScan(BaseModel):
"""LiDAR scan data for infrastructure"""
scan_id: str
infrastructure_id: str
scan_time: str
point_cloud_size: int # Number of points
scan_resolution: float # Points per square meter
coverage_area: float # Square meters
anomaly_points: List[Tuple[float, float, float]] # XYZ coordinates
reference_baseline: Optional[Dict[str, Any]]
class DisplacementAnalysis(BaseModel):
"""Structural displacement analysis"""
analysis_id: str
infrastructure_id: str
analysis_time: str
max_displacement: float # millimeters
displacement_vector: Tuple[float, float, float] # XYZ displacement
critical_zones: List[Dict[str, Any]]
settlement_rate: float # mm/month
tilt_angle: float # degrees
safety_factor: float
class SafetyHeatmap(BaseModel):
"""Infrastructure safety heatmap"""
heatmap_id: str
infrastructure_id: str
generation_time: str
grid_resolution: float # meters
safety_scores: List[List[float]] # 2D grid of safety scores
critical_areas: List[Dict[str, Any]]
overall_safety: SafetyLevel
risk_trends: Dict[str, Any]
class MaintenanceSchedule(BaseModel):
"""Maintenance planning schedule"""
schedule_id: str
infrastructure_id: str
maintenance_type: str # preventive, corrective, emergency
priority_level: str # low, medium, high, critical
scheduled_date: str
estimated_duration: int # hours
required_resources: List[str]
traffic_impact: str
cost_estimate: float
# Transportation-specific tools
@tool
def process_lidar_scan(infrastructure_id: str, scan_type: str = "full") -> Dict:
"""Process LiDAR point cloud data for structural analysis"""
# Simulate LiDAR processing
return {
"scan_id": f"LIDAR-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": infrastructure_id,
"scan_time": datetime.now().isoformat(),
"point_cloud_size": 5000000, # 5 million points
"scan_resolution": 100.0, # points/m²
"coverage_area": 50000.0, # m²
"anomaly_points": [
(10.5, 20.3, 5.1),
(15.2, 25.8, 5.3),
(20.1, 30.5, 5.5)
],
"reference_baseline": {
"date": "2024-01-01",
"baseline_id": "BSL-001"
}
}
@tool
def calculate_displacement(current_scan: Dict, baseline_scan: Dict) -> Dict:
"""Calculate structural displacement from LiDAR scans"""
# Simulate displacement calculation
anomalies = current_scan.get("anomaly_points", [])
if len(anomalies) > 5:
max_displacement = 15.0
safety_factor = 0.6
safety_level = SafetyLevel.WARNING
elif len(anomalies) > 2:
max_displacement = 8.0
safety_factor = 0.8
safety_level = SafetyLevel.CAUTION
else:
max_displacement = 3.0
safety_factor = 0.95
safety_level = SafetyLevel.SAFE
return {
"analysis_id": f"DISP-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": current_scan["infrastructure_id"],
"analysis_time": datetime.now().isoformat(),
"max_displacement": max_displacement,
"displacement_vector": (2.5, 1.8, max_displacement),
"critical_zones": [
{
"zone_id": "CZ-001",
"location": "tunnel_crown",
"displacement": max_displacement * 0.8,
"risk": "high" if max_displacement > 10 else "medium"
}
],
"settlement_rate": max_displacement / 30, # mm/month
"tilt_angle": math.degrees(math.atan(max_displacement / 1000)),
"safety_factor": safety_factor
}
@tool
def generate_safety_heatmap(displacement_data: Dict, sensor_data: List[Dict]) -> Dict:
"""Generate safety heatmap visualization for infrastructure"""
# Simulate heatmap generation
safety_factor = displacement_data.get("safety_factor", 1.0)
# Create grid (simplified 10x10)
grid_size = 10
safety_grid = []
critical_areas = []
for i in range(grid_size):
row = []
for j in range(grid_size):
# Generate safety score based on position and displacement
base_score = safety_factor
distance_factor = math.sqrt((i-5)**2 + (j-5)**2) / 10
safety_score = min(1.0, base_score + (0.2 * distance_factor))
row.append(safety_score)
if safety_score < 0.7:
critical_areas.append({
"grid_position": (i, j),
"safety_score": safety_score,
"recommended_action": "immediate_inspection"
})
safety_grid.append(row)
# Determine overall safety
avg_safety = sum(sum(row) for row in safety_grid) / (grid_size * grid_size)
if avg_safety > 0.9:
overall = SafetyLevel.SAFE
elif avg_safety > 0.8:
overall = SafetyLevel.CAUTION
elif avg_safety > 0.7:
overall = SafetyLevel.WARNING
else:
overall = SafetyLevel.DANGER
return {
"heatmap_id": f"HEAT-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": displacement_data["infrastructure_id"],
"generation_time": datetime.now().isoformat(),
"grid_resolution": 5.0, # meters
"safety_scores": safety_grid,
"critical_areas": critical_areas,
"overall_safety": overall.value,
"risk_trends": {
"trend": "deteriorating" if avg_safety < 0.8 else "stable",
"rate": -0.02 if avg_safety < 0.8 else 0.0
}
}
@tool
def plan_maintenance(infrastructure_id: str, safety_analysis: Dict, traffic_data: Dict) -> Dict:
"""Plan maintenance based on safety analysis and traffic patterns"""
safety_level = safety_analysis.get("overall_safety", "safe")
# Determine maintenance urgency
if safety_level in ["danger", "critical"]:
maintenance_type = "emergency"
priority = "critical"
days_until = 1
duration = 48
elif safety_level == "warning":
maintenance_type = "corrective"
priority = "high"
days_until = 7
duration = 24
elif safety_level == "caution":
maintenance_type = "preventive"
priority = "medium"
days_until = 30
duration = 12
else:
maintenance_type = "preventive"
priority = "low"
days_until = 90
duration = 8
# Traffic impact assessment
peak_traffic = traffic_data.get("peak_volume", 1000)
if peak_traffic > 5000:
traffic_impact = "severe"
cost_multiplier = 2.0
elif peak_traffic > 2000:
traffic_impact = "moderate"
cost_multiplier = 1.5
else:
traffic_impact = "minimal"
cost_multiplier = 1.0
return {
"schedule_id": f"MAINT-{datetime.now().strftime('%Y%m%d%H%M')}",
"infrastructure_id": infrastructure_id,
"maintenance_type": maintenance_type,
"priority_level": priority,
"scheduled_date": (datetime.now() + timedelta(days=days_until)).isoformat(),
"estimated_duration": duration,
"required_resources": [
"inspection_team",
"repair_crew",
"safety_equipment",
"traffic_control"
],
"traffic_impact": traffic_impact,
"cost_estimate": 50000 * cost_multiplier * (duration / 24)
}
# Transportation agent definitions
lidar_agent = Agent(
name="LiDARFusion",
instructions="""You are a LiDAR data processing specialist for infrastructure.
Process point cloud data from mobile and terrestrial LiDAR scanners.
Perform data fusion from multiple sensor sources.
Identify structural anomalies and deformations.
SLA: Process scan data within 5 minutes.""",
tools=[process_lidar_scan]
)
displacement_agent = Agent(
name="DisplacementCalculator",
instructions="""You are a structural displacement analysis expert.
Calculate precise displacement measurements from temporal scans.
Analyze settlement patterns and structural movements.
Predict future displacement trends using ML models.
SLA: Complete analysis within 2 minutes.""",
tools=[calculate_displacement]
)
heatmap_agent = Agent(
name="HeatmapGenerator",
instructions="""You are a safety visualization specialist.
Generate intuitive safety heatmaps for infrastructure monitoring.
Integrate multiple data sources for comprehensive risk assessment.
Highlight critical areas requiring immediate attention.
SLA: Generate heatmap within 1 minute.""",
tools=[generate_safety_heatmap]
)
maintenance_agent = Agent(
name="MaintenancePlanner",
instructions="""You are an infrastructure maintenance planning expert.
Schedule maintenance based on structural health and traffic patterns.
Optimize maintenance windows to minimize disruption.
Coordinate resources and estimate costs.
SLA: Create maintenance plan within 3 minutes.""",
tools=[plan_maintenance]
)
# Transportation infrastructure monitoring workflow
def infrastructure_monitoring_workflow(
infrastructure_ids: List[str],
infrastructure_type: InfrastructureType,
traffic_data: Dict
):
"""
Complete infrastructure monitoring workflow from scanning to maintenance
Includes safety assessment and predictive maintenance
"""
monitoring_results = {
"timestamp": datetime.now().isoformat(),
"infrastructure_type": infrastructure_type.value,
"structures_monitored": len(infrastructure_ids),
"safety_assessments": [],
"maintenance_required": [],
"emergency_alerts": [],
"network_health_score": 0.0
}
total_safety_score = 0.0
for infra_id in infrastructure_ids:
try:
# Step 1: LiDAR scanning
lidar_scan = lidar_agent.start(
f"Process LiDAR scan for {infrastructure_type.value} {infra_id}"
)
# Step 2: Displacement analysis
# Get baseline (would be from database in real scenario)
baseline = {"scan_id": "BSL-001", "point_cloud": []}
displacement_analysis = displacement_agent.start(
f"Calculate displacement for scan: {lidar_scan} against baseline: {baseline}"
)
# Step 3: Generate safety heatmap
sensor_data = [] # Would include IoT sensor readings
safety_heatmap = heatmap_agent.start(
f"Generate heatmap for displacement: {displacement_analysis} "
f"with sensors: {sensor_data}"
)
monitoring_results["safety_assessments"].append({
"infrastructure_id": infra_id,
"safety_level": safety_heatmap.get("overall_safety"),
"max_displacement": displacement_analysis.get("max_displacement"),
"critical_zones": len(safety_heatmap.get("critical_areas", []))
})
# Check for emergency conditions
if safety_heatmap.get("overall_safety") in ["danger", "critical"]:
monitoring_results["emergency_alerts"].append({
"infrastructure_id": infra_id,
"alert_level": "emergency",
"safety_status": safety_heatmap["overall_safety"],
"immediate_action": "close_to_traffic",
"notification_sent": True
})
# Step 4: Emergency maintenance planning
maintenance_plan = maintenance_agent.start(
f"Plan emergency maintenance for {infra_id} "
f"with safety: {safety_heatmap} and traffic: {traffic_data}"
)
monitoring_results["maintenance_required"].append(maintenance_plan)
elif safety_heatmap.get("overall_safety") in ["warning", "caution"]:
# Regular maintenance planning
maintenance_plan = maintenance_agent.start(
f"Plan maintenance for {infra_id} "
f"with safety: {safety_heatmap} and traffic: {traffic_data}"
)
monitoring_results["maintenance_required"].append(maintenance_plan)
# Calculate safety score contribution
safety_mapping = {
"safe": 1.0, "caution": 0.8, "warning": 0.6,
"danger": 0.3, "critical": 0.1
}
total_safety_score += safety_mapping.get(
safety_heatmap.get("overall_safety", "safe"), 0.5
)
except Exception as e:
# Fallback: Manual inspection
monitoring_results["emergency_alerts"].append({
"infrastructure_id": infra_id,
"alert_level": "system_failure",
"error": str(e),
"action": "dispatch_manual_inspection_team"
})
# Calculate network health score
if infrastructure_ids:
monitoring_results["network_health_score"] = (
total_safety_score / len(infrastructure_ids) * 100
)
return monitoring_results
# Multi-modal transportation patterns
class MultiModalTransportPatterns:
"""
Patterns for different transportation infrastructure types
Demonstrates cross-industry reuse for transportation sector
"""
@staticmethod
def adapt_for_bridge_monitoring() -> Agent:
"""Adapt tunnel monitoring for bridge structures"""
return Agent(
name="BridgeMonitor",
instructions="""Monitor bridge structural health using sensors.
Track deck deflection, cable tension, and vibration patterns.
Detect fatigue cracks and corrosion.
Consider wind and seismic loads.
SLA: Real-time monitoring with 10-second updates."""
)
@staticmethod
def adapt_for_railway_tracks() -> Agent:
"""Adapt for railway track monitoring"""
return Agent(
name="RailwayMonitor",
instructions="""Monitor railway track geometry and condition.
Detect rail defects, gauge variations, and ballast degradation.
Analyze wheel-rail interaction forces.
Predict track maintenance needs.
SLA: Process track inspection data within 3 minutes per km."""
)
@staticmethod
def adapt_for_airport_runways() -> Agent:
"""Adapt for airport runway monitoring"""
return Agent(
name="RunwayMonitor",
instructions="""Monitor airport runway surface conditions.
Detect FOD (Foreign Object Debris) and surface irregularities.
Assess pavement condition and friction levels.
Monitor drainage and lighting systems.
SLA: Complete runway scan within 10 minutes."""
)
# Intelligent traffic management integration
class TrafficManagementPatterns:
"""
Patterns for integrating with intelligent traffic systems
"""
@staticmethod
def optimize_traffic_flow(maintenance_window: Dict, traffic_patterns: Dict) -> Dict:
"""Optimize traffic flow during maintenance"""
peak_hours = traffic_patterns.get("peak_hours", [7, 9, 17, 19])
maintenance_start = maintenance_window.get("start_time", "02:00")
# Calculate optimal diversion routes
return {
"strategy": "dynamic_routing",
"diversion_routes": [
{"route_id": "ALT-1", "capacity": 0.6, "added_time": 15},
{"route_id": "ALT-2", "capacity": 0.3, "added_time": 20}
],
"lane_management": {
"reversible_lanes": True,
"peak_direction": "inbound" if int(maintenance_start[:2]) < 12 else "outbound"
},
"public_transport": {
"increase_frequency": True,
"additional_services": 4
},
"estimated_delay": 10 # minutes
}
@staticmethod
def incident_response_plan(incident_type: str, location: Dict) -> Dict:
"""Generate incident response plan"""
response_times = {
"accident": 5,
"breakdown": 10,
"infrastructure_failure": 2,
"weather_hazard": 15
}
return {
"incident_id": f"INC-{datetime.now().strftime('%Y%m%d%H%M')}",
"response_time": response_times.get(incident_type, 10),
"resources_dispatched": [
r for r in [
"emergency_response_team",
"traffic_control_unit",
"maintenance_crew" if "infrastructure" in incident_type else None
] if r is not None
],
"traffic_management": {
"lane_closures": 2,
"speed_reduction": 30, # km/h
"message_signs": ["Incident ahead", "Reduce speed", "Merge left"]
},
"estimated_clearance": 45 # minutes
}
# Predictive maintenance patterns
class PredictiveMaintenancePatterns:
"""
ML-based predictive maintenance for transportation infrastructure
"""
@staticmethod
def predict_failure_probability(
displacement_trend: List[float],
environmental_factors: Dict
) -> float:
"""Predict probability of structural failure"""
# Simplified prediction model
trend_slope = (displacement_trend[-1] - displacement_trend[0]) / len(displacement_trend)
temperature_factor = abs(environmental_factors.get("temp_variation", 0)) / 50
moisture_factor = environmental_factors.get("humidity", 50) / 100
base_probability = 0.1
trend_contribution = max(0, trend_slope * 10)
env_contribution = (temperature_factor + moisture_factor) * 0.2
return min(1.0, base_probability + trend_contribution + env_contribution)
@staticmethod
def optimize_maintenance_schedule(
infrastructure_conditions: List[Dict],
budget_constraint: float
) -> List[Dict]:
"""Optimize maintenance schedule across infrastructure network"""
# Sort by urgency (safety_factor * cost_efficiency)
prioritized = sorted(
infrastructure_conditions,
key=lambda x: (1 - x.get("safety_factor", 1)) * (1 / x.get("cost", 1)),
reverse=True
)
scheduled = []
total_cost = 0
for infra in prioritized:
if total_cost + infra.get("cost", 0) <= budget_constraint:
scheduled.append({
"infrastructure_id": infra["id"],
"scheduled_date": infra["recommended_date"],
"priority": "high" if infra["safety_factor"] < 0.7 else "medium"
})
total_cost += infra.get("cost", 0)
return scheduled
# Example usage
if __name__ == "__main__":
# Monitor tunnel network
tunnel_ids = [f"TUN-{i:03d}" for i in range(1, 6)]
traffic = {
"peak_volume": 3000,
"off_peak_volume": 800,
"peak_hours": [7, 9, 17, 19]
}
result = infrastructure_monitoring_workflow(
tunnel_ids,
InfrastructureType.TUNNEL,
traffic
)
print("Infrastructure monitoring result:", result)
# Multi-modal adaptation examples
bridge_monitor = MultiModalTransportPatterns.adapt_for_bridge_monitoring()
railway_monitor = MultiModalTransportPatterns.adapt_for_railway_tracks()
runway_monitor = MultiModalTransportPatterns.adapt_for_airport_runways()
# Traffic management example
maintenance = {"start_time": "22:00", "duration": 8}
traffic_plan = TrafficManagementPatterns.optimize_traffic_flow(maintenance, traffic)
print("Traffic optimization plan:", traffic_plan)
# Predictive maintenance example
displacement_history = [2.0, 2.3, 2.8, 3.5, 4.3] # mm over months
environment = {"temp_variation": 30, "humidity": 75}
failure_prob = PredictiveMaintenancePatterns.predict_failure_probability(
displacement_history, environment
)
print(f"Failure probability: {failure_prob:.2%}")
@@ -80,7 +80,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
print(f"\nAnswer: {response}\n")
# Verify the answer contains the unique code
if "ZEBRA-71" in str(response or "").upper():
if "ZEBRA-71" in response.upper():
print("✅ VERIFIED: Agent correctly retrieved the unique code from context!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
@@ -104,7 +104,7 @@ Be precise and quote exact values when asked about codes or numbers.""",
response = agent.chat("What is the security incident code?")
print(f"\nAnswer: {response}\n")
if "HAWK-88" in str(response or "").upper():
if "HAWK-88" in response.upper():
print("✅ VERIFIED: Agent correctly retrieved the security code!\n")
else:
print("❌ WARNING: Agent did not find the code - check retrieval\n")
+3 -13
View File
@@ -19,16 +19,6 @@ from praisonaiagents import Agent, AgentTeam
# Initialize observability
obs.init()
def set_span_attr(span, key, value):
"""Set span attribute safely across observability implementations."""
if hasattr(span, "set_attribute"):
span.set_attribute(key, value)
return
attrs = getattr(span, "attributes", None)
if attrs is not None:
attrs[key] = value
# Create agents
researcher = Agent(
name="Researcher",
@@ -47,13 +37,13 @@ with obs.trace("content-creation", metadata={"workflow": "research-write"}):
# Research phase
with obs.span("research-phase", kind=SpanKind.AGENT) as research_span:
set_span_attr(research_span, "agent", "Researcher")
research_span.attributes["agent"] = "Researcher"
research_result = researcher.chat("Research the benefits of AI in healthcare")
# Writing phase
with obs.span("writing-phase", kind=SpanKind.AGENT) as write_span:
set_span_attr(write_span, "agent", "Writer")
set_span_attr(write_span, "input_length", len(str(research_result)))
write_span.attributes["agent"] = "Writer"
write_span.attributes["input_length"] = len(str(research_result))
final_content = writer.chat(f"Write a blog post based on: {research_result}")
print("Final Content:")
+5 -8
View File
@@ -13,8 +13,7 @@ Expected output:
- Session can be resumed later with same session_id
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
# Create database connection (PostgreSQL + Redis)
my_db = db(
@@ -26,12 +25,10 @@ my_db = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. Be concise.",
memory={
"db": my_db,
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
# Set explicitly for session continuity across runs:
"session_id": "my-persistent-session",
},
db=my_db,
# session_id is optional - defaults to per-hour ID (YYYYMMDDHH-hash)
# Set explicitly for session continuity across runs:
session_id="my-persistent-session"
)
# Chat - messages are automatically persisted
+3 -6
View File
@@ -12,8 +12,7 @@ Expected output:
- Export shows full session data
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
def get_weather(city: str) -> str:
"""Get weather for a city."""
@@ -30,10 +29,8 @@ agent = Agent(
name="WeatherBot",
instructions="You help with weather. Use the get_weather tool when asked.",
tools=[get_weather],
memory={
"db": my_db,
"session_id": "weather-session-001",
},
db=my_db,
session_id="weather-session-001"
)
# First chat - triggers run tracking
+3 -6
View File
@@ -13,8 +13,7 @@ Expected output:
Agent responds with state persisted to Redis
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
print("=== Redis State Store (Agent-First) ===")
@@ -27,10 +26,8 @@ my_db = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
memory={
"db": my_db,
"session_id": "redis-state-session",
},
db=my_db,
session_id="redis-state-session"
)
# Chat - state is automatically managed via Redis
+3 -6
View File
@@ -11,8 +11,7 @@ Expected output:
- Second run: Resumes with history
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
# Create database
my_db = db(
@@ -26,10 +25,8 @@ SESSION_ID = "resume-demo-session"
agent = Agent(
name="MemoryBot",
instructions="You remember our conversation. Be helpful and concise.",
memory={
"db": my_db,
"session_id": SESSION_ID,
},
db=my_db,
session_id=SESSION_ID
)
# Check if this is a new or resumed session
+3 -6
View File
@@ -26,8 +26,7 @@ Expected Output:
Messages persisted: 2
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
# Create database adapter (simplified import)
db_instance = db(
@@ -38,10 +37,8 @@ db_instance = db(
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. Keep responses brief.",
memory={
"db": db_instance,
"session_id": "demo-session-001", # Same session_id = resume conversation
},
db=db_instance,
session_id="demo-session-001", # Same session_id = resume conversation
output="silent"
)
+3 -6
View File
@@ -11,8 +11,7 @@ Expected output:
- Data persisted to local SQLite file
"""
from praisonaiagents import Agent
from praisonaiagents.db import db
from praisonaiagents import Agent, db
import tempfile
import os
@@ -26,10 +25,8 @@ print(f"Database: {db_path}")
agent = Agent(
name="LocalBot",
instructions="You are a helpful assistant.",
memory={
"db": my_db,
"session_id": "local-session",
},
db=my_db,
session_id="local-session"
)
# Chat
@@ -60,10 +60,9 @@ agents_system = AgentTeam(
process="sequential", output="verbose"
)
if __name__ == "__main__":
print("Starting hierarchical agent generation...")
result = agents_system.start()
print("Starting hierarchical agent generation...")
result = agents_system.start()
print(f"\nHierarchical Result: {str(result)[:200]}...")
print("\n✅ AutoAgents hierarchical generation complete!")
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
print(f"\nHierarchical Result: {result[:200]}...")
print("\n✅ AutoAgents hierarchical generation complete!")
print("Demonstrated coordinated hierarchical workflow between manager and specialist agents.")
+12 -16
View File
@@ -6,18 +6,10 @@ It includes self-reflection for improving code quality and multiple specialized
"""
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.tools import CodeInterpreter
def run_python_code(code: str) -> str:
"""Execute Python code safely and return stdout or errors."""
import io
import contextlib
try:
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
exec(code, {"__builtins__": __builtins__})
return buf.getvalue() or "Executed successfully (no output)"
except Exception as e:
return f"Execution error: {e}"
# Initialize code execution tool
code_interpreter = CodeInterpreter()
# Custom code analysis tool
def analyze_code_complexity(code: str) -> str:
@@ -40,7 +32,9 @@ code_writer = Agent(
goal="Write clean, efficient, and well-documented Python code",
backstory="You are a senior Python developer with 10+ years of experience in writing production-quality code.",
instructions="Write Python code that follows PEP 8 standards, includes proper error handling, and has clear documentation.",
output="silent",
reflection=True,
)
code_reviewer = Agent(
@@ -58,7 +52,7 @@ code_executor = Agent(
goal="Safely execute and test Python code",
backstory="You are responsible for running code and reporting results or errors.",
instructions="Execute the provided code safely and report the output or any errors encountered.",
tools=[run_python_code]
tools=[code_interpreter]
)
# Example tasks
@@ -93,7 +87,7 @@ def demonstrate_code_agent():
workflow = AgentTeam(
agents=[code_writer, code_reviewer, code_executor],
tasks=[write_task, review_task, execute_task],
process="sequential", output="silent"
process="sequential", output="verbose"
)
# Run the workflow
@@ -112,8 +106,10 @@ general_code_agent = Agent(
3. Explain code concepts
4. Refactor code for better performance
5. Add tests and documentation""",
tools=[run_python_code, analyze_code_complexity],
output="silent",
tools=[code_interpreter, analyze_code_complexity],
reflection=True,
)
if __name__ == "__main__":
+16 -19
View File
@@ -41,25 +41,21 @@ def calculate_statistics(numbers: list) -> str:
"""Calculate basic statistics for a list of numbers."""
if not numbers:
return "No numbers provided"
try:
nums = [float(x) for x in numbers]
except (TypeError, ValueError):
return "Error: all values must be numbers"
n = len(nums)
mean = sum(nums) / n
sorted_nums = sorted(nums)
n = len(numbers)
mean = sum(numbers) / n
sorted_nums = sorted(numbers)
median = sorted_nums[n//2] if n % 2 else (sorted_nums[n//2-1] + sorted_nums[n//2]) / 2
variance = sum((x - mean)**2 for x in nums) / n
variance = sum((x - mean)**2 for x in numbers) / n
std_dev = math.sqrt(variance)
return f"""Statistics:
- Count: {n}
- Mean: {mean:.4f}
- Median: {median:.4f}
- Standard Deviation: {std_dev:.4f}
- Min: {min(nums)}
- Max: {max(nums)}"""
- Min: {min(numbers)}
- Max: {max(numbers)}"""
# Create specialized math agents
calculation_agent = Agent(
@@ -70,7 +66,8 @@ calculation_agent = Agent(
instructions="Perform the requested calculations step by step, showing your work clearly.",
tools=[basic_calculator, solve_quadratic, calculate_statistics],
reflection=True,
output="silent",
)
problem_solver_agent = Agent(
@@ -85,7 +82,8 @@ problem_solver_agent = Agent(
4. Show each step of the solution
5. Verify the answer makes sense""",
reflection=True,
output="silent",
)
math_tutor_agent = Agent(
@@ -93,8 +91,7 @@ math_tutor_agent = Agent(
role="Mathematics tutor and explainer",
goal="Explain mathematical concepts clearly and help users understand math",
backstory="You are a patient math tutor who can explain concepts at any level from elementary to advanced.",
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding.",
output="silent",
instructions="Explain mathematical concepts using simple language, provide examples, and check for understanding."
)
# Example workflow for solving complex problems
@@ -129,8 +126,7 @@ def solve_math_problem_workflow():
workflow = AgentTeam(
agents=[problem_solver_agent, calculation_agent, math_tutor_agent],
tasks=[understand_task, solve_task, verify_task],
process="sequential",
output="silent",
process="sequential", output="verbose"
)
return workflow.start()
@@ -152,7 +148,8 @@ math_assistant = Agent(
Always show your work step by step.""",
tools=[basic_calculator, solve_quadratic, calculate_statistics],
reflection=True,
output="silent",
)
if __name__ == "__main__":
+2 -3
View File
@@ -1,5 +1,4 @@
from praisonaiagents import Agent
if __name__ == "__main__":
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
agent.launch(port=8080, protocol="mcp", path="/")
agent = Agent(name="TweetAgent", instructions="Create a Tweet based on the topic provided")
agent.launch(port=8080, protocol="mcp")
-238
View File
@@ -1,238 +0,0 @@
#!/usr/bin/env python3
"""
Example: Bot In-Flight Run Control
Demonstrates how to use SessionRunControl to provide better UX for bots
during long-running agent operations. Shows busy feedback, pending message
handling, and /stop command support.
This example shows the "before" and "after" behavior described in issue #1914.
"""
import asyncio
import time
from typing import Optional
# Mock agent for demonstration
class MockAgent:
"""Simple mock agent that simulates long-running tasks."""
def __init__(self, name="assistant"):
self.name = name
self.chat_history = []
self._interrupt_controller = None
def chat(self, prompt: str) -> str:
"""Simulate a long-running chat response."""
print(f"[Agent] Starting to process: '{prompt[:50]}...'")
# Simulate long processing with interrupt checking
for i in range(30): # 3 second task (30 x 0.1s)
time.sleep(0.1)
# Check for interruption
if (self._interrupt_controller and
hasattr(self._interrupt_controller, 'is_set') and
self._interrupt_controller.is_set()):
reason = getattr(self._interrupt_controller, 'reason', 'unknown')
print(f"[Agent] ⚠️ Interrupted: {reason}")
raise InterruptedError(f"Task cancelled: {reason}")
response = f"Completed analysis of: {prompt}"
print(f"[Agent] ✅ Finished: {response}")
return response
async def demo_without_run_control():
"""Demonstrate the old behavior (silent blocking)."""
print("\n" + "="*60)
print("🔴 BEFORE: Without Run Control (Silent Blocking)")
print("="*60)
from praisonai.bots._session import BotSessionManager
# Standard session manager (no run control)
session_mgr = BotSessionManager()
agent = MockAgent("research-agent")
user_id = "user123"
print("\n1. User sends long task:")
print(" User: 'research quantum computing trends'")
# Start first task (this will run for 3 seconds)
task1 = asyncio.create_task(
session_mgr.chat(agent, user_id, "research quantum computing trends")
)
# Simulate user sending follow-up messages during execution
await asyncio.sleep(0.5)
print("\n2. User sends follow-up (this will block silently):")
print(" User: 'actually focus on business impact'")
task2 = asyncio.create_task(
session_mgr.chat(agent, user_id, "actually focus on business impact")
)
await asyncio.sleep(0.5)
print("\n3. User tries to send /stop (this will also block):")
print(" User: '/stop'")
task3 = asyncio.create_task(
session_mgr.chat(agent, user_id, "/stop")
)
print("\n⏳ Waiting for all messages to complete...")
results = await asyncio.gather(task1, task2, task3)
print("\n📊 Results (all processed sequentially with no feedback):")
for i, result in enumerate(results, 1):
print(f" {i}. {result}")
print("\n❌ Problems with this approach:")
print(" - No immediate feedback on follow-up messages")
print(" - No way to cancel long-running tasks")
print(" - User doesn't know if bot is working or broken")
async def demo_with_run_control():
"""Demonstrate the new behavior with run control."""
print("\n" + "="*60)
print("🟢 AFTER: With Run Control (Better UX)")
print("="*60)
from praisonai.bots._run_control import SessionRunControl
from praisonai.bots._session import BotSessionManager
from praisonai.bots._commands import handle_stop_command
# Session manager with run control
run_control = SessionRunControl(
busy_mode="queue",
busy_ack_template="⏳ {action} — will process after current task finishes"
)
session_mgr = BotSessionManager(run_control=run_control)
agent = MockAgent("research-agent")
user_id = "user123"
print("\n1. User sends long task:")
print(" User: 'research quantum computing trends'")
# Start first task
task1 = asyncio.create_task(
session_mgr.chat_with_run_control(agent, user_id, "research quantum computing trends")
)
# Immediate follow-up messages get acknowledgments
await asyncio.sleep(0.5)
print("\n2. User sends follow-up (gets immediate feedback):")
print(" User: 'actually focus on business impact'")
result2 = await session_mgr.chat_with_run_control(
agent, user_id, "actually focus on business impact"
)
print(f" Bot: {result2['response']}")
print(f" Metadata: {result2['metadata']}")
await asyncio.sleep(0.5)
print("\n3. User decides to stop current task:")
print(" User: '/stop'")
stop_response = await handle_stop_command(user_id, run_control)
print(f" Bot: {stop_response}")
# Wait for first task to complete (it should be cancelled)
try:
result1 = await task1
print(f"\n📊 Task 1 result: {result1['response']}")
print(f" Metadata: {result1['metadata']}")
except asyncio.CancelledError:
print("\n📊 Task 1 was cancelled as expected")
print("\n4. User sends fresh request after stopping:")
print(" User: 'what's the weather like?'")
result4 = await session_mgr.chat_with_run_control(
agent, user_id, "what's the weather like?"
)
print(f" Bot: {result4['response']}")
print("\n✅ Benefits of this approach:")
print(" - Immediate feedback on all messages")
print(" - Can cancel long-running tasks with /stop")
print(" - Pending messages are queued and processed in order")
print(" - User always knows what's happening")
async def demo_interrupt_mode():
"""Demonstrate interrupt mode."""
print("\n" + "="*60)
print("⚡ BONUS: Interrupt Mode (Cancel and Restart)")
print("="*60)
from praisonai.bots._run_control import SessionRunControl
from praisonai.bots._session import BotSessionManager
# Use interrupt mode instead of queue mode
run_control = SessionRunControl(busy_mode="interrupt")
session_mgr = BotSessionManager(run_control=run_control)
agent = MockAgent("research-agent")
user_id = "user123"
print("\n1. User starts long task:")
print(" User: 'research renewable energy for 30 pages'")
task1 = asyncio.create_task(
session_mgr.chat_with_run_control(agent, user_id, "research renewable energy for 30 pages")
)
await asyncio.sleep(0.5)
print("\n2. User changes mind (interrupts and restarts):")
print(" User: 'actually just summarize solar panel efficiency'")
result2 = await session_mgr.chat_with_run_control(
agent, user_id, "actually just summarize solar panel efficiency"
)
print(f" Bot: {result2['response']}")
# Wait for original task (should be interrupted)
try:
result1 = await task1
print(f"\n📊 Original task result: {result1['response']}")
if result1['metadata'].get('interrupted'):
print(" ✅ Original task was properly interrupted")
except:
print("\n📊 Original task was cancelled")
print("\n✅ Interrupt mode is great for:")
print(" - When users frequently change direction")
print(" - Interactive sessions where latest intent matters most")
print(" - Real-time collaboration scenarios")
async def main():
"""Run all demonstrations."""
print("🚀 Bot In-Flight Run Control Demo")
print("Solving the silent lock problem described in issue #1914")
# Show the problem
await demo_without_run_control()
# Show the solution
await demo_with_run_control()
# Show advanced feature
await demo_interrupt_mode()
print("\n" + "="*60)
print("🎯 Summary")
print("="*60)
print("SessionRunControl solves the silent blocking problem by providing:")
print("1. Immediate feedback for mid-run messages")
print("2. /stop command support via InterruptController")
print("3. Configurable policies (queue/interrupt/steer)")
print("4. Run generation tracking to prevent race conditions")
print("5. Pending message merging and ordering")
print("\nThis makes bot interactions feel responsive and predictable!")
if __name__ == "__main__":
asyncio.run(main())
+2 -11
View File
@@ -90,18 +90,9 @@ def main():
if result:
print("\n🔍 Analysis Results:")
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
for task_id, task_result in result["task_results"].items():
print(f"\nTask {task_id}:")
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
print(task_result.raw)
else:
print("❌ Failed to capture and analyze camera feed")
+2 -11
View File
@@ -148,19 +148,10 @@ def main():
"scene_analysis": "🌍 SCENE ANALYSIS"
}
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
for task_id, task_result in result["task_results"].items():
print(f"\n{agent_names.get(task_id, f'Task {task_id}')}:")
print("-" * 40)
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
print(task_result.raw)
print("\n" + "="*60)
print("✅ Multi-agent analysis complete!")
@@ -34,5 +34,5 @@ print(f"\nChanged to: {handler.get_mode()}")
print("File edits now auto-approved, shell commands still require approval")
# Get statistics
stats = manager.get_stats()
stats = handler.get_stats()
print(f"\nStats: {stats}")
+1 -1
View File
@@ -31,4 +31,4 @@ print(f"Total cost: ${summary['total_cost']:.4f}")
print(f"Avg cost/request: ${summary['avg_cost_per_request']:.4f}")
# Display formatted (uses Rich if available)
handler.print_summary()
handler.display_summary()
@@ -41,9 +41,9 @@ def my_custom_handler(args, context):
custom_cmd = SlashCommand(
name="mycommand",
description="My custom command",
action=my_custom_handler,
kind=CommandKind.CUSTOM,
alt_names=["mc"]
handler=my_custom_handler,
kind=CommandKind.ACTION,
aliases=["mc"]
)
handler.register(custom_cmd)
@@ -1,9 +1,8 @@
from pathlib import Path
from praisonaiagents import Agent, Task, AgentTeam
import logging
import tempfile
import os
import sys
import logging
from praisonaiagents import Agent, Task, AgentTeam
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -20,9 +19,11 @@ config = {
}
}
_sample_path = Path(__file__).with_name("sample_knowledge.txt")
if not _sample_path.exists():
_sample_path.write_text("Mervin Praison is the creator of PraisonAI.\n", encoding="utf-8")
# Create sample file in system temp directory
_sample_path = os.path.join(tempfile.gettempdir(), "sample_knowledge.txt")
if not os.path.exists(_sample_path):
with open(_sample_path, "w", encoding="utf-8") as f:
f.write("Mervin Praison is the creator of PraisonAI.\n")
# Create an agent with knowledge capabilities
knowledge_agent = Agent(
@@ -30,7 +31,7 @@ knowledge_agent = Agent(
role="Information Specialist",
goal="Store and retrieve knowledge efficiently",
backstory="Expert in managing and utilizing stored knowledge",
knowledge={**config, "sources": [str(_sample_path)]},
knowledge={**config, "sources": [_sample_path]},
)
# Define a task for the agent
@@ -19,5 +19,4 @@ agents = AgentTeam(
execution={"max_iter": 30},
)
if __name__ == "__main__":
agents.start()
agents.start()
@@ -5,8 +5,7 @@ This example demonstrates self-reflection capabilities using PraisonAI's
built-in reflection features for iterative improvement and quality optimization.
"""
from praisonaiagents import Agent, Task
from praisonaiagents.agents import AgentTeam
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.tools import internet_search
print("=== Self-Reflection Optimization Patterns Example ===\n")
@@ -34,16 +33,9 @@ reflection_task = Task(
)
# Run with self-reflection optimization
if __name__ == "__main__":
print("Starting self-reflection optimization demonstration...")
agents = AgentTeam(
agents=[reflection_agent],
tasks=[reflection_task],
process="sequential",
output="verbose",
)
result = agents.start()
print("Starting self-reflection optimization demonstration...")
result = reflection_agent.execute_task(reflection_task)
print(f"\nSelf-Reflection Result: {str(result)[:200]}...")
print("\n✅ Self-reflection optimization complete!")
print("Agent demonstrated iterative improvement through built-in self-reflection capabilities.")
print(f"\nSelf-Reflection Result: {result[:200]}...")
print("\n✅ Self-reflection optimization complete!")
print("Agent demonstrated iterative improvement through built-in self-reflection capabilities.")
+1 -2
View File
@@ -136,5 +136,4 @@ agents = AgentTeam(
output="minimal",
)
if __name__ == "__main__":
agents.start()
agents.start()
-201
View File
@@ -1,201 +0,0 @@
"""
Prototype implementation of Gateway Server with backpressure and flow control.
This demonstrates how the bounded queues and slow consumer detection would be
implemented in the wrapper layer (praisonai package) using the configuration
from the core SDK (praisonaiagents).
"""
from __future__ import annotations
import asyncio
import logging
from dataclasses import dataclass, field
from typing import Any, Dict, Optional
# For the prototype, we'll define minimal config inline
# from praisonaiagents.gateway import GatewayConfig
@dataclass
class SessionConfig:
max_inbox: int = 256
@dataclass
class GatewayConfig:
session_config: SessionConfig = field(default_factory=SessionConfig)
max_buffered_bytes: int = 1024 * 1024 # 1MB default
logger = logging.getLogger(__name__)
@dataclass
class GatewaySession:
"""Gateway session with bounded inbox for backpressure."""
_session_id: str
_agent_id: str
_client_id: Optional[str] = None
_inbox: asyncio.Queue = field(default_factory=asyncio.Queue)
_max_inbox: int = 256
_is_executing: bool = False
def setup_inbox(self, max_inbox: int = 256) -> None:
"""Set up the inbox with proper bounds."""
self._max_inbox = max_inbox
if max_inbox > 0:
self._inbox = asyncio.Queue(maxsize=max_inbox)
else:
self._inbox = asyncio.Queue() # Unbounded for backward compat if 0
async def queue_message(self, message: str) -> bool:
"""Queue a user message with backpressure handling.
Returns:
True if message was queued, False if inbox is full.
"""
if self._max_inbox > 0:
# Bounded queue - check if full
try:
self._inbox.put_nowait(message)
return True
except asyncio.QueueFull:
return False
else:
# Unbounded queue for backward compatibility
await self._inbox.put(message)
return True
class GatewayServer:
"""Gateway server with flow control."""
def __init__(self, config: GatewayConfig):
self.config = config
self._sessions: Dict[str, GatewaySession] = {}
self._clients: Dict[str, Any] = {} # client_id -> WebSocket
def create_session(self, session_id: str, agent_id: str, client_id: str) -> GatewaySession:
"""Create a new session with bounded inbox."""
session = GatewaySession(
_session_id=session_id,
_agent_id=agent_id,
_client_id=client_id,
)
# Set up inbox with configured bounds
session.setup_inbox(self.config.session_config.max_inbox)
self._sessions[session_id] = session
return session
async def handle_message(self, session: GatewaySession, message: str, client_id: str) -> str:
"""Handle incoming message with inbox overflow protection."""
# Try to queue the message
queued = await session.queue_message(message)
if not queued:
# Inbox is full, send error response
await self._send_to_client(
client_id,
{
"type": "error",
"code": "inbox_full",
"message": "Message queue is full. Please wait for current messages to be processed."
}
)
return "Inbox full - message rejected."
# Start processing if not already running
if not session._is_executing:
session._is_executing = True
asyncio.create_task(self._drain_session_queue(session, client_id))
return "Processing started."
return "Message queued."
async def _drain_session_queue(self, session: GatewaySession, client_id: str) -> None:
"""Process all messages from the session's inbox queue."""
try:
while not session._inbox.empty():
msg = await session._inbox.get()
# Process message here (simplified for prototype)
logger.info(f"Processing message for session {session._session_id}: {msg}")
# Simulate processing delay
await asyncio.sleep(0.1)
# Send response to client (simplified)
await self._send_to_client(
client_id,
{
"type": "response",
"message": f"Processed: {msg}"
}
)
except Exception as e:
logger.error(f"Error processing queue for session {session._session_id}: {e}")
finally:
session._is_executing = False
async def _send_to_client(self, client_id: str, data: Dict[str, Any]) -> None:
"""Send data to client with slow consumer detection."""
ws = self._clients.get(client_id)
if ws:
try:
# Check for slow consumer (buffered bytes)
if self.config.max_buffered_bytes > 0:
# Try to get transport buffer size
transport = getattr(ws, 'transport', None) or getattr(ws, '_transport', None)
if transport and hasattr(transport, 'get_write_buffer_size'):
buffered_bytes = transport.get_write_buffer_size()
if buffered_bytes > self.config.max_buffered_bytes:
# Check if this is a droppable event type
event_type = data.get("type", "")
droppable_types = {"presence", "typing", "status"}
if event_type in droppable_types:
# Drop the message for slow consumers
logger.warning(f"Dropping {event_type} event for slow consumer {client_id} (buffered: {buffered_bytes} bytes)")
return
else:
# Critical event - close the connection
logger.warning(f"Closing slow consumer {client_id} (buffered: {buffered_bytes} bytes > {self.config.max_buffered_bytes})")
await ws.close(code=1013, reason="slow consumer")
return
# Send the message
await ws.send_json(data)
except Exception as e:
logger.error(f"Error sending to client {client_id}: {e}", exc_info=True)
# Example usage demonstrating the flow control
async def demo():
"""Demonstrate the flow control implementation."""
# Create config with bounded inbox and slow consumer detection
config = GatewayConfig()
config.session_config.max_inbox = 10 # Small inbox for demo
config.max_buffered_bytes = 1024 # 1KB for demo
# Create server and session
server = GatewayServer(config)
session = server.create_session("sess1", "agent1", "client1")
# Simulate filling up the inbox
print(f"Inbox max size: {session._inbox.maxsize}")
# Fill the inbox
for i in range(10):
success = await session.queue_message(f"Message {i}")
print(f"Queued message {i}: {success}")
# Try to add one more - should fail
success = await session.queue_message("Overflow message")
print(f"Overflow message queued: {success}")
# Process messages
while not session._inbox.empty():
msg = await session._inbox.get()
print(f"Processing: {msg}")
if __name__ == "__main__":
asyncio.run(demo())
+6 -16
View File
@@ -49,20 +49,10 @@ agents = AgentTeam(
process="sequential"
)
if __name__ == "__main__":
# Run all tasks
result = agents.start()
# Run all tasks
result = agents.start()
# Print results
task_results = result.get("task_results") if isinstance(result, dict) else result
if isinstance(task_results, dict):
iterable = task_results.items()
elif isinstance(task_results, list):
iterable = enumerate(task_results)
else:
iterable = [("result", task_results)]
for task_id, task_result in iterable:
print(f"\nTask {task_id} Result:")
raw_value = getattr(task_result, "raw", None)
print(raw_value if raw_value is not None else str(task_result))
# Print results
for task_id, task_result in result["task_results"].items():
print(f"\nTask {task_id} Result:")
print(task_result.raw)
@@ -14,33 +14,30 @@ agent = Agent(name="env-mgmt", backend=managed)
# First call creates agent + environment + session
agent.start("Say hello briefly")
if hasattr(managed, "_get_client"):
# Access the underlying Anthropic client for environment management
client = managed._get_client()
env_id = managed.environment_id
print(f"Environment ID: {env_id}")
# Access the underlying Anthropic client for environment management
client = managed._get_client()
env_id = managed.environment_id
print(f"Environment ID: {env_id}")
# List all environments
environments = client.beta.environments.list()
print(f"\nTotal environments: {len(environments.data)}")
for env in environments.data[:5]:
print(f" {env.id} | {env.name} | {getattr(env, 'status', 'active')}")
# List all environments
environments = client.beta.environments.list()
print(f"\nTotal environments: {len(environments.data)}")
for env in environments.data[:5]:
print(f" {env.id} | {env.name} | {getattr(env, 'status', 'active')}")
# Retrieve a specific environment
env = client.beta.environments.retrieve(env_id)
print(f"\nRetrieved: {env.id} | {env.name}")
# Retrieve a specific environment
env = client.beta.environments.retrieve(env_id)
print(f"\nRetrieved: {env.id} | {env.name}")
# Archive (read-only, existing sessions continue)
archived = client.beta.environments.archive(env_id)
print(f"Archived: {archived.id} | status: {getattr(archived, 'status', 'archived')}")
# Archive (read-only, existing sessions continue)
archived = client.beta.environments.archive(env_id)
print(f"Archived: {archived.id} | status: {getattr(archived, 'status', 'archived')}")
# Delete (only if no sessions reference it — may fail if sessions exist)
try:
client.beta.environments.delete(env_id)
print(f"Deleted: {env_id}")
except Exception as e:
print(f"Delete skipped (expected if sessions exist): {e}")
else:
print("Environment management demo skipped: backend does not expose _get_client().")
# Delete (only if no sessions reference it — may fail if sessions exist)
try:
client.beta.environments.delete(env_id)
print(f"Deleted: {env_id}")
except Exception as e:
print(f"Delete skipped (expected if sessions exist): {e}")
print("\nEnvironment management complete.")
+7 -6
View File
@@ -15,6 +15,13 @@ Protocol: MCP 2025-11-25
from praisonaiagents import Agent, MCP
# Agent with resumability support (automatic)
agent = Agent(
name="Resumable Assistant",
instructions="You are a helpful assistant with resumable connections.",
tools=MCP("https://api.example.com/mcp")
)
# Resumability is automatic - the MCP client:
# 1. Tracks event IDs from SSE streams (id: field)
# 2. Stores last event ID per stream
@@ -22,12 +29,6 @@ from praisonaiagents import Agent, MCP
# 4. Respects retry delay from server (retry: field)
if __name__ == "__main__":
agent = Agent(
name="Resumable Assistant",
instructions="You are a helpful assistant with resumable connections.",
tools=MCP("https://api.example.com/mcp"),
)
print("MCP Resumability Example")
print("=" * 40)
print()
@@ -15,6 +15,13 @@ Protocol: MCP 2025-11-25
from praisonaiagents import Agent, MCP
# Basic agent with automatic session management
agent = Agent(
name="Session Assistant",
instructions="You are a helpful assistant with session support.",
tools=MCP("https://api.example.com/mcp")
)
# Session management is automatic - the MCP client:
# 1. Receives Mcp-Session-Id from server during initialization
# 2. Includes session ID in all subsequent requests
@@ -22,12 +29,6 @@ from praisonaiagents import Agent, MCP
# 4. Can terminate session explicitly when done
if __name__ == "__main__":
agent = Agent(
name="Session Assistant",
instructions="You are a helpful assistant with session support.",
tools=MCP("https://api.example.com/mcp"),
)
print("MCP Session Management Example")
print("=" * 40)
print()
+27 -24
View File
@@ -16,31 +16,34 @@ Protocol: MCP 2025-11-25
from praisonaiagents import Agent, MCP
# Basic Streamable HTTP connection
agent = Agent(
name="HTTP Stream Assistant",
instructions="You are a helpful assistant connected via Streamable HTTP.",
tools=MCP("https://api.example.com/mcp")
)
# With authentication headers
agent_auth = Agent(
name="Authenticated Assistant",
instructions="You are a helpful assistant with authenticated connection.",
tools=MCP(
"https://api.example.com/mcp",
headers={"Authorization": "Bearer your-token"}
)
)
# With timeout and debug
agent_debug = Agent(
name="Debug Assistant",
tools=MCP(
"https://api.example.com/mcp",
timeout=120,
debug=True
)
)
if __name__ == "__main__":
agent = Agent(
name="HTTP Stream Assistant",
instructions="You are a helpful assistant connected via Streamable HTTP.",
tools=MCP("https://api.example.com/mcp"),
)
agent_auth = Agent(
name="Authenticated Assistant",
instructions="You are a helpful assistant with authenticated connection.",
tools=MCP(
"https://api.example.com/mcp",
headers={"Authorization": "Bearer your-token"},
),
)
agent_debug = Agent(
name="Debug Assistant",
tools=MCP(
"https://api.example.com/mcp",
timeout=120,
debug=True,
),
)
print("Streamable HTTP MCP Transport Example")
print("=" * 40)
print("This example requires a Streamable HTTP MCP server running.")
+29 -26
View File
@@ -16,33 +16,36 @@ Protocol: SEP-1288 (WebSocket Transport)
from praisonaiagents import Agent, MCP
# Basic WebSocket connection
agent = Agent(
name="WebSocket Assistant",
instructions="You are a helpful assistant connected via WebSocket.",
tools=MCP("ws://localhost:8080/mcp")
)
# Secure WebSocket with authentication
agent_secure = Agent(
name="Secure WebSocket Assistant",
instructions="You are a helpful assistant with secure WebSocket connection.",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="Bearer your-secret-token",
timeout=60
)
)
# WebSocket with custom options
agent_custom = Agent(
name="Custom WebSocket Assistant",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="your-token",
timeout=120,
debug=True
)
)
if __name__ == "__main__":
agent = Agent(
name="WebSocket Assistant",
instructions="You are a helpful assistant connected via WebSocket.",
tools=MCP("ws://localhost:8080/mcp"),
)
agent_secure = Agent(
name="Secure WebSocket Assistant",
instructions="You are a helpful assistant with secure WebSocket connection.",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="Bearer your-secret-token",
timeout=60,
),
)
agent_custom = Agent(
name="Custom WebSocket Assistant",
tools=MCP(
"wss://api.example.com/mcp",
auth_token="your-token",
timeout=120,
debug=True,
),
)
# Note: Requires a running WebSocket MCP server
# Example server: https://github.com/modelcontextprotocol/servers
-173
View File
@@ -1,173 +0,0 @@
#!/usr/bin/env python3
"""
Message Steering Example
Demonstrates real-time message steering for agents during execution.
Shows how to send guidance messages to agents while they're running tasks.
Usage:
# Python API
python message_steering_example.py
# CLI with steering enabled
praisonai --message-steering "Write a detailed report about AI trends"
# YAML (add message_steering: true to agent config)
"""
import asyncio
import threading
import time
from praisonaiagents import Agent
def basic_message_steering():
"""Basic message steering example - enable steering and send guidance."""
print("🎯 Basic Message Steering Example")
print("=" * 50)
# Create agent with message steering enabled
agent = Agent(
name="researcher",
instructions="You are a helpful research assistant",
message_steering=True, # Enable real-time message steering
llm="gpt-4o-mini"
)
print("✅ Agent created with message steering enabled")
print(f"📊 Steering status: {agent.get_steering_status()}")
# Send some guidance
msg_id_1 = agent.steer("Focus on recent developments from 2024")
msg_id_2 = agent.steer("Keep response under 200 words", priority=10)
print(f"📨 Sent steering messages: {msg_id_1}, {msg_id_2}")
print(f"📊 Steering status: {agent.get_steering_status()}")
# Execute task - steering will be automatically injected
result = agent.start("Summarize the latest AI trends")
print("\n📝 Result:")
print(result)
def threaded_message_steering():
"""Advanced example - send steering messages while agent is running."""
print("\n🚀 Threaded Message Steering Example")
print("=" * 50)
agent = Agent(
name="writer",
instructions="You are a content writer",
message_steering=True,
llm="gpt-4o-mini"
)
result_holder = [None]
def run_agent():
"""Run agent in background thread."""
result = agent.start("Write a comprehensive article about quantum computing")
result_holder[0] = result
# Start agent in background
thread = threading.Thread(target=run_agent)
thread.start()
# Send steering messages while it's running
time.sleep(0.5) # Let execution start
agent.steer("Make it accessible to beginners")
time.sleep(1.0)
agent.steer("Include practical applications", priority=15)
time.sleep(1.5)
agent.steer("Keep it under 300 words total", priority=20)
# Wait for completion
thread.join(timeout=60)
if result_holder[0]:
print("\n📝 Final Result:")
print(result_holder[0])
else:
print("⚠️ Agent execution timed out")
async def async_message_steering():
"""Async example showing message steering with async execution."""
print("\n⚡ Async Message Steering Example")
print("=" * 50)
agent = Agent(
name="analyst",
instructions="You are a data analyst",
message_steering=True,
llm="gpt-4o-mini"
)
# Start async task
async def run_analysis():
return await agent.astart("Analyze the impact of AI on job markets")
# Create task
task = asyncio.create_task(run_analysis())
# Send steering while running
await asyncio.sleep(0.3)
agent.steer("Focus on positive opportunities")
await asyncio.sleep(0.7)
agent.steer("Include specific examples", priority=12)
# Wait for result
result = await task
print("\n📝 Analysis Result:")
print(result)
def cli_yaml_examples():
"""Show CLI and YAML usage examples."""
print("\n💻 CLI & YAML Examples")
print("=" * 50)
print("🔧 CLI Usage:")
print(" praisonai --message-steering 'Your task here'")
print(" # Then in another terminal:")
print(" # agent.steer('Additional guidance')")
print("\n📄 YAML Configuration:")
yaml_example = """
# agents.yaml
framework: praisonai
roles:
researcher:
role: Research Assistant
goal: Conduct thorough research
backstory: Expert researcher with steering support
message_steering: true # Enable steering for this agent
tools:
- internet_search
- write_file
tasks:
research_task:
description: Research AI trends and write report
expected_output: Comprehensive research report
"""
print(yaml_example.strip())
if __name__ == "__main__":
# Run examples
basic_message_steering()
threaded_message_steering()
# Run async example
asyncio.run(async_message_steering())
# Show CLI/YAML usage
cli_yaml_examples()
print("\n✨ Message steering examples completed!")
print("📚 See docs for more advanced patterns and priority levels")
+2 -2
View File
@@ -12,6 +12,7 @@ Environment:
"""
import os
import tempfile
from praisonaiagents import Agent, AutoRagAgent
# Ensure API key is set
@@ -20,8 +21,7 @@ if not os.environ.get("OPENAI_API_KEY"):
exit(1)
# Create a sample document for testing
from pathlib import Path
sample_doc = str(Path(__file__).with_name("sample_doc.txt"))
sample_doc = os.path.join(tempfile.gettempdir(), "sample_doc.txt")
with open(sample_doc, "w", encoding="utf-8") as f:
f.write("""
PraisonAI Framework Documentation
@@ -4,7 +4,7 @@ Example 3: Save Agent Output Using Workflow output_file
Workflow step output is automatically saved with variable substitution.
"""
from praisonaiagents import Agent, AgentFlow, Task
from praisonaiagents import AgentFlow
# Define workflow with output_file
workflow_config = {
@@ -33,30 +33,9 @@ workflow_config = {
]
}
# Build agent and workflow from config dict
writer_cfg = workflow_config["agents"]["writer"]
writer = Agent(
name="writer",
role=writer_cfg["role"],
goal=writer_cfg["goal"],
llm=writer_cfg["llm"],
)
step_cfg = workflow_config["steps"][0]
workflow = AgentFlow(
name=workflow_config["metadata"]["name"],
variables=workflow_config["variables"],
steps=[
Task(
name="write_tutorial",
description=step_cfg["action"],
expected_output=step_cfg["expected_output"],
output_file=step_cfg["output_file"],
agent=writer,
)
],
)
result = workflow.start()
# Run workflow
workflow = AgentFlow(config=workflow_config)
result = workflow.run()
print("✅ Workflow completed!")
print("Output saved to: generated/tutorial.md")
@@ -69,10 +69,9 @@ agents_system = AgentTeam(
memory=True, output="verbose"
)
if __name__ == "__main__":
print("Starting state management demonstration...")
result = agents_system.start()
print("Starting state management demonstration...")
result = agents_system.start()
print(f"\nState Management Result: {str(result)[:200]}...")
print("\n✅ State management patterns complete!")
print("Agent demonstrated persistent state tracking across multiple tasks.")
print(f"\nState Management Result: {result[:200]}...")
print("\n✅ State management patterns complete!")
print("Agent demonstrated persistent state tracking across multiple tasks.")
@@ -179,7 +179,7 @@ def main():
context = memory.build_context_for_task(
task_descr="Explain AI alignment challenges",
memory={"user_id": "quality_demo_user"},
user_id="quality_demo_user",
max_items=3
)
@@ -1,55 +1,284 @@
"""
Production Telemetry Example
Demonstrates compatible telemetry setup with `enable_telemetry()` and
`TelemetryCollector` patterns.
This example demonstrates comprehensive telemetry and monitoring setup for
production AI agent systems, including metrics collection, performance tracking,
and observability.
Features demonstrated:
- OpenTelemetry integration for distributed tracing
- Custom metrics collection and monitoring
- Performance tracking and analytics
- Error tracking and alerting
- Usage analytics and reporting
"""
from contextlib import nullcontext
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.tools import duckduckgo
from praisonaiagents.telemetry import enable_telemetry, TelemetryCollector
from praisonaiagents.telemetry import enable_telemetry
import time
import random
from datetime import datetime
# Initialize telemetry for production monitoring
print("="*80)
print("PRODUCTION TELEMETRY SETUP")
print("="*80)
def main() -> None:
print("=" * 80)
print("PRODUCTION TELEMETRY SETUP")
print("=" * 80)
# Configure telemetry (in production, you'd use actual telemetry backends)
telemetry = enable_telemetry(
backend="opentelemetry",
config={
"service_name": "praisonai_production_agents",
"service_version": "1.0.0",
"environment": "production",
"export_console": True, # For demo purposes
"export_file": True, # Export to file for analysis
"sample_rate": 1.0, # 100% sampling for demo
"attributes": {
"deployment": "cloud",
"region": "us-east-1",
"cluster": "main"
}
}
)
# New API: no args
enable_telemetry()
collector = TelemetryCollector()
# Create agents for production workflow with telemetry
customer_service_agent = Agent(
name="CustomerServiceAgent",
role="Customer Service Representative",
goal="Provide excellent customer service with fast response times",
backstory="You are a customer service representative focused on resolving customer issues quickly and effectively.",
tools=[duckduckgo],
instructions="Provide helpful, accurate customer service responses. Research solutions when needed."
)
customer_service_agent = Agent(
name="CustomerServiceAgent",
role="Customer Service Representative",
goal="Provide excellent customer service with fast response times",
backstory="Customer service expert focused on quick issue resolution.",
tools=[duckduckgo],
instructions="Provide accurate customer-service responses.",
)
technical_support_agent = Agent(
name="TechnicalSupportAgent",
role="Technical Support Specialist",
goal="Resolve technical issues with detailed solutions",
backstory="You are a technical support specialist who provides detailed technical solutions and troubleshooting.",
instructions="Provide detailed technical solutions with step-by-step instructions."
)
requests = [
"Customer asking about monthly bill charges",
"Customer experiencing connectivity issues",
quality_assurance_agent = Agent(
name="QualityAssuranceAgent",
role="Quality Assurance Specialist",
goal="Ensure all customer interactions meet quality standards",
backstory="You review and assess the quality of customer service interactions to ensure high standards.",
instructions="Review customer service interactions and provide quality scores with detailed feedback."
)
# Production workflow simulation with telemetry tracking
def simulate_customer_requests():
"""Simulate various customer requests for telemetry demonstration"""
customer_requests = [
{
"type": "billing_inquiry",
"complexity": "low",
"description": "Customer asking about their monthly bill charges",
"expected_duration": 30
},
{
"type": "technical_issue",
"complexity": "high",
"description": "Customer experiencing connectivity issues with the service",
"expected_duration": 120
},
{
"type": "account_setup",
"complexity": "medium",
"description": "New customer needs help setting up their account",
"expected_duration": 60
},
{
"type": "feature_question",
"complexity": "low",
"description": "Customer asking about product features and capabilities",
"expected_duration": 45
}
]
return customer_requests
for idx, description in enumerate(requests, start=1):
trace_ctx = getattr(collector, "trace", None)
trace_cm = trace_ctx(f"customer_request_{idx}") if callable(trace_ctx) else nullcontext()
with trace_cm:
task = Task(
name=f"customer_request_{idx}",
description=description,
expected_output="Professional customer service response with solution",
agent=customer_service_agent,
)
agents = AgentTeam(agents=[customer_service_agent], tasks=[task], output="minimal")
result = agents.start()
print(f"✅ Request {idx} completed, response length={len(str(result))}")
# Execute production workflow with comprehensive telemetry
print("Starting production workflow with telemetry tracking...")
print("\nTelemetry demonstration completed.")
customer_requests = simulate_customer_requests()
for i, request in enumerate(customer_requests):
print(f"\n{'='*60}")
print(f"PROCESSING CUSTOMER REQUEST {i+1}: {request['type'].upper()}")
print(f"Complexity: {request['complexity']} | Expected Duration: {request['expected_duration']}s")
print(f"{'='*60}")
# Start telemetry trace for this customer interaction
with telemetry.trace_operation(f"customer_request_{request['type']}") as trace:
# Add custom attributes to the trace
trace.set_attributes({
"request.type": request['type'],
"request.complexity": request['complexity'],
"request.expected_duration": request['expected_duration'],
"customer.id": f"customer_{random.randint(1000, 9999)}",
"agent.assigned": "CustomerServiceAgent"
})
start_time = time.time()
# Create customer service task with telemetry
customer_task = Task(
name=f"customer_request_{i+1}",
description=request['description'],
expected_output="Professional customer service response with solution",
agent=customer_service_agent
)
# Execute with telemetry tracking
try:
# Customer service response
with telemetry.trace_agent_execution("CustomerServiceAgent") as agent_trace:
agents = AgentTeam(
agents=[customer_service_agent],
tasks=[customer_task],
output="minimal" # Reduce verbosity for telemetry demo
)
cs_result = agents.start()
agent_trace.set_attributes({
"agent.response_length": len(str(cs_result)),
"agent.tools_used": len(customer_service_agent.tools) if customer_service_agent.tools else 0
})
# Technical support (if needed for high complexity)
if request['complexity'] == 'high':
with telemetry.trace_agent_execution("TechnicalSupportAgent") as tech_trace:
tech_task = Task(
name=f"technical_support_{i+1}",
description=f"Provide detailed technical support for: {request['description']}",
expected_output="Detailed technical solution with troubleshooting steps",
agent=technical_support_agent,
context=[customer_task]
)
tech_agents = AgentTeam(
agents=[technical_support_agent],
tasks=[tech_task], output="minimal"
)
tech_result = tech_agents.start()
tech_trace.set_attributes({
"agent.response_length": len(str(tech_result)),
"agent.escalation": True
})
# Quality assurance check
with telemetry.trace_agent_execution("QualityAssuranceAgent") as qa_trace:
qa_task = Task(
name=f"quality_check_{i+1}",
description=f"Review the quality of customer service provided for: {request['description']}",
expected_output="Quality assessment score and feedback",
agent=quality_assurance_agent
)
qa_agents = AgentTeam(
agents=[quality_assurance_agent],
tasks=[qa_task], output="minimal"
)
qa_result = qa_agents.start()
# Extract quality score (simulated)
quality_score = random.uniform(0.8, 1.0) # Simulated score
qa_trace.set_attributes({
"qa.score": quality_score,
"qa.passed": quality_score >= 0.85
})
# Calculate performance metrics
end_time = time.time()
duration = end_time - start_time
# Record custom metrics
telemetry.record_metric("request_duration", duration, {
"request_type": request['type'],
"complexity": request['complexity']
})
telemetry.record_metric("request_success", 1, {
"request_type": request['type']
})
# Set final trace attributes
trace.set_attributes({
"request.duration_seconds": duration,
"request.status": "completed",
"request.quality_score": quality_score,
"request.escalated": request['complexity'] == 'high'
})
print(f"✅ Request completed successfully in {duration:.2f}s (Quality: {quality_score:.2f})")
except Exception as e:
# Record error metrics
telemetry.record_metric("request_error", 1, {
"request_type": request['type'],
"error_type": type(e).__name__
})
trace.set_attributes({
"request.status": "error",
"request.error": str(e)
})
print(f"❌ Request failed: {str(e)}")
# Add some delay between requests
time.sleep(2)
if __name__ == "__main__":
main()
# Generate telemetry summary
print(f"\n{'='*80}")
print("TELEMETRY SUMMARY AND ANALYTICS")
print(f"{'='*80}")
# Simulated telemetry analytics (in production, this would come from your telemetry backend)
print("📊 Performance Metrics:")
print("- Total Requests Processed: 4")
print("- Average Response Time: 3.2s")
print("- Success Rate: 100%")
print("- Quality Score Average: 0.91")
print("- High Complexity Requests: 25%")
print("\n📈 Usage Analytics:")
print("- Most Common Request Type: technical_issue")
print("- Peak Performance Window: All requests within SLA")
print("- Agent Utilization: CustomerService (100%), TechnicalSupport (25%), QA (100%)")
print("\n🔍 Observability Features Demonstrated:")
print("- Distributed tracing across agent interactions")
print("- Custom metrics for business KPIs")
print("- Performance monitoring and SLA tracking")
print("- Error tracking and alerting")
print("- Quality assurance metrics")
print("- User journey tracking")
print("- Resource utilization monitoring")
print("\n⚙️ Production Monitoring Setup:")
print("- OpenTelemetry integration for standardized observability")
print("- Custom metrics for business-specific KPIs")
print("- Trace correlation across multi-agent workflows")
print("- Performance benchmarking against expected durations")
print("- Quality scoring and automated validation")
print("- Error categorization and alerting")
print(f"\n{'='*80}")
print("TELEMETRY DEMONSTRATION COMPLETED")
print(f"{'='*80}")
print("This example demonstrated comprehensive production telemetry including:")
print("- Request tracing and performance monitoring")
print("- Custom business metrics collection")
print("- Quality assurance tracking")
print("- Error handling and alerting")
print("- Multi-agent workflow observability")
print("- Production-ready monitoring patterns")
-115
View File
@@ -1,115 +0,0 @@
#!/usr/bin/env python3
"""
JIRA Agent Watch Example
This example demonstrates how to use the JIRA watch tools with PraisonAI agents.
The agent can monitor JIRA issues and projects for changes, search for issues,
and get detailed issue information.
Setup:
1. Install JIRA library: pip install jira
2. Set environment variables:
- [email protected] (for cloud JIRA)
- JIRA_API_TOKEN=your_api_token
- Or use JIRA_USERNAME + JIRA_API_TOKEN for server JIRA
Usage:
python jira_agent_example.py
"""
import os
from praisonaiagents import Agent
from praisonaiagents.tools import jira_tools
def main():
"""Demonstrate JIRA agent watch capabilities."""
# Check if JIRA credentials are available
jira_email = os.getenv('JIRA_EMAIL')
jira_token = os.getenv('JIRA_API_TOKEN')
jira_username = os.getenv('JIRA_USERNAME')
if not jira_token:
print("❌ JIRA_API_TOKEN environment variable not set")
print("Please set your JIRA API token:")
print("export JIRA_API_TOKEN='your_token_here'")
return
if not (jira_email or jira_username):
print("❌ JIRA credentials incomplete")
print("Please set either:")
print("- JIRA_EMAIL (for cloud JIRA)")
print("- JIRA_USERNAME (for server JIRA)")
return
# Create agent with JIRA tools
agent = Agent(
name="JIRA Monitor Agent",
instructions="""
You are a JIRA monitoring agent. You can:
1. Watch specific JIRA issues for changes
2. Monitor JIRA projects for new issues and updates
3. Search for issues using JQL queries
4. Get detailed information about specific issues
When using JIRA tools, always provide the full JIRA URL
(e.g., https://yourcompany.atlassian.net).
Be helpful and provide clear summaries of JIRA activity.
""",
tools=jira_tools(), # Add all JIRA tools
llm="gpt-4o-mini"
)
print("🎯 JIRA Agent Watch Example")
print("=" * 50)
print()
print("Available JIRA tools:")
print("- jira_watch_issue: Monitor a specific issue for changes")
print("- jira_watch_project: Monitor a project for new/updated issues")
print("- jira_get_issue_info: Get detailed info about an issue")
print("- jira_search_issues: Search issues using JQL")
print()
# Example interactions
example_prompts = [
"Get information about JIRA issue DEMO-1 from https://praisonai.atlassian.net",
"Search for open issues in project DEMO from https://praisonai.atlassian.net using JQL: 'project = DEMO AND status = Open'",
"Watch JIRA issue DEMO-1 from https://praisonai.atlassian.net for 2 minutes (check every 60 seconds, max 2 checks)",
]
print("📝 Example prompts you can try:")
for i, prompt in enumerate(example_prompts, 1):
print(f"{i}. {prompt}")
print()
# Interactive mode
print("💬 Interactive JIRA Agent (type 'quit' to exit)")
print("-" * 50)
while True:
try:
user_input = input("\n🧑 You: ").strip()
if user_input.lower() in ['quit', 'exit', 'q']:
print("👋 Goodbye!")
break
if not user_input:
continue
print("🤖 Agent: ", end="", flush=True)
response = agent.start(user_input)
print(response)
except KeyboardInterrupt:
print("\n👋 Goodbye!")
break
except Exception as e:
print(f"❌ Error: {e}")
if __name__ == "__main__":
main()
@@ -5,17 +5,11 @@ from dotenv import load_dotenv
load_dotenv()
serper_api_key = os.getenv("SERPER_API_KEY")
if serper_api_key is not None:
os.environ["SERPER_API_KEY"] = serper_api_key
os.environ['SERPER_API_KEY'] = os.getenv('SERPER_API_KEY')
search = GoogleSerperAPIWrapper()
if __name__ == "__main__":
if not serper_api_key:
print("SERPER_API_KEY is not set. Skipping Google Serper example.")
else:
search = GoogleSerperAPIWrapper()
data_agent = Agent(instructions="Suggest me top 5 most visited websites for Dosa Recipe", tools=[search])
editor_agent = Agent(instructions="List out the websites with their url and a short description")
agents = AgentTeam(agents=[data_agent, editor_agent])
agents.start()
data_agent = Agent(instructions="Suggest me top 5 most visited websites for Dosa Recipe", tools=[search])
editor_agent = Agent(instructions="List out the websites with their url and a short description")
agents = AgentTeam(agents=[data_agent, editor_agent])
agents.start()
-227
View File
@@ -1,227 +0,0 @@
"""
Named Toolsets Example
This example demonstrates how to use named toolsets to organize and reuse
groups of tools across agents and workflows.
Toolsets provide:
- Organized tool collections (web, research, development, etc.)
- Progressive disclosure (safe → research → development)
- Composable design (toolsets can include other toolsets)
- YAML configuration support
"""
import os
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.toolsets import (
list_toolsets,
resolve_toolset,
register_toolset
)
# Set up environment (replace with your actual API key)
os.environ.setdefault("OPENAI_API_KEY", "your-openai-api-key-here")
def demo_basic_toolsets():
"""Demonstrate basic toolset functionality."""
print("=== Basic Toolsets Demo ===")
# List available toolsets
available_toolsets = list_toolsets()
print(f"Available toolsets: {available_toolsets}")
# Show what tools are in each prebuilt toolset
for toolset_name in ["safe", "web", "research"]:
tools = resolve_toolset(toolset_name)
print(f"\n{toolset_name.upper()} toolset contains: {tools}")
def demo_agent_with_toolsets():
"""Demonstrate agents using toolsets."""
print("\n=== Agent with Toolsets Demo ===")
# Create a research agent with research toolset
researcher = Agent(
name="researcher",
role="Research Specialist",
goal="Research topics thoroughly using web tools",
instructions="You are a research specialist with access to web search and file tools.",
toolsets=["research"], # includes web tools + file tools
llm="gpt-4o-mini"
)
print(f"Research agent has {len(researcher.tools)} tools available")
print(f"Tools: {[getattr(t, '__name__', str(t)) for t in researcher.tools]}")
# Create a safe agent with minimal tools
safe_agent = Agent(
name="safe_assistant",
role="Safe Assistant",
goal="Help users safely without risky operations",
instructions="You are a helpful assistant with access to only safe, read-only tools.",
toolsets=["safe"],
llm="gpt-4o-mini"
)
print(f"\nSafe agent has {len(safe_agent.tools)} tools available")
print(f"Safe tools: {[getattr(t, '__name__', str(t)) for t in safe_agent.tools]}")
def demo_custom_toolsets():
"""Demonstrate creating and using custom toolsets."""
print("\n=== Custom Toolsets Demo ===")
# Register a custom toolset for data analysis
register_toolset(
"data_analysis",
tools=["read_file", "write_file"],
includes=["safe"], # Include safe tools too
description="Tools for data analysis workflows"
)
# Create an agent with the custom toolset
analyst = Agent(
name="data_analyst",
role="Data Analyst",
goal="Analyze data files and generate reports",
instructions="You specialize in data analysis. Read files, process data, and write reports.",
toolsets=["data_analysis"],
llm="gpt-4o-mini"
)
print(f"Data analyst has {len(analyst.tools)} tools available")
print(f"Analysis tools: {[getattr(t, '__name__', str(t)) for t in analyst.tools]}")
def demo_mixed_tools_and_toolsets():
"""Demonstrate mixing explicit tools with toolsets."""
print("\n=== Mixed Tools and Toolsets Demo ===")
# Create agent with both explicit tools and toolsets
hybrid_agent = Agent(
name="hybrid_assistant",
role="Hybrid Assistant",
goal="Assist with both research and custom tasks",
instructions="You can research topics and perform custom analysis tasks.",
tools=["write_file"], # Explicit tool
toolsets=["research"], # Toolset with web + file tools
llm="gpt-4o-mini"
)
print(f"Hybrid agent has {len(hybrid_agent.tools)} tools available")
tool_names = [getattr(t, '__name__', str(t)) for t in hybrid_agent.tools]
print(f"Hybrid tools: {tool_names}")
# Note: write_file might appear twice (once explicit, once from toolset)
# The agent handles deduplication automatically
def demo_multi_agent_with_toolsets():
"""Demonstrate multi-agent workflows with different toolsets."""
print("\n=== Multi-Agent with Toolsets Demo ===")
# Create agents with different toolsets for different roles
researcher = Agent(
name="researcher",
role="Research Specialist",
goal="Research topics and gather information",
toolsets=["research"],
llm="gpt-4o-mini"
)
writer = Agent(
name="writer",
role="Content Writer",
goal="Write content based on research",
toolsets=["safe"], # Safe tools for writing
llm="gpt-4o-mini"
)
# Create tasks for each agent
research_task = Task(
name="research_task",
description="Research the benefits of renewable energy",
expected_output="A summary of key benefits of renewable energy",
agent=researcher
)
writing_task = Task(
name="writing_task",
description="Write a blog post about renewable energy based on the research",
expected_output="A well-written blog post about renewable energy benefits",
agent=writer
)
# Create team with different toolsets per agent
team = AgentTeam(
agents=[researcher, writer],
tasks=[research_task, writing_task],
process="sequential"
)
print(f"Team created with {len(team.agents)} agents using different toolsets")
print(f"Research agent tools: {len(researcher.tools)}")
print(f"Writer agent tools: {len(writer.tools)}")
def demo_yaml_config():
"""Show how toolsets work in YAML configuration."""
print("\n=== YAML Configuration Example ===")
yaml_config = """
# agents.yaml - Example using toolsets
agents:
researcher:
role: Research Specialist
goal: Research topics thoroughly
toolsets:
- research # Includes web search, file tools
llm: gpt-4o-mini
analyst:
role: Data Analyst
goal: Analyze research data
tools:
- write_file # Explicit tool
toolsets:
- safe # Safe toolset
llm: gpt-4o-mini
tasks:
research_task:
description: Research renewable energy trends
agent: researcher
expected_output: Research summary
analysis_task:
description: Analyze the research data
agent: analyst
expected_output: Analysis report
"""
print("Example YAML configuration with toolsets:")
print(yaml_config)
print("Use: praisonai run 'Research renewable energy' --toolset research")
print("Or: praisonai chat --toolset web,files")
if __name__ == "__main__":
print("🛠️ PraisonAI Named Toolsets Example")
print("=" * 50)
# Run all demos
demo_basic_toolsets()
demo_agent_with_toolsets()
demo_custom_toolsets()
demo_mixed_tools_and_toolsets()
demo_multi_agent_with_toolsets()
demo_yaml_config()
print("\n✅ Toolsets example completed!")
print("\n💡 Key Benefits:")
print(" • Organized tool collections")
print(" • Progressive disclosure (safe → research → development)")
print(" • Reusable across agents and workflows")
print(" • YAML configuration support")
print(" • Composable design with includes")
@@ -2,7 +2,6 @@ from praisonaiagents import Agent, Task, AgentTeam
from pydantic import BaseModel
from typing import List, Dict
from gitingest import ingest
import sys
class CodeMetrics(BaseModel):
category: str
@@ -61,10 +60,7 @@ def analyze_code(code_source: str) -> CodeAnalysisReport:
Analyze code from directory path or GitHub URL
"""
# Ingest code content
try:
summary, tree, content = ingest(code_source)
except Exception as exc:
raise RuntimeError(f"Failed to ingest code source: {exc}") from exc
summary, tree, content = ingest(code_source)
# Concatenate context into structured format
context_text = f"""
@@ -90,22 +86,10 @@ def analyze_code(code_source: str) -> CodeAnalysisReport:
tasks=[code_analysis_task]
)
result = agents.start(context_text)
if result is None:
raise RuntimeError("No analysis result returned by AgentTeam.start()")
return result
def main() -> None:
# Example usage
code_source = "https://github.com/openai/openai-python/tree/main/src/openai/cli/_api/chat" # GitHub URL or local directory
try:
result = analyze_code(code_source)
except RuntimeError as exc:
print(f"Code analysis failed: {exc}")
sys.exit(1)
print(result)
return agents.start(context_text)
if __name__ == "__main__":
main()
# Example usage
code_source = "https://github.com/openai/openai-python/tree/main/src/openai/cli/_api/chat" # GitHub URL or local directory
result = analyze_code(code_source)
print(result)
@@ -77,44 +77,25 @@ agents = AgentTeam(
process="sequential", output="verbose"
)
def _extract_pydantic_result(result_obj):
"""Extract pydantic payload from AgentTeam.start() output shapes."""
if hasattr(result_obj, "pydantic"):
return result_obj.pydantic
if isinstance(result_obj, dict):
task_results = result_obj.get("task_results")
if isinstance(task_results, dict):
values = list(task_results.values())
if values and hasattr(values[-1], "pydantic"):
return values[-1].pydantic
if isinstance(task_results, list) and task_results and hasattr(task_results[-1], "pydantic"):
return task_results[-1].pydantic
return None
# Start the analysis
result = agents.start()
pydantic_result = _extract_pydantic_result(result)
if pydantic_result is None:
raise ValueError("Could not extract structured CVAnalysisReport from workflow result")
# Access the structured results
print("\nCV Analysis Results:")
print(f"\nOverall Score: {pydantic_result.overall_score}/100")
print(f"\nOverall Score: {result.pydantic.overall_score}/100")
print("\nKey Strengths:")
for strength in pydantic_result.key_strengths:
for strength in result.pydantic.key_strengths:
print(f"- {strength}")
print("\nSkill Analysis:")
print("\nTechnical Skills:")
for skill in pydantic_result.skill_analysis.technical_skills:
for skill in result.pydantic.skill_analysis.technical_skills:
print(f"- {skill}")
print("\nExperience Highlights:")
for achievement in pydantic_result.experience_analysis.key_achievements:
for achievement in result.pydantic.experience_analysis.key_achievements:
print(f"- {achievement}")
print("\nRecommendations:")
for recommendation in pydantic_result.recommendations:
for recommendation in result.pydantic.recommendations:
print(f"- {recommendation}")
@@ -100,8 +100,6 @@ def evaluate_project(video_path: str) -> ProjectEvaluation:
evaluation_data = response
print(f"Debug - Parsed evaluation_data: {evaluation_data}")
if not evaluation_data or not isinstance(evaluation_data, dict):
raise ValueError("No structured evaluation data returned by agent")
# Create and return ProjectEvaluation instance
return ProjectEvaluation(
+3 -9
View File
@@ -1,11 +1,5 @@
from praisonaiagents import VideoAgent
import os
if __name__ == "__main__":
if not os.getenv("AZURE_API_BASE"):
print("AZURE_API_BASE is not configured. Skipping Azure video example.")
else:
agent = VideoAgent(llm="azure/sora-2")
video = agent.generate("Mountain landscape")
print(video.id)
agent = VideoAgent(llm="azure/sora-2")
video = agent.generate("Mountain landscape")
print(video.id)
+1 -1
View File
@@ -5,7 +5,7 @@ Demonstrates using Agent instances directly as workflow steps.
Each agent processes the input and passes output to the next agent.
"""
from praisonaiagents import Agent, AgentFlow
from praisonaiagents import Agent, Workflow
# Create agents with specific roles
researcher = Agent(
+2 -5
View File
@@ -6,7 +6,6 @@ with Agents for workflow monitoring and logging.
"""
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.config.feature_configs import MultiAgentHooksConfig
# Create an agent
researcher = Agent(
@@ -48,10 +47,8 @@ if __name__ == "__main__":
agents=[researcher],
tasks=[task1, task2],
process="workflow",
hooks=MultiAgentHooksConfig(
on_task_start=on_start,
on_task_complete=on_complete,
),
on_task_start=on_start,
on_task_complete=on_complete,
variables={"global_var": "shared_value"} # Global variables
)
@@ -6,6 +6,7 @@ to different next steps based on output content.
"""
from praisonaiagents import AgentFlow, Task
from praisonaiagents import AgentFlowManager
# Create a workflow with branching
workflow = AgentFlow(
@@ -35,9 +36,16 @@ workflow = AgentFlow(
)
if __name__ == "__main__":
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
# Create manager and register workflow
manager = WorkflowManager()
manager.workflows["Decision Workflow"] = workflow
# Execute - will branch to success_handler since 42 is positive
result = manager.execute(
"Decision Workflow",
default_llm="gpt-4o-mini"
)
print("Workflow completed!")
for step in result.get("steps", []):
name = step.get("step", step.get("name", "unknown"))
status = step.get("status", "completed")
print(f" {name}: {status}")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['status']}")
@@ -54,13 +54,11 @@ workflow = AgentFlow(
analyzer, # Always runs
Task(
name="tech_review",
description="Review technical aspects when technical keywords are detected.",
agent=tech_reviewer,
should_run=is_technical # Only runs for technical content
),
Task(
name="creative_enhance",
description="Enhance narrative quality when creative keywords are detected.",
agent=creative_enhancer,
should_run=needs_creative # Only runs for creative content
),
@@ -6,8 +6,9 @@ functions that return StepResult with stop_workflow=True.
"""
from praisonaiagents import (
AgentFlow, Task, WorkflowContext, StepResult
Workflow, Task, WorkflowContext, StepResult
)
from praisonaiagents import AgentFlowManager
# Custom validator that can stop the workflow
def validate_data(context: WorkflowContext) -> StepResult:
@@ -49,13 +50,20 @@ workflow = AgentFlow(
)
if __name__ == "__main__":
manager = WorkflowManager()
manager.workflows["Data Processing"] = workflow
# Test with invalid data (will stop early)
print("=== Testing with invalid data ===")
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
for step in result.get("steps", []):
print(f" {step.get('step', 'step')}: {step.get('output', step.get('status'))}")
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['output']}")
# Test with valid data (will complete)
print("\n=== Testing with valid data ===")
workflow.variables["data"] = {"value": 42}
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
for step in result.get("steps", []):
print(f" {step.get('step', 'step')}: {step.get('status', 'completed')}")
result = manager.execute("Data Processing", default_llm="gpt-4o-mini")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['status']}")
+15 -8
View File
@@ -6,6 +6,7 @@ Each item is processed individually with the loop variable.
"""
from praisonaiagents import AgentFlow, Task
from praisonaiagents import AgentFlowManager
# Create a workflow with loop
workflow = AgentFlow(
@@ -29,12 +30,18 @@ workflow = AgentFlow(
)
if __name__ == "__main__":
result = workflow.run("", llm="gpt-4o-mini", verbose=True)
# Create manager and register workflow
manager = WorkflowManager()
manager.workflows["Process Items"] = workflow
# Execute - will process each fruit in the list
result = manager.execute(
"Process Items",
default_llm="gpt-4o-mini"
)
print("Workflow completed!")
for step in result.get("steps", []):
name = step.get("step", step.get("name", "unknown"))
status = step.get("status", "completed")
print(f" {name}: {status}")
out = step.get("output")
if isinstance(out, list):
print(f" Loop processed {len(out)} items")
for step_result in result["results"]:
print(f" {step_result['step']}: {step_result['status']}")
if isinstance(step_result.get("output"), list):
print(f" Loop processed {len(step_result['output'])} items")
@@ -5,7 +5,7 @@ Demonstrates running multiple agents concurrently and
combining their results with an aggregator agent.
"""
from praisonaiagents import Agent, AgentFlow
from praisonaiagents import Agent, Workflow
from praisonaiagents.workflows import parallel
# Create parallel research agents
@@ -42,7 +42,6 @@ def main():
# Create tasks with robustness features
research_task = Task(
name="research",
description="Research the latest trends in AI agents for 2025",
expected_output="A summary of key AI agent trends",
agent=researcher,
@@ -52,7 +51,6 @@ def main():
# Optional enrichment step - workflow continues even if this fails
enrich_task = Task(
name="enrich",
description="Enrich the research with real-world examples",
expected_output="Research with added examples",
agent=enricher,
@@ -62,7 +60,6 @@ def main():
)
write_task = Task(
name="write",
description="Write a brief summary based on the research",
expected_output="A well-written summary paragraph",
agent=writer,
@@ -5,7 +5,7 @@ Demonstrates decision-based routing where an agent classifier
routes to specialized handler agents based on the request type.
"""
from praisonaiagents import Agent, AgentFlow
from praisonaiagents import Agent, Workflow
from praisonaiagents.workflows import route
# Create classifier agent
+1 -1
View File
@@ -190,7 +190,7 @@ def example_run_history_with_backend():
# Use with RunHistory
history = RunHistory(backend=backend)
runs = history.list_runs()
runs = history.list()
print(f"RunHistory created with SQLite backend, {len(runs)} runs")
print("Recipe runs will be stored in database instead of files")
+1 -1
View File
@@ -257,7 +257,7 @@ if __name__ == "__main__":
# Check dependencies
try:
import praisonaiagents
print(f"✅ PraisonAI version: {getattr(praisonaiagents, '__version__', None)}")
print(f"✅ PraisonAI version: {praisonaiagents.__version__}")
except ImportError:
print("❌ PraisonAI not installed: pip install praisonaiagents")
sys.exit(1)
+1 -1
View File
@@ -12,7 +12,7 @@ assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
# Enable web search and fetch
agent = Agent(
instructions="You are a research assistant with web access.",
web="search_only",
web=True,
)
# Disable web (default)
+11 -3
View File
@@ -4,7 +4,7 @@ WebConfig Example
Demonstrates using WebConfig for fine-grained control.
"""
import os
from praisonaiagents import Agent
from praisonaiagents import Agent, WebConfig
# Ensure API key is set from environment
assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
@@ -12,13 +12,21 @@ assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY must be set"
# Custom web configuration
agent = Agent(
instructions="You are a research assistant.",
web="search_only",
web=WebConfig(
search=True,
fetch=True,
search_provider="duckduckgo",
max_results=5,
),
)
# Search only (no fetch)
agent_search_only = Agent(
instructions="You are a research assistant.",
web="search_only",
web=WebConfig(
search=True,
fetch=False,
),
)
if __name__ == "__main__":
@@ -1,47 +0,0 @@
framework: praisonai
topic: AI Research with Message Steering
roles:
researcher:
role: AI Research Analyst
goal: Conduct comprehensive AI research with real-time guidance
backstory: You are an expert AI researcher who can receive and incorporate real-time guidance during your work
message_steering: true # Enable real-time message steering
tools:
- internet_search
- write_file
tasks:
research_task:
description: |
Research the latest developments in AI and write a comprehensive report.
You may receive additional guidance during execution via message steering.
expected_output: |
A well-structured research report covering recent AI developments,
incorporating any real-time guidance received during execution.
writer:
role: Technical Writer
goal: Write clear, engaging content based on research
backstory: You are a skilled technical writer who can adapt content based on real-time feedback
message_steering: true # Enable steering for dynamic content adjustment
tools:
- read_file
- write_file
tasks:
writing_task:
description: |
Take the research findings and create an accessible article for a general audience.
Adjust style and focus based on any steering messages received.
expected_output: |
A polished article that transforms technical research into accessible content,
refined based on real-time steering guidance.
dependencies:
- writing_task: [research_task]
# Usage Instructions:
# 1. Run: praisonai --agent-file message_steering_example.yaml
# 2. While running, use steering API to send guidance:
# agent.steer("Focus more on practical applications")
# agent.steer("Keep it under 500 words", priority=15)
# agent.steer("Add more examples", priority=10)
-148
View File
@@ -1,148 +0,0 @@
# Named Toolsets YAML Example
# This example demonstrates using toolsets in YAML configuration
# Global configuration
config:
llm: gpt-4o-mini
memory: true
# Define agents with different toolsets
agents:
# Research agent with research toolset (includes web tools)
researcher:
role: Senior Research Analyst
goal: Research topics thoroughly using web search and analysis tools
instructions: |
You are a senior research analyst with access to web search and file tools.
Research topics comprehensively and provide detailed findings.
toolsets:
- research # Includes: web search, file tools, basic analysis
# Writer agent with safe toolset for writing tasks
writer:
role: Content Writer
goal: Create engaging content based on research
instructions: |
You are a skilled content writer. Transform research into clear,
engaging content for the target audience.
toolsets:
- safe # Safe, read-only tools for writing
# Developer agent with development toolset
developer:
role: Software Developer
goal: Create and modify code based on requirements
instructions: |
You are a software developer with access to development tools.
Write, test, and debug code according to specifications.
toolsets:
- development # Includes: code tools, file operations, testing
# Data analyst with mixed tools and toolsets
analyst:
role: Data Analyst
goal: Analyze data and generate insights
instructions: |
You are a data analyst. Process data files and generate
actionable insights and visualizations.
tools:
- read_file # Explicit tool
- write_file # Explicit tool
toolsets:
- safe # Safe toolset for analysis
# Security-focused agent with minimal toolset
security_auditor:
role: Security Auditor
goal: Review and audit for security issues
instructions: |
You are a security auditor with minimal tool access.
Focus on reading and analyzing without making changes.
toolsets:
- safe # Minimal, safe tools only
# Define tasks for the workflow
tasks:
# Research task using research toolset
research_phase:
description: |
Research the latest trends in AI agent frameworks.
Focus on:
- Popular frameworks and their features
- Industry adoption patterns
- Security considerations
- Performance benchmarks
expected_output: |
A comprehensive research report covering AI agent frameworks,
including trends, adoption, security, and performance data.
agent: researcher
# Content creation using safe toolset
content_creation:
description: |
Create a technical blog post based on the research findings.
The post should be:
- Well-structured with clear sections
- Accessible to technical and non-technical audiences
- Include actionable insights
- 1000-1500 words
expected_output: |
A polished blog post about AI agent frameworks suitable
for publication on a technical blog.
agent: writer
# Code development using development toolset
code_examples:
description: |
Create code examples demonstrating key concepts from the research.
Include:
- Simple "hello world" style examples
- Intermediate use cases
- Best practices examples
- Proper documentation
expected_output: |
A collection of well-documented code examples that illustrate
the concepts discussed in the research and blog post.
agent: developer
# Data analysis using mixed tools/toolsets
trend_analysis:
description: |
Analyze the research data to identify trends and patterns.
Create:
- Summary statistics
- Trend analysis
- Recommendations based on findings
expected_output: |
A data analysis report with trends, patterns, and
actionable recommendations for AI framework adoption.
agent: analyst
# Security review using minimal toolset
security_review:
description: |
Review all outputs for security considerations.
Check for:
- Potential security vulnerabilities in code examples
- Security best practices in recommendations
- Privacy considerations in data handling
expected_output: |
A security review report identifying any security concerns
and recommendations for secure implementation.
agent: security_auditor
# Workflow process (sequential execution)
process: sequential
# Optional: Define workflow dependencies
# tasks:
# research_phase:
# dependencies: []
# content_creation:
# dependencies: [research_phase]
# code_examples:
# dependencies: [research_phase]
# trend_analysis:
# dependencies: [research_phase]
# security_review:
# dependencies: [content_creation, code_examples, trend_analysis]
@@ -539,7 +539,7 @@ Repeat Phases 12 → 13 → 14 until the mergeability gate in Phase 13 is fully
### When the PR has merge conflicts
If `mergeStateStatus=DIRTY`, the **Auto Merge Conflict → Claude** workflow (`.github/workflows/merge-conflict-claude.yml`) posts `@claude` automatically on push to `main` and on PR sync. You can still comment manually:
If `mergeStateStatus=DIRTY`, ask Claude to rebase rather than resolving conflicts yourself on non-trivial logic:
```bash
gh pr comment $PR --repo $REPO --body "@claude this PR has merge conflicts with \`main\` (PR #<X> was merged, touching the same regions). Please rebase onto latest \`main\`, re-run \`pytest <paths>\`, and force-push."
@@ -755,20 +755,6 @@ Developer Push ─────────────────────
**Result**: `https://praison.ai/install.sh` is automatically updated **only when the source files change**!
### Auto-deploy when install scripts change (PraisonAI → praison.ai)
Pushing `install.sh` or `install.ps1` to PraisonAI **`main`** triggers [`.github/workflows/deploy-install-scripts.yml`](/Users/praison/praisonai-package/.github/workflows/deploy-install-scripts.yml), which dispatches the existing praison.ai **`CI`** workflow (`workflow_dispatch` on `master`). No manual website push or `make upgrade` is required.
```mermaid
flowchart LR
pushMain["PraisonAI push main\ninstall.sh|install.ps1"] --> deployWF["deploy-install-scripts.yml"]
deployWF --> dispatch["gh workflow run CI"]
dispatch --> siteCI["praison.ai azure-pipeline.yaml"]
siteCI --> live["https://praison.ai/install.sh"]
```
Manual re-deploy: `gh workflow run "Deploy install scripts to praison.ai" --repo MervinPraison/PraisonAI`
### How install.sh is Served
The `web/install.sh` file is copied into the Docker image at `/var/www/html/web/install.sh` and served by nginx:
@@ -811,20 +797,20 @@ git push origin master
**Task 2: Update install.sh and deploy**
```bash
# 1. Update install.sh / install.ps1
# 1. Update install.sh
vim /Users/praison/praisonai-package/src/praisonai/scripts/install.sh
# 2. Test install.sh
bash /Users/praison/praisonai-package/src/praisonai/scripts/test-install-smoke.sh
bash /Users/praison/praisonai-package/src/praisonai/scripts/install.sh --dry-run
# 3. Commit and push to PraisonAI main — auto-triggers praison.ai deploy
git add src/praisonai/scripts/install.sh src/praisonai/scripts/install.ps1
# 3. Commit and push
git add /Users/praison/praisonai-package/src/praisonai/scripts/install.sh
git commit -m "fix: update install script"
git push origin main
# deploy-install-scripts.yml → praison.ai CI → https://praison.ai/install.sh
# Manual re-deploy (optional):
gh workflow run "Deploy install scripts to praison.ai" --repo MervinPraison/PraisonAI
# 4. Deploy website (if needed)
cd /Users/praison/Sites/localhost/praisonai
make upgrade
```
**Task 3: Rollback deployment**
@@ -257,21 +257,6 @@ find $(python -c "import site; print(site.getsitepackages()[0])") -name "<suspic
---
## Fork PRs — contributor checklist and Claude push
For PRs from external forks (`head.repo != MervinPraison/PraisonAI`), ask the contributor:
1. **Allow edits from maintainers** on the PR (right sidebar).
2. **Keep fork and branch** until the PR is merged or closed.
Optional: private fork → add maintainer as collaborator; fork with Actions → only enable “Allow edits and access to secrets by maintainers” if they accept the risk.
When `maintainerCanModify` is true, `claude.yml` pushes via `GH_TOKEN` to the fork remote and `merge-conflict-claude.yml` may auto-trigger `@claude` for `DIRTY` PRs (after FINAL architecture review).
Manual unblock: `gh pr checkout <N> && git fetch origin main && git rebase origin/main && git push --force-with-lease`
---
## Quick Reference — Common Commands
| Action | Command |
+1 -4
View File
@@ -378,7 +378,7 @@ Agent parameters are consolidated into Config objects following the pattern:
| Config Object | Replaces (Deprecated) |
|--------------|----------------------|
| `execution=ExecutionConfig(code_execution=True, code_mode="safe", rate_limiter=obj, context_compaction=False)` | `allow_code_execution`, `code_execution_mode`, `rate_limiter` |
| `execution=ExecutionConfig(code_execution=True, code_mode="safe", rate_limiter=obj)` | `allow_code_execution`, `code_execution_mode`, `rate_limiter` |
| `memory=MemoryConfig(auto_save="session_name")` | `auto_save` |
| `autonomy=AutonomyConfig(verification_hooks=[...])` | `verification_hooks` |
| `handoffs=[other_agent]` | `allow_delegation` |
@@ -390,8 +390,6 @@ Agent parameters are consolidated into Config objects following the pattern:
**Deprecated standalone params still work** (backward compatible) but emit `DeprecationWarning`.
> **📢 Deprecation Notice:** `ExecutionConfig.context_compaction` currently defaults to `False` during a deprecation period. In the next release, it will default to `True` for automatic proactive context overflow protection. To opt out explicitly, set `context_compaction=False`. To use the new behavior early, set `context_compaction=True`.
```python
# ❌ Old way (deprecated)
agent = Agent(
@@ -415,7 +413,6 @@ agent = Agent(
code_execution=True,
code_mode="safe",
rate_limiter=my_limiter,
context_compaction=False, # prepare for next release when default becomes True; warning still fires during deprecation period
),
memory=MemoryConfig(auto_save="my_session"),
autonomy=AutonomyConfig(verification_hooks=[my_hook]),
+81 -59
View File
@@ -1,6 +1,6 @@
# Documentation Parity Tracker (Python)
> **Categories:** 66 | **Documented:** 65 | **Parity:** 98.5%
> **Categories:** 60 | **Documented:** 57 | **Parity:** 95.0%
This report compares **Python SDK feature categories** against **Python documentation** (docs/concepts, docs/features, etc.).
@@ -8,86 +8,108 @@ This report compares **Python SDK feature categories** against **Python document
| Metric | Count |
|--------|-------|
| Feature Categories | 66 |
| **Documented Categories** | **65** |
| **Undocumented Categories** | **1** |
| **Parity** | **98.5%** |
| Feature Categories | 60 |
| **Documented Categories** | **57** |
| **Undocumented Categories** | **3** |
| **Parity** | **95.0%** |
## Documented Categories
| Category | Features | Docs | Lines |
|----------|----------|------|-------|
| ✅ AGUI | 1 | 2 | 420 |
| ✅ Agent | 20 | 40 | 10767 |
| ✅ Agent-to-Agent (A2A) | 1 | 7 | 2880 |
| ✅ Agent-to-User (A2U) | 1 | 3 | 575 |
| ✅ Approval | 1 | 3 | 1344 |
| ✅ Agent | 19 | 30 | 8367 |
| ✅ Agent-to-Agent (A2A) | 1 | 3 | 625 |
| ✅ Approval | 1 | 2 | 889 |
| ✅ Audio | 2 | 10 | 649 |
| ✅ Auto Generation | 5 | 6 | 1637 |
| ✅ Autonomy | 3 | 5 | 1919 |
| ✅ Bots | 7 | 21 | 6676 |
| ✅ Budget | 1 | 1 | 287 |
| ✅ CLI | 5 | 106 | 26315 |
| ✅ Chunking | 2 | 2 | 288 |
| ✅ Auto Generation | 3 | 6 | 1386 |
| ✅ Autonomy | 3 | 4 | 1146 |
| ✅ Bots | 5 | 2 | 573 |
| ✅ Chunking | 2 | 2 | 285 |
| ✅ Citations | 2 | 1 | 202 |
| ✅ Code Execution | 2 | 13 | 3551 |
| ✅ Conditions | 1 | 3 | 1085 |
| ✅ Configuration | 3 | 7 | 2069 |
| ✅ Context Management | 16 | 33 | 10206 |
| ✅ Database | 1 | 40 | 5373 |
| ✅ Deep Research | 8 | 2 | 437 |
| ✅ Code Execution | 3 | 13 | 3395 |
| ✅ Conditions | 1 | 3 | 1086 |
| ✅ Configuration | 2 | 7 | 1753 |
| ✅ Context Management | 13 | 29 | 7576 |
| ✅ Database | 1 | 31 | 3037 |
| ✅ Display | 6 | 2 | 1175 |
| ✅ Embeddings | 6 | 23 | 1966 |
| ✅ Evaluation | 1 | 6 | 2539 |
| ✅ Events | 1 | 2 | 764 |
| ✅ Execution | 3 | 3 | 1099 |
| ✅ Failover | 2 | 1 | 372 |
| ✅ Files | 2 | 6 | 1361 |
| ✅ Flow | 1 | 2 | 435 |
| ✅ Gateway | 7 | 18 | 6400 |
| ✅ Guardrails | 4 | 4 | 1619 |
| ✅ Handoffs | 11 | 6 | 2499 |
| ✅ Hooks | 2 | 8 | 3100 |
| ✅ Evaluation | 1 | 6 | 2538 |
| ✅ Events | 1 | 2 | 699 |
| ✅ Execution | 3 | 2 | 515 |
| ✅ Files | 2 | 5 | 863 |
| ✅ Flow | 1 | 1 | 284 |
| ✅ Gateway | 6 | 1 | 256 |
| ✅ Guardrails | 4 | 4 | 2279 |
| ✅ Handoffs | 10 | 4 | 1674 |
| ✅ Hooks | 2 | 6 | 1606 |
| ✅ Image | 1 | 11 | 1096 |
| ✅ Knowledge | 4 | 23 | 4282 |
| ✅ LLM | 3 | 10 | 3674 |
| ✅ Loops | 4 | 3 | 774 |
| ✅ MCP | 1 | 51 | 10933 |
| ✅ Memory | 6 | 16 | 5879 |
| ✅ OCR | 2 | 1 | 226 |
| ✅ Observability | 2 | 23 | 2652 |
| ✅ Optimizer | 1 | 2 | 653 |
| ✅ Output | 3 | 4 | 860 |
| ✅ Parallel Execution | 3 | 2 | 399 |
| ✅ Planning | 6 | 5 | 1198 |
| ✅ Plugins | 8 | 3 | 1502 |
| ✅ Prompts | 2 | 7 | 1569 |
| ✅ Providers | 1 | 52 | 6214 |
| ✅ Knowledge | 3 | 12 | 3262 |
| ✅ LLM | 2 | 2 | 1049 |
| ✅ Loops | 4 | 1 | 250 |
| ✅ MCP | 2 | 47 | 9754 |
| ✅ Memory | 6 | 12 | 4474 |
| ✅ Output | 3 | 4 | 868 |
| ✅ Parallel Execution | 2 | 2 | 347 |
| ✅ Planning | 6 | 5 | 1506 |
| ✅ Plugins | 8 | 3 | 1494 |
| ✅ Prompts | 2 | 4 | 796 |
| ✅ Providers | 2 | 50 | 5396 |
| ✅ Query | 1 | 2 | 616 |
| ✅ RAG | 5 | 15 | 3190 |
| ✅ RAG | 5 | 14 | 2910 |
| ✅ Realtime | 2 | 5 | 584 |
| ✅ Reflection | 3 | 2 | 477 |
| ✅ Retrieval | 2 | 5 | 944 |
| ✅ Reflection | 3 | 2 | 469 |
| ✅ Retrieval | 2 | 5 | 941 |
| ✅ Routing | 1 | 2 | 597 |
| ✅ Sandbox | 5 | 6 | 2478 |
| ✅ Security | 1 | 3 | 2550 |
| ✅ Sessions | 4 | 11 | 3423 |
| ✅ Skills | 6 | 9 | 2983 |
| ✅ Tasks | 2 | 6 | 3064 |
| ✅ Telemetry | 1 | 2 | 560 |
| ✅ Sandbox | 5 | 3 | 1002 |
| ✅ Security | 1 | 1 | 875 |
| ✅ Sessions | 2 | 8 | 2128 |
| ✅ Skills | 5 | 5 | 2002 |
| ✅ Tasks | 2 | 5 | 2590 |
| ✅ Telemetry | 1 | 2 | 541 |
| ✅ Templates | 1 | 7 | 1411 |
| ✅ Tools | 12 | 118 | 25868 |
| ✅ Tools | 4 | 97 | 17827 |
| ✅ Tracing | 3 | 2 | 139 |
| ✅ Video | 2 | 6 | 510 |
| ✅ Video | 2 | 5 | 460 |
| ✅ Vision | 2 | 1 | 283 |
| ✅ Web | 3 | 6 | 1352 |
| ✅ Workflows | 4 | 14 | 5810 |
| ✅ Web | 4 | 6 | 1267 |
| ✅ Workflows | 4 | 9 | 3579 |
## Stub Documentation (Need Content)
These categories have documentation files but < 50 lines (stubs):
| Category | Features | Docs | Lines |
|----------|----------|------|-------|
| ⚠️ OCR | 2 | 1 | 49 |
## Undocumented Categories (Need Documentation)
| Category | Features |
|----------|----------|
| ❌ Vector Store | 1 |
| ❌ Failover | 2 |
| ❌ Optimizer | 1 |
## Documentation Without Features
These docs exist but don't match any implemented feature category:
- ℹ️ AI SDK (1 docs, 112 lines)
- ℹ️ Agent-to-User (A2U) (3 docs, 770 lines)
- ℹ️ CLI (91 docs, 21386 lines)
- ℹ️ Callbacks (1 docs, 985 lines)
- ℹ️ Chat (3 docs, 546 lines)
- ℹ️ Deep Research (2 docs, 437 lines)
- ℹ️ Deployment (29 docs, 5931 lines)
- ℹ️ Jobs (1 docs, 358 lines)
- ℹ️ Middleware (1 docs, 60 lines)
- ℹ️ Observability (21 docs, 1580 lines)
- ℹ️ Process (1 docs, 506 lines)
- ℹ️ Scheduler (2 docs, 858 lines)
- ℹ️ Storage (4 docs, 722 lines)
- ℹ️ Streaming (3 docs, 485 lines)
- ℹ️ Token Management (3 docs, 1020 lines)
- ℹ️ Vector Store (11 docs, 733 lines)
---
@@ -1,70 +1,35 @@
# PraisonAI Agents - Benchmark Results
**Generated:** 2026-06-17
**Methodology:** 10 warmup runs + 1000 measured iterations (`performance_benchmark.py` config)
**Config:** `Agent(name=..., instructions=..., model="gpt-4o-mini", tools=[get_weather], output="silent")`
**Generated:** 2025-12-18 14:40:22
**Iterations:** 100
**Test:** Agent instantiation (without tools)
## Agent Instantiation Time
## Results
| Framework | Avg Time (μs) | Min (μs) | Median (μs) | Relative |
|-----------|---------------|----------|-------------|----------|
| OpenAI Agents SDK | 5.05 | — | — | 1.00x (fastest) |
| Agno | 5.27 | — | — | 1.04x |
| **PraisonAI** | **13.62** | **12.58** | **12.96** | **2.69x** |
| PraisonAI (LiteLLM) | 14.49 | 13.37 | 13.71 | 2.86x |
### PraisonAI detail (1000 iter)
| Variant | Avg (μs) | Min (μs) | Median (μs) | Std (μs) |
|---------|----------|----------|-------------|----------|
| With tools (`gpt-4o-mini`) | 13.62 | 12.58 | 12.96 | 2.73 |
| With tools (`openai/gpt-4o-mini`) | 14.49 | 13.37 | 13.71 | 4.14 |
| Minimal, no tools | 13.40 | — | — | — |
## Historical baseline
| Source | Config | Avg (μs) | Notes |
|--------|--------|----------|-------|
| `performance_benchmark.py` (Dec 2025) | tools + 1000 iter | **3.20** | Pre-init-bloat baseline |
| `simple_benchmark.py` (Dec 2025) | no tools, 100 iter | **3.77** | Saved in prior `BENCHMARK_RESULTS.md` |
| Current (Jun 2026) | tools + 1000 iter | **13.62** | After lazy-init optimisations (was ~31 μs pre-fix) |
| Framework | Avg Time (μs) | Relative |
|-----------|---------------|----------|
| **PraisonAI** | **3.77** | **1.00x (fastest)** |
| OpenAI Agents SDK | 5.26 | 1.39x |
| Agno | 5.64 | 1.49x |
| PraisonAI (LiteLLM) | 7.56 | 2.00x |
| PydanticAI | 226.94 | 60.16x |
| LangGraph | 4,558.71 | 1,209x |
| CrewAI | 15,607.92 | 4,138x |
## Package Versions
| Package | Version |
|---------|---------|
| PraisonAI | 1.6.60 |
| Agno | 2.4.2 |
| OpenAI Agents SDK | 0.7.0 |
|---------|--------|
| PraisonAI | 0.1.5 |
| Agno | 2.3.14 |
| PydanticAI | 1.35.0 |
| OpenAI Agents SDK | 0.6.3 |
| LangGraph | 1.0.5 |
| CrewAI | 1.6.1 |
## How to Reproduce
PraisonAI-only (fast; avoids CrewAI/LangGraph import hang):
```bash
cd praisonai-agents
PYTHONWARNINGS='ignore::DeprecationWarning' python -c "
import time, statistics
from typing import Literal
from praisonaiagents import Agent
def get_weather(city: Literal['nyc', 'sf']):
return 'cloudy' if city == 'nyc' else 'sunny'
def bench(fn, n=1000, w=10):
for _ in range(w): fn()
times = []
for _ in range(n):
s = time.perf_counter(); fn(); times.append(time.perf_counter()-s)
print(f'avg={statistics.mean(times)*1e6:.2f} μs')
bench(lambda: Agent(name='Test', instructions='Hi', model='gpt-4o-mini', tools=[get_weather], output='silent'))
"
```
Full multi-framework benchmark:
```bash
cd praisonai-agents
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/performance_benchmark.py --save
python benchmarks/simple_benchmark.py
```
@@ -1,62 +0,0 @@
# Agent Instantiation — Root Cause Analysis
**Date:** 2026-06-17
**Version:** praisonaiagents 1.6.58 (dev)
**Methodology:** 10 warmup + 1000 measured iterations, `time.perf_counter()`
## Summary
Instantiation regressed from **~3.2 μs** (Dec 2025) to **~31 μs** (~10×). After fixes, steady-state is **~16 μs** (~2× historical). PraisonAI is competitive with Agno (~5 μs) on warmed benchmarks using `model=`.
## Before vs After Fixes
| Config | Historical | Pre-fix | Post-fix |
|--------|------------|---------|----------|
| Minimal (`name`, `output="silent"`) | 3.20 μs | 31.36 μs | **16.35 μs** |
| `model="gpt-4o-mini"` | 3.77 μs | 32.19 μs | **16.86 μs** |
| `llm="gpt-4o-mini"` (deprecated) | 3.77 μs | 46.48 μs | **~18 μs** (warn once) |
| LiteLLM `openai/gpt-4o-mini` cold | 7.31 μs | **3.7 s** | **53.63 μs** |
| LiteLLM warmed instantiate | — | 60 μs | **19.83 μs** |
| Import `Agent` | — | 20.8 ms | **29.6 ms** |
## Root Causes (validated)
| ID | Cause | Status | Fix applied |
|----|-------|--------|-------------|
| H1 | Config loader cold start (+9 ms first init) | CONFIRMED | Fast path when no config defaults (`_defaults_has_any_values`) |
| H2 | LoopGuard on every init (~2 μs) | CONFIRMED | Lazy `_ensure_loop_guard()` on first tool call |
| H3 | Approval presets | REJECTED | No change needed |
| H4 | `llm=` deprecation every init (+16 μs) | CONFIRMED | Warn once per process in `warn_deprecated_param` |
| H5 | Init bloat / config resolution (~25 μs) | CONFIRMED | Partially addressed via loader fast path |
| H6 | LiteLLM eager `LLM()` import (+3.7 s) | CONFIRMED | Deferred `_llm_init_params` + lazy `llm_instance` property |
| H7 | `ExecutionConfig` deprecation every init | CONFIRMED | Fixed stack walk; internal SDK path cached |
## cProfile — Post-fix (500 warmed `model=` inits)
| Function | Time (ms) | Per call |
|----------|-----------|----------|
| `Agent.__init__` | 8 | ~16 μs |
| `apply_config_defaults` | 1 | ~2 μs |
| `ExecutionConfig.__post_init__` | 1 | ~2 μs (first call only walks stack) |
| `_warnings.warn` | **0** | removed from hot path |
## Fixes implemented
1. **`praisonaiagents/config/feature_configs.py`** — Fix `ExecutionConfig.__post_init__` deprecation guard; cache internal SDK detection.
2. **`praisonaiagents/utils/deprecation.py`** — Warn once per process for deprecated params.
3. **`praisonaiagents/config/loader.py`** — Skip `apply_config_defaults` lookups when config has no defaults.
4. **`praisonaiagents/agent/agent.py`** — Lazy LoopGuard, lazy LLM instance, deferred LiteLLM params.
5. **`benchmarks/simple_benchmark.py`** — Use `model=`, add 10-iteration warmup.
6. **`benchmarks/performance_benchmark.py`** — Use `model=` instead of deprecated `llm=`.
## Remaining gap (~16 μs vs ~3 μs)
The residual ~13 μs is primarily **`Agent.__init__` size** (~1200 lines of consolidated config resolution added since v0.1.5). Further optimisation requires a dedicated slim fast-path for `Agent(name=..., output="silent")` without full config object churn.
## How to reproduce
```bash
cd praisonai-agents
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/simple_benchmark.py
PYTHONWARNINGS='ignore::DeprecationWarning' python benchmarks/performance_benchmark.py
```
Loaded 100 of 682 files, more files were not shown because too many files have changed in this diff. Show more