Compare commits

...
6 Commits
Author SHA1 Message Date
Mervin Praison 98874da689 Update PraisonAI to version 2.2.39 and PraisonAI Agents to version 0.0.112 across Dockerfiles and project files for consistency and improved functionality. 2025-07-08 19:37:21 +01:00
Mervin Praison 6f019de05c Merge pull request #761 from MervinPraison/claude/issue-752-20250708_181532
refactor: Phase 2 - Extract shared _build_messages helper
2025-07-08 19:31:50 +01:00
Mervin Praison a03d288e01 Rename test_phase2_refactor.py to src/praisonai/tests/test_phase2_refactor.py 2025-07-08 19:31:19 +01:00
claude[bot]andMervin Praison 03c23021ab refactor: extract shared _build_messages helper to reduce code duplication
Phase 2 implementation to reduce code duplication in LLM class:
- Added _build_messages() helper method to consolidate message building logic
- Updated all 4 methods to use the shared helper:
  - get_response() (sync)
  - response() (sync)
  - get_response_async() (async)
  - aresponse() (async)
- Removed ~40 lines of duplicated code
- No breaking changes - all functionality preserved
- Added test script to verify backward compatibility

Co-authored-by: Mervin Praison <[email protected]>
2025-07-08 18:26:45 +00:00
Mervin Praison 0178ec807e Merge pull request #731 from Dhivya-Bharathy/add-talk2find_voice_agent_demo
Add Talk2find Voice Agent Notebook
2025-07-08 19:25:03 +01:00
Dhivya-Bharathy 9757e3dea7 Add talk2find_voice_agent_demo notebook 2025-07-04 11:18:21 +00:00
16 changed files with 690 additions and 114 deletions

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
flask \
"praisonai>=2.2.38" \
"praisonai>=2.2.39" \
"praisonai[api]" \
gunicorn \
markdown
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.38" \
"praisonai>=2.2.39" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.38" \
"praisonai>=2.2.39" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.38" \
"praisonai>=2.2.39" \
"praisonai[ui]" \
"praisonai[crewai]"
+2 -2
View File
@@ -121,7 +121,7 @@ healthcheck:
## 📦 Package Versions
All Docker images use consistent, up-to-date versions:
- PraisonAI: `>=2.2.38`
- PraisonAI: `>=2.2.39`
- PraisonAI Agents: `>=0.0.92`
- Python: `3.11-slim`
@@ -218,7 +218,7 @@ docker-compose up -d
### Version Pinning
To use specific versions, update the Dockerfile:
```dockerfile
RUN pip install "praisonai==2.2.38" "praisonaiagents==0.0.92"
RUN pip install "praisonai==2.2.39" "praisonaiagents==0.0.92"
```
## 🌐 Production Deployment
@@ -0,0 +1,401 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "W5cYeCT1jIBS",
"metadata": {
"id": "W5cYeCT1jIBS"
},
"source": [
"# 🗣️ Talk2Find: Voice-Driven Knowledge Agent (with Synthetic Audio)"
]
},
{
"cell_type": "markdown",
"id": "uTxgPYvKjJty",
"metadata": {
"id": "uTxgPYvKjJty"
},
"source": [
"This notebook demonstrates a voice-driven knowledge agent using PraisonAI Agents. It generates a valid WAV file from text using gTTS and pydub, transcribes the audio with SpeechRecognition, and then uses PraisonAI Agents to answer the spoken question. This workflow is robust, reproducible, and does not require manual audio uploads."
]
},
{
"cell_type": "markdown",
"id": "CJdIonVMjgU7",
"metadata": {
"id": "CJdIonVMjgU7"
},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Dhivya-Bharathy/PraisonAI/blob/main/examples/cookbooks/talk2find_voice_agent_demo.ipynb)\n"
]
},
{
"cell_type": "markdown",
"id": "wjVE-KWwjQxP",
"metadata": {
"id": "wjVE-KWwjQxP"
},
"source": [
"# Dependencies"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "RFG1v766idiL",
"metadata": {
"id": "RFG1v766idiL"
},
"outputs": [],
"source": [
"!pip install praisonaiagents openai SpeechRecognition pydub gTTS --quiet"
]
},
{
"cell_type": "markdown",
"id": "0IYiVLgsjTJf",
"metadata": {
"id": "0IYiVLgsjTJf"
},
"source": [
"# Set API Key"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3AVzmY5FilJ9",
"metadata": {
"id": "3AVzmY5FilJ9"
},
"outputs": [],
"source": [
"import os\n",
"os.environ[\"OPENAI_API_KEY\"] = \"Enter your api key\" # <-- Replace with your actual OpenAI API key"
]
},
{
"cell_type": "markdown",
"id": "SfHMkOTHjVMa",
"metadata": {
"id": "SfHMkOTHjVMa"
},
"source": [
"# Tools"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "Hj28HfsNip9V",
"metadata": {
"id": "Hj28HfsNip9V"
},
"outputs": [],
"source": [
"from praisonaiagents import Agent\n",
"import speech_recognition as sr\n",
"from gtts import gTTS\n",
"from pydub import AudioSegment\n",
"from IPython.display import Audio"
]
},
{
"cell_type": "markdown",
"id": "8BIPQ6fzjXu4",
"metadata": {
"id": "8BIPQ6fzjXu4"
},
"source": [
"# Generate a Valid WAV File from Text"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "rdcn-AiYisHb",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 92
},
"id": "rdcn-AiYisHb",
"outputId": "2daf8cb4-93f6-4e02-ab2b-3e449d7deee0"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ WAV file 'question.wav' has been created.\n"
]
},
{
"data": {
"text/html": [
"\n",
" <audio controls=\"controls\" >\n",
" <source src=\"data:audio/x-wav;base64,UklGRiRfAQBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAAZGF0YQBfAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAEAAAAAAAEAAQAAAAAAAAABAAEAAQAAAAEAAQABAAAA//8AAP//AAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAA//8BAP////8AAAAAAAAAAAAA//8AAAAAAAAAAAAAAAD//wEAAAAAAP//AAAAAAAA//8AAAAA//8AAP//AAAAAAAAAAAAAAAA/////wAAAAAAAAAA//8AAAAAAQABAAAAAAAAAAEAAQAAAAEAAQABAP//AQAAAP//AQAAAAEAAQAAAAAAAQABAAEAAAACAAIAAQABAAEAAQABAAEAAQABAAEAAgABAAEAAAABAAEAAQABAAAAAAD/////AAD//wAAAAAAAP///v8AAAAA/////wEA//////////8AAAAAAAD//wAA/v8AAAAA//8AAAAAAAAAAAAAAAAAAP//AQAAAAAAAAAAAAAAAAAAAAgABwD+/wEAAQD7//7/AgACAAEAAgAEAAQAAwABAAEAAAABAAEAAQD//wAA/v/8//3//f/+//7//v/9//3//v///////f/9/wAA/////wAAAQAAAP7////9/wEAAAD///3//P/8//7////+/wEA///+//7//v/+//z//v8AAP///v/9//z//v/8//r//f/9//v/+v/5//v/+//7//z/+v/7//r//P/6//n/+//6//z/+f/4//n/+v/8//3///////z/+///////AAAAAAIA///+//////8CAP7///8BAAMAAAD9//7/AQABAAEA///8/////P8AAP3/+//+//v//P/9//z/9f/4//3/+f/9//r/9f/5//j/9f/5//3/+P/7//b/9//2/Line truncated
" Your browser does not support the audio element.\n",
" </audio>\n",
" "
],
"text/plain": [
"<IPython.lib.display.Audio object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Step 1: Define the text you want to convert to speech\n",
"text = \"What time is the meeting?\"\n",
"\n",
"# Step 2: Generate speech from text and save as MP3\n",
"tts = gTTS(text=text, lang='en')\n",
"tts.save(\"question.mp3\")\n",
"\n",
"# Step 3: Convert MP3 to WAV using pydub\n",
"sound = AudioSegment.from_mp3(\"question.mp3\")\n",
"sound.export(\"question.wav\", format=\"wav\")\n",
"\n",
"print(\"✅ WAV file 'question.wav' has been created.\")\n",
"\n",
"# Optional: Play the audio (works in Jupyter/Colab)\n",
"try:\n",
" display(Audio(\"question.wav\"))\n",
"except:\n",
" print(\"You can now play 'question.wav' manually.\")"
]
},
{
"cell_type": "markdown",
"id": "AuA3Cj7mjamh",
"metadata": {
"id": "AuA3Cj7mjamh"
},
"source": [
"# YAML Prompt (Role, Goal, Instructions)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "Au5fg-6UiwDP",
"metadata": {
"id": "Au5fg-6UiwDP"
},
"outputs": [],
"source": [
"ROLE = \"Voice-driven knowledge assistant. Expert in understanding spoken queries and providing accurate, concise answers.\"\n",
"GOAL = \"Transcribe the user's voice input and answer their question or find relevant information.\"\n",
"INSTRUCTIONS = \"Given a transcribed user question, provide a clear, factual, and helpful answer. If the question is unclear, ask for clarification.\""
]
},
{
"cell_type": "markdown",
"id": "yz4P9xvpjc4b",
"metadata": {
"id": "yz4P9xvpjc4b"
},
"source": [
"# Main (Transcription + Agent)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "ubYuWOYCiyCx",
"metadata": {
"id": "ubYuWOYCiyCx"
},
"outputs": [],
"source": [
"def transcribe_audio(audio_file):\n",
" recognizer = sr.Recognizer()\n",
" with sr.AudioFile(audio_file) as source:\n",
" audio = recognizer.record(source)\n",
" try:\n",
" return recognizer.recognize_google(audio)\n",
" except Exception as e:\n",
" print(\"Transcription error:\", e)\n",
" return None\n",
"\n",
"def talk2find_agent(audio_file):\n",
" query = transcribe_audio(audio_file)\n",
" if not query:\n",
" return {\"error\": \"Could not transcribe audio.\"}\n",
" print(\"Transcribed text:\", query)\n",
" agent = Agent(role=ROLE, goal=GOAL, instructions=INSTRUCTIONS)\n",
" return agent.start(query)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "VDMMruvCiz8r",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 314,
"referenced_widgets": [
"b72c0f15f2e24a678edcf696b7cc13a3",
"99867a03f5474e4c9f22ec5ea948a6ed"
]
},
"id": "VDMMruvCiz8r",
"outputId": "14354595-25ef-4600-c3c8-8b1ff9c4aba0"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Transcribed text: what time is the meeting\n"
]
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">╭─ </span><span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8; font-weight: bold\">Agent Info</span><span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\"> ────────────────────────────────────────────────────────────────────────────────────────────────────╮</span>\n",
"<span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span> <span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span>\n",
"<span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span> <span style=\"color: #ff9b9b; text-decoration-color: #ff9b9b; font-weight: bold\">👤 Agent:</span> <span style=\"color: #ffe5e5; text-decoration-color: #ffe5e5\">Agent</span> <span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span>\n",
"<span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span> <span style=\"color: #b4b4b3; text-decoration-color: #b4b4b3; font-weight: bold\">Role:</span> <span style=\"color: #ffe5e5; text-decoration-color: #ffe5e5\">Voice-driven knowledge assistant. Expert in understanding spoken queries and providing accurate, </span> <span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span>\n",
"<span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span> <span style=\"color: #ffe5e5; text-decoration-color: #ffe5e5\">concise answers.</span> <span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span>\n",
"<span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span> <span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">│</span>\n",
"<span style=\"color: #d2e3c8; text-decoration-color: #d2e3c8\">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[38;2;210;227;200m╭─\u001b[0m\u001b[38;2;210;227;200m \u001b[0m\u001b[1;38;2;210;227;200mAgent Info\u001b[0m\u001b[38;2;210;227;200m \u001b[0m\u001b[38;2;210;227;200m───────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[38;2;210;227;200m─╮\u001b[0m\n",
"\u001b[38;2;210;227;200m│\u001b[0m \u001b[38;2;210;227;200m│\u001b[0m\n",
"\u001b[38;2;210;227;200m│\u001b[0m \u001b[1;38;2;255;155;155m👤 Agent:\u001b[0m \u001b[38;2;255;229;229mAgent\u001b[0m \u001b[38;2;210;227;200m│\u001b[0m\n",
"\u001b[38;2;210;227;200m│\u001b[0m \u001b[1;38;2;180;180;179mRole:\u001b[0m \u001b[38;2;255;229;229mVoice-driven knowledge assistant. Expert in understanding spoken queries and providing accurate, \u001b[0m \u001b[38;2;210;227;200m│\u001b[0m\n",
"\u001b[38;2;210;227;200m│\u001b[0m \u001b[38;2;255;229;229mconcise answers.\u001b[0m \u001b[38;2;210;227;200m│\u001b[0m\n",
"\u001b[38;2;210;227;200m│\u001b[0m \u001b[38;2;210;227;200m│\u001b[0m\n",
"\u001b[38;2;210;227;200m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b72c0f15f2e24a678edcf696b7cc13a3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Output()"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"></pre>\n"
],
"text/plain": []
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">\n",
"</pre>\n"
],
"text/plain": [
"\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">Response generated in 2.3s</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[2mResponse generated in 2.3s\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #008080; text-decoration-color: #008080\">╭───────────────────────────────────────────────────── Task ──────────────────────────────────────────────────────╮</span>\n",
"<span style=\"color: #008080; text-decoration-color: #008080\">│</span> what time is the meeting <span style=\"color: #008080; text-decoration-color: #008080\">│</span>\n",
"<span style=\"color: #008080; text-decoration-color: #008080\">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[36m╭─\u001b[0m\u001b[36m────────────────────────────────────────────────────\u001b[0m\u001b[36m Task \u001b[0m\u001b[36m─────────────────────────────────────────────────────\u001b[0m\u001b[36m─╮\u001b[0m\n",
"\u001b[36m│\u001b[0m what time is the meeting \u001b[36m│\u001b[0m\n",
"\u001b[36m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #008080; text-decoration-color: #008080\">╭─────────────────────────────────────────────────── Response ────────────────────────────────────────────────────╮</span>\n",
"<span style=\"color: #008080; text-decoration-color: #008080\">│</span> I'm sorry, I don't have access to your schedule or specific meeting details. Could you provide more information <span style=\"color: #008080; text-decoration-color: #008080\">│</span>\n",
"<span style=\"color: #008080; text-decoration-color: #008080\">│</span> about which meeting you're referring to? <span style=\"color: #008080; text-decoration-color: #008080\">│</span>\n",
"<span style=\"color: #008080; text-decoration-color: #008080\">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[36m╭─\u001b[0m\u001b[36m──────────────────────────────────────────────────\u001b[0m\u001b[36m Response \u001b[0m\u001b[36m───────────────────────────────────────────────────\u001b[0m\u001b[36m─╮\u001b[0m\n",
"\u001b[36m│\u001b[0m I'm sorry, I don't have access to your schedule or specific meeting details. Could you provide more information \u001b[36m│\u001b[0m\n",
"\u001b[36m│\u001b[0m about which meeting you're referring to? \u001b[36m│\u001b[0m\n",
"\u001b[36m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"I'm sorry, I don't have access to your schedule or specific meeting details. Could you provide more information about which meeting you're referring to?\n"
]
}
],
"source": [
"audio_path = \"question.wav\"\n",
"result = talk2find_agent(audio_path)\n",
"print(result)"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.x"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,45 @@
from praisonaiagents import Agent
# Detailed LLM configuration
llm_config = {
"model": "gemini/gemini-1.5-flash-latest", # Model name without provider prefix
# Core settings
"temperature": 0.7, # Controls randomness (like temperature)
"timeout": 30, # Timeout in seconds
"top_p": 0.9, # Nucleus sampling parameter
"max_tokens": 1000, # Max tokens in response
# Advanced parameters
"presence_penalty": 0.1, # Penalize repetition of topics (-2.0 to 2.0)
"frequency_penalty": 0.1, # Penalize token repetition (-2.0 to 2.0)
# API settings (optional)
"api_key": None, # Your API key (or use environment variable)
"base_url": None, # Custom API endpoint if needed
# Response formatting
"response_format": { # Force specific response format
"type": "text" # Options: "text", "json_object"
},
# Additional controls
"seed": 42, # For reproducible responses
"stop_phrases": ["##", "END"], # Custom stop sequences
}
agent = Agent(
instructions="You are a helpful Assistant specialized in scientific explanations. "
"Provide clear, accurate, and engaging responses.",
llm=llm_config, # Pass the detailed configuration
verbose=True, # Enable detailed output
markdown=True, # Format responses in markdown
self_reflect=True, # Enable self-reflection
max_reflect=3, # Maximum reflection iterations
min_reflect=1 # Minimum reflection iterations
)
# Test the agent
response = agent.start("Why is the sky blue? Please explain in simple terms.")
print(response)
-10
View File
@@ -1,10 +0,0 @@
from praisonaiagents import Agent
from praisonaiagents.tools import internet_search
agent = Agent(
instructions="You are a helpful assistant",
tools=[internet_search],
llm="gemini/gemini-1.5-flash-8b"
)
agent.start("What is Praison AI?")
+78 -84
View File
@@ -311,6 +311,58 @@ class LLM:
# This ensures we make a single non-streaming call rather than risk
# missing tool calls or making duplicate calls
return False
def _build_messages(self, prompt, system_prompt=None, chat_history=None, output_json=None, output_pydantic=None):
"""Build messages list for LLM completion. Works for both sync and async.
Args:
prompt: The user prompt (str or list)
system_prompt: Optional system prompt
chat_history: Optional list of previous messages
output_json: Optional Pydantic model for JSON output
output_pydantic: Optional Pydantic model for JSON output (alias)
Returns:
tuple: (messages list, original prompt)
"""
messages = []
# Handle system prompt
if system_prompt:
# Append JSON schema if needed
if output_json:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_json.model_json_schema())}"
elif output_pydantic:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_pydantic.model_json_schema())}"
# Skip system messages for legacy o1 models as they don't support them
if not self._needs_system_message_skip():
messages.append({"role": "system", "content": system_prompt})
# Add chat history if provided
if chat_history:
messages.extend(chat_history)
# Handle prompt modifications for JSON output
original_prompt = prompt
if output_json or output_pydantic:
if isinstance(prompt, str):
prompt = prompt + "\nReturn ONLY a valid JSON object. No other text or explanation."
elif isinstance(prompt, list):
# Create a copy to avoid modifying the original
prompt = prompt.copy()
for item in prompt:
if item.get("type") == "text":
item["text"] = item["text"] + "\nReturn ONLY a valid JSON object. No other text or explanation."
break
# Add prompt to messages
if isinstance(prompt, list):
messages.append({"role": "user", "content": prompt})
else:
messages.append({"role": "user", "content": prompt})
return messages, original_prompt
def get_response(
self,
@@ -421,36 +473,14 @@ class LLM:
if not formatted_tools:
formatted_tools = None
# Build messages list
messages = []
if system_prompt:
if output_json:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_json.model_json_schema())}"
elif output_pydantic:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_pydantic.model_json_schema())}"
# Skip system messages for legacy o1 models as they don't support them
if not self._needs_system_message_skip():
messages.append({"role": "system", "content": system_prompt})
if chat_history:
messages.extend(chat_history)
# Handle prompt modifications for JSON output
original_prompt = prompt
if output_json or output_pydantic:
if isinstance(prompt, str):
prompt += "\nReturn ONLY a valid JSON object. No other text or explanation."
elif isinstance(prompt, list):
for item in prompt:
if item["type"] == "text":
item["text"] += "\nReturn ONLY a valid JSON object. No other text or explanation."
break
# Add prompt to messages
if isinstance(prompt, list):
messages.append({"role": "user", "content": prompt})
else:
messages.append({"role": "user", "content": prompt})
# Build messages list using shared helper
messages, original_prompt = self._build_messages(
prompt=prompt,
system_prompt=system_prompt,
chat_history=chat_history,
output_json=output_json,
output_pydantic=output_pydantic
)
start_time = time.time()
reflection_count = 0
@@ -1160,36 +1190,14 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
reasoning_steps = kwargs.pop('reasoning_steps', self.reasoning_steps)
litellm.set_verbose = False
# Build messages list
messages = []
if system_prompt:
if output_json:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_json.model_json_schema())}"
elif output_pydantic:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {json.dumps(output_pydantic.model_json_schema())}"
# Skip system messages for legacy o1 models as they don't support them
if not self._needs_system_message_skip():
messages.append({"role": "system", "content": system_prompt})
if chat_history:
messages.extend(chat_history)
# Handle prompt modifications for JSON output
original_prompt = prompt
if output_json or output_pydantic:
if isinstance(prompt, str):
prompt += "\nReturn ONLY a valid JSON object. No other text or explanation."
elif isinstance(prompt, list):
for item in prompt:
if item["type"] == "text":
item["text"] += "\nReturn ONLY a valid JSON object. No other text or explanation."
break
# Add prompt to messages
if isinstance(prompt, list):
messages.append({"role": "user", "content": prompt})
else:
messages.append({"role": "user", "content": prompt})
# Build messages list using shared helper
messages, original_prompt = self._build_messages(
prompt=prompt,
system_prompt=system_prompt,
chat_history=chat_history,
output_json=output_json,
output_pydantic=output_pydantic
)
start_time = time.time()
reflection_count = 0
@@ -1901,18 +1909,11 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
}
logger.debug(f"Response method configuration: {json.dumps(debug_info, indent=2, default=str)}")
# Build messages list
messages = []
if system_prompt:
# Skip system messages for legacy o1 models as they don't support them
if not self._needs_system_message_skip():
messages.append({"role": "system", "content": system_prompt})
# Add prompt to messages
if isinstance(prompt, list):
messages.append({"role": "user", "content": prompt})
else:
messages.append({"role": "user", "content": prompt})
# Build messages list using shared helper (simplified version without JSON output)
messages, _ = self._build_messages(
prompt=prompt,
system_prompt=system_prompt
)
# Get response from LiteLLM
if stream:
@@ -2009,18 +2010,11 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
}
logger.debug(f"Async response method configuration: {json.dumps(debug_info, indent=2, default=str)}")
# Build messages list
messages = []
if system_prompt:
# Skip system messages for legacy o1 models as they don't support them
if not self._needs_system_message_skip():
messages.append({"role": "system", "content": system_prompt})
# Add prompt to messages
if isinstance(prompt, list):
messages.append({"role": "user", "content": prompt})
else:
messages.append({"role": "user", "content": prompt})
# Build messages list using shared helper (simplified version without JSON output)
messages, _ = self._build_messages(
prompt=prompt,
system_prompt=system_prompt
)
# Get response from LiteLLM
if stream:
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.111"
version = "0.0.112"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
requires-python = ">=3.10"
authors = [
+1 -1
View File
@@ -2375,7 +2375,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.111"
version = "0.0.112"
source = { editable = "." }
dependencies = [
{ name = "mcp" },
+2 -2
View File
@@ -3,8 +3,8 @@ class Praisonai < Formula
desc "AI tools for various AI applications"
homepage "https://github.com/MervinPraison/PraisonAI"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.38.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.38.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.39.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.39.tar.gz | shasum -a 256`.split.first
license "MIT"
depends_on "[email protected]"
+1 -1
View File
@@ -57,7 +57,7 @@ class CloudDeployer:
file.write("FROM python:3.11-slim\n")
file.write("WORKDIR /app\n")
file.write("COPY . .\n")
file.write("RUN pip install flask praisonai==2.2.38 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.2.39 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.2.38"
version = "2.2.39"
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
readme = "README.md"
license = ""
@@ -12,7 +12,7 @@ dependencies = [
"rich>=13.7",
"markdown>=3.5",
"pyparsing>=3.0.0",
"praisonaiagents>=0.0.111",
"praisonaiagents>=0.0.112",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -95,7 +95,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
[tool.poetry]
name = "PraisonAI"
version = "2.2.38"
version = "2.2.39"
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
authors = ["Mervin Praison"]
license = ""
@@ -113,7 +113,7 @@ python = ">=3.10,<3.13"
rich = ">=13.7"
markdown = ">=3.5"
pyparsing = ">=3.0.0"
praisonaiagents = ">=0.0.111"
praisonaiagents = ">=0.0.112"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python3
"""Test script to verify Phase 2 refactoring maintains backward compatibility"""
import asyncio
from src.praisonai_agents.praisonaiagents.llm.llm import LLM
def test_sync_methods():
"""Test synchronous methods still work correctly"""
print("Testing synchronous methods...")
# Test basic response
llm = LLM(model="gpt-3.5-turbo")
# Test simple prompt
print("1. Testing simple prompt...")
response = llm.response(
prompt="What is 2+2?",
verbose=False
)
print(f" Response: {response}")
assert response, "Should get a response"
# Test with system prompt
print("2. Testing with system prompt...")
response = llm.response(
prompt="Hello",
system_prompt="You are a helpful assistant. Always respond politely.",
verbose=False
)
print(f" Response: {response[:50]}...")
assert response, "Should get a response with system prompt"
# Test with chat history
print("3. Testing with chat history...")
chat_history = [
{"role": "user", "content": "My name is Alice"},
{"role": "assistant", "content": "Nice to meet you, Alice!"}
]
response = llm.get_response(
prompt="What's my name?",
chat_history=chat_history,
verbose=False
)
print(f" Response: {response}")
assert response, "Should get a response with chat history"
print("✅ Synchronous methods working correctly\n")
async def test_async_methods():
"""Test asynchronous methods still work correctly"""
print("Testing asynchronous methods...")
# Test basic async response
llm = LLM(model="gpt-3.5-turbo")
# Test simple prompt
print("1. Testing async simple prompt...")
response = await llm.aresponse(
prompt="What is 3+3?",
verbose=False
)
print(f" Response: {response}")
assert response, "Should get an async response"
# Test with system prompt
print("2. Testing async with system prompt...")
response = await llm.aresponse(
prompt="Goodbye",
system_prompt="You are a helpful assistant. Always respond politely.",
verbose=False
)
print(f" Response: {response[:50]}...")
assert response, "Should get an async response with system prompt"
# Test with chat history
print("3. Testing async with chat history...")
chat_history = [
{"role": "user", "content": "My favorite color is blue"},
{"role": "assistant", "content": "Blue is a nice color!"}
]
response = await llm.get_response_async(
prompt="What's my favorite color?",
chat_history=chat_history,
verbose=False
)
print(f" Response: {response}")
assert response, "Should get an async response with chat history"
print("✅ Asynchronous methods working correctly\n")
def test_edge_cases():
"""Test edge cases and special scenarios"""
print("Testing edge cases...")
llm = LLM(model="gpt-3.5-turbo")
# Test with list prompt (multimodal-style)
print("1. Testing list-style prompt...")
prompt = [{"type": "text", "text": "What is 4+4?"}]
response = llm.response(
prompt=prompt,
verbose=False
)
print(f" Response: {response}")
assert response, "Should handle list-style prompts"
# Test with legacy o1 model (system message should be skipped)
print("2. Testing legacy o1 model handling...")
llm_o1 = LLM(model="o1-mini")
response = llm_o1.response(
prompt="Hello",
system_prompt="This should be skipped",
verbose=False
)
print(f" Response: {response[:50]}...")
assert response, "Should handle legacy o1 models"
print("✅ Edge cases handled correctly\n")
def main():
"""Run all tests"""
print("=== Phase 2 Refactoring Verification ===\n")
try:
# Test sync methods
test_sync_methods()
# Test async methods
asyncio.run(test_async_methods())
# Test edge cases
test_edge_cases()
print("✅ ALL TESTS PASSED! The refactoring maintains backward compatibility.")
print("\nSummary:")
print("- Message building logic successfully extracted to _build_messages()")
print("- All 4 methods (get_response, response, get_response_async, aresponse) updated")
print("- ~40 lines of duplicated code removed")
print("- No breaking changes detected")
except Exception as e:
print(f"❌ TEST FAILED: {str(e)}")
raise
if __name__ == "__main__":
main()
+5 -5
View File
@@ -3931,7 +3931,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.2.38"
version = "2.2.39"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -4073,7 +4073,7 @@ requires-dist = [
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.15" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.15" },
{ name = "praisonaiagents", specifier = ">=0.0.111" },
{ name = "praisonaiagents", specifier = ">=0.0.112" },
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
@@ -4130,7 +4130,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.111"
version = "0.0.112"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mcp" },
@@ -4139,9 +4139,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3c/8f/815e2074fb03d24dd0d89adb28c6150f6112ff23af36bf021f387176cc0c/praisonaiagents-0.0.111.tar.gz", hash = "sha256:17d4fec346d61a9830410a4f23a052da44b3a9978a7304b9ca9ca35cf747ceaa", size = 166895 }
sdist = { url = "https://files.pythonhosted.org/packages/b8/f0/f03a2e64ee0f78824a78275b8a2350f2086a37dd75601b43e740f7a55c3b/praisonaiagents-0.0.112.tar.gz", hash = "sha256:504efc8a427b80a2e4865831c651c834239fb33b7766792e2f3d1157518ac09a", size = 166924 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/92/a6/dbbe85f62411b784a79055be924fa3d6c3481ed7659ce2ec3ab3bf315254/praisonaiagents-0.0.111-py3-none-any.whl", hash = "sha256:1a8f884c48eac03eff4ceec11fec4625f01decaaa28636a082cd61797cef984b", size = 186353 },
{ url = "https://files.pythonhosted.org/packages/f8/da/83271d3671e468f855e225c9c23fe19b7a5be654ab8f8b0fc305bf1b10f1/praisonaiagents-0.0.112-py3-none-any.whl", hash = "sha256:b402af5eb7e3ffca702aeae89ca01f8f4c223aa194b743a3d9d1feb71e5efd6e", size = 186397 },
]
[[package]]