-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Develop #598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Develop #598
Changes from all commits
67807ea
0ebbbf3
8c59943
9f1c50e
13503f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| name: Run tests and upload coverage | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main, develop ] | ||
| pull_request: | ||
| branches: [ main, develop ] | ||
|
|
||
| jobs: | ||
| test: | ||
| name: Run tests and collect coverage | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 2 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: 3.11 | ||
|
|
||
| - name: Install UV | ||
| run: | | ||
| curl -LsSf https://astral.sh/uv/install.sh | sh | ||
| echo "$HOME/.local/bin" >> $GITHUB_PATH | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| cd src/praisonai | ||
| uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]" | ||
| uv pip install --system duckduckgo_search | ||
| uv pip install --system pytest pytest-cov pytest-asyncio | ||
| # Install knowledge dependencies from praisonai-agents | ||
| uv pip install --system "praisonaiagents[knowledge]" | ||
|
|
||
| - name: Set environment variables | ||
| run: | | ||
| echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV | ||
| echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV | ||
| echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-4o-mini' }}" >> $GITHUB_ENV | ||
| echo "LOGLEVEL=DEBUG" >> $GITHUB_ENV | ||
| echo "PYTHONPATH=${{ github.workspace }}/src/praisonai-agents:$PYTHONPATH" >> $GITHUB_ENV | ||
|
|
||
| - name: Run tests | ||
| run: | | ||
| cd src/praisonai | ||
| pytest --cov=praisonai --cov-branch --cov-report=xml tests/unit/ | ||
|
|
||
| - name: Upload results to Codecov | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| file: src/praisonai/coverage.xml | ||
| flags: unit-tests | ||
| name: unit-tests-coverage | ||
| fail_ci_if_error: false | ||
| verbose: true |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -788,69 +788,92 @@ def _chat_completion(self, messages, temperature=0.2, tools=None, stream=True, r | |||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| # Use the standard OpenAI client approach | ||||||||||||||||||||||||||||||
| if stream: | ||||||||||||||||||||||||||||||
| # Process as streaming response with formatted tools | ||||||||||||||||||||||||||||||
| final_response = self._process_stream_response( | ||||||||||||||||||||||||||||||
| messages, | ||||||||||||||||||||||||||||||
| temperature, | ||||||||||||||||||||||||||||||
| start_time, | ||||||||||||||||||||||||||||||
| formatted_tools=formatted_tools if formatted_tools else None, | ||||||||||||||||||||||||||||||
| reasoning_steps=reasoning_steps | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| # Process as regular non-streaming response | ||||||||||||||||||||||||||||||
| final_response = client.chat.completions.create( | ||||||||||||||||||||||||||||||
| model=self.llm, | ||||||||||||||||||||||||||||||
| messages=messages, | ||||||||||||||||||||||||||||||
| temperature=temperature, | ||||||||||||||||||||||||||||||
| tools=formatted_tools if formatted_tools else None, | ||||||||||||||||||||||||||||||
| stream=False | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| tool_calls = getattr(final_response.choices[0].message, 'tool_calls', None) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if tool_calls: | ||||||||||||||||||||||||||||||
| messages.append({ | ||||||||||||||||||||||||||||||
| "role": "assistant", | ||||||||||||||||||||||||||||||
| "content": final_response.choices[0].message.content, | ||||||||||||||||||||||||||||||
| "tool_calls": tool_calls | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
| # Continue tool execution loop until no more tool calls are needed | ||||||||||||||||||||||||||||||
| max_iterations = 10 # Prevent infinite loops | ||||||||||||||||||||||||||||||
| iteration_count = 0 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| while iteration_count < max_iterations: | ||||||||||||||||||||||||||||||
| if stream: | ||||||||||||||||||||||||||||||
| # Process as streaming response with formatted tools | ||||||||||||||||||||||||||||||
| final_response = self._process_stream_response( | ||||||||||||||||||||||||||||||
| messages, | ||||||||||||||||||||||||||||||
| temperature, | ||||||||||||||||||||||||||||||
| start_time, | ||||||||||||||||||||||||||||||
| formatted_tools=formatted_tools if formatted_tools else None, | ||||||||||||||||||||||||||||||
| reasoning_steps=reasoning_steps | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| # Process as regular non-streaming response | ||||||||||||||||||||||||||||||
| final_response = client.chat.completions.create( | ||||||||||||||||||||||||||||||
| model=self.llm, | ||||||||||||||||||||||||||||||
| messages=messages, | ||||||||||||||||||||||||||||||
| temperature=temperature, | ||||||||||||||||||||||||||||||
| tools=formatted_tools if formatted_tools else None, | ||||||||||||||||||||||||||||||
| stream=False | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| for tool_call in tool_calls: | ||||||||||||||||||||||||||||||
| function_name = tool_call.function.name | ||||||||||||||||||||||||||||||
| arguments = json.loads(tool_call.function.arguments) | ||||||||||||||||||||||||||||||
| tool_calls = getattr(final_response.choices[0].message, 'tool_calls', None) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if self.verbose: | ||||||||||||||||||||||||||||||
| display_tool_call(f"Agent {self.name} is calling function '{function_name}' with arguments: {arguments}") | ||||||||||||||||||||||||||||||
| if tool_calls: | ||||||||||||||||||||||||||||||
| messages.append({ | ||||||||||||||||||||||||||||||
| "role": "assistant", | ||||||||||||||||||||||||||||||
| "content": final_response.choices[0].message.content, | ||||||||||||||||||||||||||||||
| "tool_calls": tool_calls | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| tool_result = self.execute_tool(function_name, arguments) | ||||||||||||||||||||||||||||||
| results_str = json.dumps(tool_result) if tool_result else "Function returned an empty output" | ||||||||||||||||||||||||||||||
| for tool_call in tool_calls: | ||||||||||||||||||||||||||||||
| function_name = tool_call.function.name | ||||||||||||||||||||||||||||||
| arguments = json.loads(tool_call.function.arguments) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if self.verbose: | ||||||||||||||||||||||||||||||
| display_tool_call(f"Function '{function_name}' returned: {results_str}") | ||||||||||||||||||||||||||||||
| if self.verbose: | ||||||||||||||||||||||||||||||
| display_tool_call(f"Agent {self.name} is calling function '{function_name}' with arguments: {arguments}") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| messages.append({ | ||||||||||||||||||||||||||||||
| "role": "tool", | ||||||||||||||||||||||||||||||
| "tool_call_id": tool_call.id, | ||||||||||||||||||||||||||||||
| "content": results_str | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
| tool_result = self.execute_tool(function_name, arguments) | ||||||||||||||||||||||||||||||
| results_str = json.dumps(tool_result) if tool_result else "Function returned an empty output" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Get final response after tool calls | ||||||||||||||||||||||||||||||
| if stream: | ||||||||||||||||||||||||||||||
| final_response = self._process_stream_response( | ||||||||||||||||||||||||||||||
| messages, | ||||||||||||||||||||||||||||||
| temperature, | ||||||||||||||||||||||||||||||
| start_time, | ||||||||||||||||||||||||||||||
| formatted_tools=formatted_tools if formatted_tools else None, | ||||||||||||||||||||||||||||||
| reasoning_steps=reasoning_steps | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| final_response = client.chat.completions.create( | ||||||||||||||||||||||||||||||
| model=self.llm, | ||||||||||||||||||||||||||||||
| messages=messages, | ||||||||||||||||||||||||||||||
| temperature=temperature, | ||||||||||||||||||||||||||||||
| stream=False | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| if self.verbose: | ||||||||||||||||||||||||||||||
| display_tool_call(f"Function '{function_name}' returned: {results_str}") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| messages.append({ | ||||||||||||||||||||||||||||||
| "role": "tool", | ||||||||||||||||||||||||||||||
| "tool_call_id": tool_call.id, | ||||||||||||||||||||||||||||||
| "content": results_str | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Check if we should continue (for tools like sequential thinking) | ||||||||||||||||||||||||||||||
| should_continue = False | ||||||||||||||||||||||||||||||
| for tool_call in tool_calls: | ||||||||||||||||||||||||||||||
| function_name = tool_call.function.name | ||||||||||||||||||||||||||||||
| arguments = json.loads(tool_call.function.arguments) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # For sequential thinking tool, check if nextThoughtNeeded is True | ||||||||||||||||||||||||||||||
| if function_name == "sequentialthinking" and arguments.get("nextThoughtNeeded", False): | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The string
Suggested change
|
||||||||||||||||||||||||||||||
| should_continue = True | ||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if not should_continue: | ||||||||||||||||||||||||||||||
| # Get final response after tool calls | ||||||||||||||||||||||||||||||
| if stream: | ||||||||||||||||||||||||||||||
| final_response = self._process_stream_response( | ||||||||||||||||||||||||||||||
| messages, | ||||||||||||||||||||||||||||||
| temperature, | ||||||||||||||||||||||||||||||
| start_time, | ||||||||||||||||||||||||||||||
| formatted_tools=formatted_tools if formatted_tools else None, | ||||||||||||||||||||||||||||||
| reasoning_steps=reasoning_steps | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
Comment on lines
+857
to
+863
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When
Suggested change
|
||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| final_response = client.chat.completions.create( | ||||||||||||||||||||||||||||||
| model=self.llm, | ||||||||||||||||||||||||||||||
| messages=messages, | ||||||||||||||||||||||||||||||
| temperature=temperature, | ||||||||||||||||||||||||||||||
| stream=False | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| iteration_count += 1 | ||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||
| # No tool calls, we're done | ||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return final_response | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the This could lead to the agent returning a stale response that doesn't reflect the outcome of the final tool operations. Consider adding logic before this return statement to make a final LLM call if the loop exited due to # If the loop terminated due to max_iterations and the last LLM call requested tools
# (implying those tools were run and their results are in 'messages' but not yet processed by LLM for a final response).
if iteration_count == max_iterations and getattr(final_response.choices[0].message, 'tool_calls', None):
logging.warning(f"Agent {self.name} reached max_iterations ({max_iterations}). Generating final response based on accumulated tool results.")
if stream:
final_response = self._process_stream_response(
messages,
temperature,
start_time,
formatted_tools=None, # No further tools when max_iterations is hit
reasoning_steps=reasoning_steps
)
else:
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=None, # No further tools when max_iterations is hit
stream=False
)
return final_response |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
max_iterationsvalue is currently hardcoded as10. For better maintainability and configurability, would it be beneficial to define this as a class attribute (e.g.,self.MAX_TOOL_ITERATIONS) or make it configurable, perhaps through the agent's initialization parameters? This would make it easier to adjust this limit without modifying the method's core logic.