Compare commits

...

20 Commits

Author SHA1 Message Date
Jiayi Zhang fad4722324 Merge pull request #1127 from KaeyoungLIAN/flow-auto-validate
Pre-commit checks / pre-commit-check (push) Failing after 1s
Add Latex beamer Generate and auto validate
2025-05-14 15:36:53 +08:00
Kaeyoung e2335877a8 precommit 2025-05-14 17:32:31 +10:00
Kaeyoung 0b62ef35b4 Update generatePPT.py 2025-05-14 17:22:23 +10:00
Kaeyoung a27732dca8 update according to comments 2025-05-14 17:02:20 +10:00
Kaeyoung 3f1e24f1d6 change file name 2025-05-08 11:02:48 +10:00
Kaeyoung 6026e7a9c6 Delete excessive modifications. 2025-05-08 10:39:26 +10:00
a5507203 b4161badc6 udpate 2025-05-05 22:54:01 +10:00
a5507203 a6b3571077 update 2025-05-05 18:00:44 +10:00
Kaeyoung abb9eb28a1 Update ppt.py 2025-04-22 14:16:11 +10:00
Kaeyoung 28a4a44420 retrieve the modification of latex_generator and task 2025-04-09 11:04:09 +10:00
Kaeyoung d307ee9a8d Add judge terminate logic and set latex_generator as test state 2025-04-09 10:49:39 +10:00
Kaeyoung a890058eb8 add fixtoolcall 2025-04-05 16:10:40 +11:00
Kaeyoung 2a437a2cd9 change .gitignore to push workspace and revert main.py 2025-03-31 15:32:28 +11:00
Kaeyoung 14c15d96d0 fix some files 2025-03-31 15:28:24 +11:00
Kaeyoung caa7ec8e16 delete some comments. 2025-03-29 19:52:54 +11:00
Kaeyoung d82387244a update server and llm 2025-03-29 19:43:02 +11:00
Kaeyoung 1dc96af1c8 Update toolcall.py 2025-03-29 19:40:37 +11:00
Kaeyoung 8a1c0ce89c delete required properties of validator tool. 2025-03-29 19:39:27 +11:00
Kaeyoung 2b9e0ba673 recover terminate and update valiterminate 2025-03-29 19:37:31 +11:00
Kaeyoung b0dfe3b224 update 2025-03-29 19:28:30 +11:00
17 changed files with 666 additions and 301 deletions
+176 -176
View File
@@ -1,176 +1,176 @@
<p align="center">
<img src="assets/logo.jpg" width="200"/>
</p>
English | [中文](README_zh.md) | [한국어](README_ko.md) | [日本語](README_ja.md)
[![GitHub stars](https://img.shields.io/github/stars/mannaandpoem/OpenManus?style=social)](https://github.com/mannaandpoem/OpenManus/stargazers)
&ensp;
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) &ensp;
[![Discord Follow](https://dcbadge.vercel.app/api/server/DYn29wFk9z?style=flat)](https://discord.gg/DYn29wFk9z)
# 👋 OpenManus
Manus is incredible, but OpenManus can achieve any idea without an *Invite Code* 🛫!
Our team members [@Xinbin Liang](https://github.com/mannaandpoem) and [@Jinyu Xiang](https://github.com/XiangJinyu) (core authors), along with [@Zhaoyang Yu](https://github.com/MoshiQAQ), [@Jiayi Zhang](https://github.com/didiforgithub), and [@Sirui Hong](https://github.com/stellaHSR), we are from [@MetaGPT](https://github.com/geekan/MetaGPT). The prototype is launched within 3 hours and we are keeping building!
It's a simple implementation, so we welcome any suggestions, contributions, and feedback!
Enjoy your own agent with OpenManus!
We're also excited to introduce [OpenManus-RL](https://github.com/OpenManus/OpenManus-RL), an open-source project dedicated to reinforcement learning (RL)- based (such as GRPO) tuning methods for LLM agents, developed collaboratively by researchers from UIUC and OpenManus.
## Project Demo
<video src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" data-canonical-src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" controls="controls" muted="muted" class="d-block rounded-bottom-2 border-top width-fit" style="max-height:640px; min-height: 200px"></video>
## Installation
We provide two installation methods. Method 2 (using uv) is recommended for faster installation and better dependency management.
### Method 1: Using conda
1. Create a new conda environment:
```bash
conda create -n open_manus python=3.12
conda activate open_manus
```
2. Clone the repository:
```bash
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
```
3. Install dependencies:
```bash
pip install -r requirements.txt
```
### Method 2: Using uv (Recommended)
1. Install uv (A fast Python package installer and resolver):
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
2. Clone the repository:
```bash
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
```
3. Create a new virtual environment and activate it:
```bash
uv venv --python 3.12
source .venv/bin/activate # On Unix/macOS
# Or on Windows:
# .venv\Scripts\activate
```
4. Install dependencies:
```bash
uv pip install -r requirements.txt
```
### Browser Automation Tool (Optional)
```bash
playwright install
```
## Configuration
OpenManus requires configuration for the LLM APIs it uses. Follow these steps to set up your configuration:
1. Create a `config.toml` file in the `config` directory (you can copy from the example):
```bash
cp config/config.example.toml config/config.toml
```
2. Edit `config/config.toml` to add your API keys and customize settings:
```toml
# Global LLM configuration
[llm]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
api_key = "sk-..." # Replace with your actual API key
max_tokens = 4096
temperature = 0.0
# Optional configuration for specific LLM models
[llm.vision]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
api_key = "sk-..." # Replace with your actual API key
```
## Quick Start
One line for run OpenManus:
```bash
python main.py
```
Then input your idea via terminal!
For MCP tool version, you can run:
```bash
python run_mcp.py
```
For unstable multi-agent version, you also can run:
```bash
python run_flow.py
```
## How to contribute
We welcome any friendly suggestions and helpful contributions! Just create issues or submit pull requests.
Or contact @mannaandpoem via 📧email: mannaandpoem@gmail.com
**Note**: Before submitting a pull request, please use the pre-commit tool to check your changes. Run `pre-commit run --all-files` to execute the checks.
## Community Group
Join our networking group on Feishu and share your experience with other developers!
<div align="center" style="display: flex; gap: 20px;">
<img src="assets/community_group.jpg" alt="OpenManus 交流群" width="300" />
</div>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=mannaandpoem/OpenManus&type=Date)](https://star-history.com/#mannaandpoem/OpenManus&Date)
## Acknowledgement
Thanks to [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo)
and [browser-use](https://github.com/browser-use/browser-use) for providing basic support for this project!
Additionally, we are grateful to [AAAJ](https://github.com/metauto-ai/agent-as-a-judge), [MetaGPT](https://github.com/geekan/MetaGPT), [OpenHands](https://github.com/All-Hands-AI/OpenHands) and [SWE-agent](https://github.com/SWE-agent/SWE-agent).
OpenManus is built by contributors from MetaGPT. Huge thanks to this agent community!
## Cite
```bibtex
@misc{openmanus2025,
author = {Xinbin Liang and Jinyu Xiang and Zhaoyang Yu and Jiayi Zhang and Sirui Hong},
title = {OpenManus: An open-source framework for building general AI agents},
year = {2025},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/mannaandpoem/OpenManus}},
}
```
<p align="center">
<img src="assets/logo.jpg" width="200"/>
</p>
English | [中文](README_zh.md) | [한국어](README_ko.md) | [日本語](README_ja.md)
[![GitHub stars](https://img.shields.io/github/stars/mannaandpoem/OpenManus?style=social)](https://github.com/mannaandpoem/OpenManus/stargazers)
&ensp;
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) &ensp;
[![Discord Follow](https://dcbadge.vercel.app/api/server/DYn29wFk9z?style=flat)](https://discord.gg/DYn29wFk9z)
# 👋 OpenManus
Manus is incredible, but OpenManus can achieve any idea without an *Invite Code* 🛫!
Our team members [@Xinbin Liang](https://github.com/mannaandpoem) and [@Jinyu Xiang](https://github.com/XiangJinyu) (core authors), along with [@Zhaoyang Yu](https://github.com/MoshiQAQ), [@Jiayi Zhang](https://github.com/didiforgithub), and [@Sirui Hong](https://github.com/stellaHSR), we are from [@MetaGPT](https://github.com/geekan/MetaGPT). The prototype is launched within 3 hours and we are keeping building!
It's a simple implementation, so we welcome any suggestions, contributions, and feedback!
Enjoy your own agent with OpenManus!
We're also excited to introduce [OpenManus-RL](https://github.com/OpenManus/OpenManus-RL), an open-source project dedicated to reinforcement learning (RL)- based (such as GRPO) tuning methods for LLM agents, developed collaboratively by researchers from UIUC and OpenManus.
## Project Demo
<video src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" data-canonical-src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" controls="controls" muted="muted" class="d-block rounded-bottom-2 border-top width-fit" style="max-height:640px; min-height: 200px"></video>
## Installation
We provide two installation methods. Method 2 (using uv) is recommended for faster installation and better dependency management.
### Method 1: Using conda
1. Create a new conda environment:
```bash
conda create -n open_manus python=3.12
conda activate open_manus
```
2. Clone the repository:
```bash
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
```
3. Install dependencies:
```bash
pip install -r requirements.txt
```
### Method 2: Using uv (Recommended)
1. Install uv (A fast Python package installer and resolver):
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
2. Clone the repository:
```bash
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
```
3. Create a new virtual environment and activate it:
```bash
uv venv --python 3.12
source .venv/bin/activate # On Unix/macOS
# Or on Windows:
# .venv\Scripts\activate
```
4. Install dependencies:
```bash
uv pip install -r requirements.txt
```
### Browser Automation Tool (Optional)
```bash
playwright install
```
## Configuration
OpenManus requires configuration for the LLM APIs it uses. Follow these steps to set up your configuration:
1. Create a `config.toml` file in the `config` directory (you can copy from the example):
```bash
cp config/config.example.toml config/config.toml
```
2. Edit `config/config.toml` to add your API keys and customize settings:
```toml
# Global LLM configuration
[llm]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
api_key = "sk-..." # Replace with your actual API key
max_tokens = 4096
temperature = 0.0
# Optional configuration for specific LLM models
[llm.vision]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
api_key = "sk-..." # Replace with your actual API key
```
## Quick Start
One line for run OpenManus:
```bash
python main.py
```
Then input your idea via terminal!
For MCP tool version, you can run:
```bash
python run_mcp.py
```
For unstable multi-agent version, you also can run:
```bash
python run_flow.py
```
## How to contribute
We welcome any friendly suggestions and helpful contributions! Just create issues or submit pull requests.
Or contact @mannaandpoem via 📧email: mannaandpoem@gmail.com
**Note**: Before submitting a pull request, please use the pre-commit tool to check your changes. Run `pre-commit run --all-files` to execute the checks.
## Community Group
Join our networking group on Feishu and share your experience with other developers!
<div align="center" style="display: flex; gap: 20px;">
<img src="assets/community_group.jpg" alt="OpenManus 交流群" width="300" />
</div>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=mannaandpoem/OpenManus&type=Date)](https://star-history.com/#mannaandpoem/OpenManus&Date)
## Acknowledgement
Thanks to [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo)
and [browser-use](https://github.com/browser-use/browser-use) for providing basic support for this project!
Additionally, we are grateful to [AAAJ](https://github.com/metauto-ai/agent-as-a-judge), [MetaGPT](https://github.com/geekan/MetaGPT), [OpenHands](https://github.com/All-Hands-AI/OpenHands) and [SWE-agent](https://github.com/SWE-agent/SWE-agent).
OpenManus is built by contributors from MetaGPT. Huge thanks to this agent community!
## Cite
```bibtex
@misc{openmanus2025,
author = {Xinbin Liang and Jinyu Xiang and Zhaoyang Yu and Jiayi Zhang and Sirui Hong},
title = {OpenManus: An open-source framework for building general AI agents},
year = {2025},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/mannaandpoem/OpenManus}},
}
```
+64
View File
@@ -0,0 +1,64 @@
import json
import uuid
from openai.types.chat.chat_completion_message_tool_call import (
ChatCompletionMessageToolCall,
Function,
)
from app.agent.toolcall import ToolCallAgent
from app.logger import logger
from app.tool import LatexGenerator, ToolCollection, Validator
class PPTAgent(ToolCallAgent):
"""
Agent that executes a fixed sequence of tools, potentially terminating
early if the validator tool indicates completion.
"""
name: str = "fixed_toolcall"
description: str = (
"an agent that executes a fixed sequence of tools in predefined order, "
"potentially terminating early based on validator feedback."
)
available_tools: ToolCollection = ToolCollection(LatexGenerator(), Validator())
max_steps: int = 7
curr_step: int = 0
async def think(self) -> bool:
"""Process current state and decide next actions using tools"""
# pick which of your tools to call
tool_idx = self.curr_step % len(self.available_tools.tools)
tool_meta = self.available_tools.tools[tool_idx]
payload = {
"request": self.memory.messages[0].content,
"history": str(self.memory.messages),
}
arg_str = json.dumps(payload)
# build the Function descriptor
func_call = Function(
name=tool_meta.name,
arguments=arg_str,
)
# generate a proper call ID
call_id = f"call_{uuid.uuid4().hex}"
# wrap it up in a ChatCompletionMessageToolCall
tool_call = ChatCompletionMessageToolCall(
id=call_id, function=func_call, type="function"
)
# assign to self.tool_calls just like the SDK would
self.tool_calls = tool_calls = [tool_call]
logger.info(
f"🛠️ {self.name} selected {len(tool_calls) if tool_calls else 0} tools to use"
)
self.curr_step += 1
return True
+183 -124
View File
@@ -1,15 +1,18 @@
from typing import Dict, List, Literal, Optional, Union
import boto3
import json
import sys
import time
import uuid
from datetime import datetime
import sys
from typing import Dict, List, Literal, Optional
import boto3
# Global variables to track the current tool use ID across function calls
# Tmp solution
CURRENT_TOOLUSE_ID = None
# Class to handle OpenAI-style response formatting
class OpenAIResponse:
def __init__(self, data):
@@ -18,31 +21,37 @@ class OpenAIResponse:
if isinstance(value, dict):
value = OpenAIResponse(value)
elif isinstance(value, list):
value = [OpenAIResponse(item) if isinstance(item, dict) else item for item in value]
value = [
OpenAIResponse(item) if isinstance(item, dict) else item
for item in value
]
setattr(self, key, value)
def model_dump(self, *args, **kwargs):
# Convert object to dict and add timestamp
data = self.__dict__
data['created_at'] = datetime.now().isoformat()
data["created_at"] = datetime.now().isoformat()
return data
# Main client class for interacting with Amazon Bedrock
class BedrockClient:
def __init__(self):
# Initialize Bedrock client, you need to configure AWS env first
try:
self.client = boto3.client('bedrock-runtime')
self.client = boto3.client("bedrock-runtime")
self.chat = Chat(self.client)
except Exception as e:
print(f"Error initializing Bedrock client: {e}")
sys.exit(1)
# Chat interface class
class Chat:
def __init__(self, client):
self.completions = ChatCompletions(client)
# Core class handling chat completions functionality
class ChatCompletions:
def __init__(self, client):
@@ -52,19 +61,23 @@ class ChatCompletions:
# Convert OpenAI function calling format to Bedrock tool format
bedrock_tools = []
for tool in tools:
if tool.get('type') == 'function':
function = tool.get('function', {})
if tool.get("type") == "function":
function = tool.get("function", {})
bedrock_tool = {
"toolSpec": {
"name": function.get('name', ''),
"description": function.get('description', ''),
"name": function.get("name", ""),
"description": function.get("description", ""),
"inputSchema": {
"json": {
"type": "object",
"properties": function.get('parameters', {}).get('properties', {}),
"required": function.get('parameters', {}).get('required', [])
"properties": function.get("parameters", {}).get(
"properties", {}
),
"required": function.get("parameters", {}).get(
"required", []
),
}
}
},
}
}
bedrock_tools.append(bedrock_tool)
@@ -75,41 +88,43 @@ class ChatCompletions:
bedrock_messages = []
system_prompt = []
for message in messages:
if message.get('role') == 'system':
system_prompt = [{"text": message.get('content')}]
elif message.get('role') == 'user':
if message.get("role") == "system":
system_prompt = [{"text": message.get("content")}]
elif message.get("role") == "user":
bedrock_message = {
"role": message.get('role', 'user'),
"content": [{"text": message.get('content')}]
"role": message.get("role", "user"),
"content": [{"text": message.get("content")}],
}
bedrock_messages.append(bedrock_message)
elif message.get('role') == 'assistant':
elif message.get("role") == "assistant":
bedrock_message = {
"role": "assistant",
"content": [{"text": message.get('content')}]
"content": [{"text": message.get("content")}],
}
openai_tool_calls = message.get('tool_calls', [])
openai_tool_calls = message.get("tool_calls", [])
if openai_tool_calls:
bedrock_tool_use = {
"toolUseId": openai_tool_calls[0]['id'],
"name": openai_tool_calls[0]['function']['name'],
"input": json.loads(openai_tool_calls[0]['function']['arguments'])
"toolUseId": openai_tool_calls[0]["id"],
"name": openai_tool_calls[0]["function"]["name"],
"input": json.loads(
openai_tool_calls[0]["function"]["arguments"]
),
}
bedrock_message['content'].append({"toolUse": bedrock_tool_use})
bedrock_message["content"].append({"toolUse": bedrock_tool_use})
global CURRENT_TOOLUSE_ID
CURRENT_TOOLUSE_ID = openai_tool_calls[0]['id']
CURRENT_TOOLUSE_ID = openai_tool_calls[0]["id"]
bedrock_messages.append(bedrock_message)
elif message.get('role') == 'tool':
elif message.get("role") == "tool":
bedrock_message = {
"role": "user",
"content": [
{
"toolResult": {
"toolUseId": CURRENT_TOOLUSE_ID,
"content": [{"text":message.get('content')}]
"content": [{"text": message.get("content")}],
}
}
]
],
}
bedrock_messages.append(bedrock_message)
else:
@@ -119,26 +134,27 @@ class ChatCompletions:
def _convert_bedrock_response_to_openai_format(self, bedrock_response):
# Convert Bedrock response format to OpenAI format
content = ""
if bedrock_response.get('output', {}).get('message', {}).get('content'):
content_array = bedrock_response['output']['message']['content']
content = "".join(item.get('text', '') for item in content_array)
if content == "": content = "."
if bedrock_response.get("output", {}).get("message", {}).get("content"):
content_array = bedrock_response["output"]["message"]["content"]
content = "".join(item.get("text", "") for item in content_array)
if content == "":
content = "."
# Handle tool calls in response
openai_tool_calls = []
if bedrock_response.get('output', {}).get('message', {}).get('content'):
for content_item in bedrock_response['output']['message']['content']:
if content_item.get('toolUse'):
bedrock_tool_use = content_item['toolUse']
if bedrock_response.get("output", {}).get("message", {}).get("content"):
for content_item in bedrock_response["output"]["message"]["content"]:
if content_item.get("toolUse"):
bedrock_tool_use = content_item["toolUse"]
global CURRENT_TOOLUSE_ID
CURRENT_TOOLUSE_ID = bedrock_tool_use['toolUseId']
CURRENT_TOOLUSE_ID = bedrock_tool_use["toolUseId"]
openai_tool_call = {
'id': CURRENT_TOOLUSE_ID,
'type': 'function',
'function': {
'name': bedrock_tool_use['name'],
'arguments': json.dumps(bedrock_tool_use['input'])
}
"id": CURRENT_TOOLUSE_ID,
"type": "function",
"function": {
"name": bedrock_tool_use["name"],
"arguments": json.dumps(bedrock_tool_use["input"]),
},
}
openai_tool_calls.append(openai_tool_call)
@@ -150,126 +166,169 @@ class ChatCompletions:
"system_fingerprint": None,
"choices": [
{
"finish_reason": bedrock_response.get('stopReason', 'end_turn'),
"finish_reason": bedrock_response.get("stopReason", "end_turn"),
"index": 0,
"message": {
"content": content,
"role": bedrock_response.get('output', {}).get('message', {}).get('role', 'assistant'),
"tool_calls": openai_tool_calls if openai_tool_calls != [] else None,
"function_call": None
}
"role": bedrock_response.get("output", {})
.get("message", {})
.get("role", "assistant"),
"tool_calls": openai_tool_calls
if openai_tool_calls != []
else None,
"function_call": None,
},
}
],
"usage": {
"completion_tokens": bedrock_response.get('usage', {}).get('outputTokens', 0),
"prompt_tokens": bedrock_response.get('usage', {}).get('inputTokens', 0),
"total_tokens": bedrock_response.get('usage', {}).get('totalTokens', 0)
}
"completion_tokens": bedrock_response.get("usage", {}).get(
"outputTokens", 0
),
"prompt_tokens": bedrock_response.get("usage", {}).get(
"inputTokens", 0
),
"total_tokens": bedrock_response.get("usage", {}).get("totalTokens", 0),
},
}
return OpenAIResponse(openai_format)
async def _invoke_bedrock(
self,
model: str,
messages: List[Dict[str, str]],
max_tokens: int,
temperature: float,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
**kwargs
) -> OpenAIResponse:
self,
model: str,
messages: List[Dict[str, str]],
max_tokens: int,
temperature: float,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
**kwargs,
) -> OpenAIResponse:
# Non-streaming invocation of Bedrock model
system_prompt, bedrock_messages = self._convert_openai_messages_to_bedrock_format(messages)
(
system_prompt,
bedrock_messages,
) = self._convert_openai_messages_to_bedrock_format(messages)
response = self.client.converse(
modelId = model,
system = system_prompt,
messages = bedrock_messages,
inferenceConfig = {"temperature": temperature, "maxTokens": max_tokens},
toolConfig = {"tools": tools} if tools else None,
modelId=model,
system=system_prompt,
messages=bedrock_messages,
inferenceConfig={"temperature": temperature, "maxTokens": max_tokens},
toolConfig={"tools": tools} if tools else None,
)
openai_response = self._convert_bedrock_response_to_openai_format(response)
return openai_response
async def _invoke_bedrock_stream(
self,
model: str,
messages: List[Dict[str, str]],
max_tokens: int,
temperature: float,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
**kwargs
) -> OpenAIResponse:
self,
model: str,
messages: List[Dict[str, str]],
max_tokens: int,
temperature: float,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
**kwargs,
) -> OpenAIResponse:
# Streaming invocation of Bedrock model
system_prompt, bedrock_messages = self._convert_openai_messages_to_bedrock_format(messages)
(
system_prompt,
bedrock_messages,
) = self._convert_openai_messages_to_bedrock_format(messages)
response = self.client.converse_stream(
modelId = model,
system = system_prompt,
messages = bedrock_messages,
inferenceConfig = {"temperature": temperature, "maxTokens": max_tokens},
toolConfig = {"tools": tools} if tools else None,
modelId=model,
system=system_prompt,
messages=bedrock_messages,
inferenceConfig={"temperature": temperature, "maxTokens": max_tokens},
toolConfig={"tools": tools} if tools else None,
)
# Initialize response structure
bedrock_response = {
'output': {
'message': {
'role': '',
'content': []
}
},
'stopReason': '',
'usage': {},
'metrics': {}
"output": {"message": {"role": "", "content": []}},
"stopReason": "",
"usage": {},
"metrics": {},
}
bedrock_response_text = ""
bedrock_response_tool_input = ""
# Process streaming response
stream = response.get('stream')
stream = response.get("stream")
if stream:
for event in stream:
if event.get('messageStart', {}).get('role'):
bedrock_response['output']['message']['role'] = event['messageStart']['role']
if event.get('contentBlockDelta', {}).get('delta', {}).get('text'):
bedrock_response_text += event['contentBlockDelta']['delta']['text']
print(event['contentBlockDelta']['delta']['text'], end='', flush=True)
if event.get('contentBlockStop', {}).get('contentBlockIndex') == 0:
bedrock_response['output']['message']['content'].append({"text": bedrock_response_text})
if event.get('contentBlockStart', {}).get('start', {}).get('toolUse'):
bedrock_tool_use = event['contentBlockStart']['start']['toolUse']
if event.get("messageStart", {}).get("role"):
bedrock_response["output"]["message"]["role"] = event[
"messageStart"
]["role"]
if event.get("contentBlockDelta", {}).get("delta", {}).get("text"):
bedrock_response_text += event["contentBlockDelta"]["delta"]["text"]
print(
event["contentBlockDelta"]["delta"]["text"], end="", flush=True
)
if event.get("contentBlockStop", {}).get("contentBlockIndex") == 0:
bedrock_response["output"]["message"]["content"].append(
{"text": bedrock_response_text}
)
if event.get("contentBlockStart", {}).get("start", {}).get("toolUse"):
bedrock_tool_use = event["contentBlockStart"]["start"]["toolUse"]
tool_use = {
"toolUseId": bedrock_tool_use['toolUseId'],
"name": bedrock_tool_use['name'],
"toolUseId": bedrock_tool_use["toolUseId"],
"name": bedrock_tool_use["name"],
}
bedrock_response['output']['message']['content'].append({"toolUse": tool_use})
bedrock_response["output"]["message"]["content"].append(
{"toolUse": tool_use}
)
global CURRENT_TOOLUSE_ID
CURRENT_TOOLUSE_ID = bedrock_tool_use['toolUseId']
if event.get('contentBlockDelta', {}).get('delta', {}).get('toolUse'):
bedrock_response_tool_input += event['contentBlockDelta']['delta']['toolUse']['input']
print(event['contentBlockDelta']['delta']['toolUse']['input'], end='', flush=True)
if event.get('contentBlockStop', {}).get('contentBlockIndex') == 1:
bedrock_response['output']['message']['content'][1]['toolUse']['input'] = json.loads(bedrock_response_tool_input)
CURRENT_TOOLUSE_ID = bedrock_tool_use["toolUseId"]
if event.get("contentBlockDelta", {}).get("delta", {}).get("toolUse"):
bedrock_response_tool_input += event["contentBlockDelta"]["delta"][
"toolUse"
]["input"]
print(
event["contentBlockDelta"]["delta"]["toolUse"]["input"],
end="",
flush=True,
)
if event.get("contentBlockStop", {}).get("contentBlockIndex") == 1:
bedrock_response["output"]["message"]["content"][1]["toolUse"][
"input"
] = json.loads(bedrock_response_tool_input)
print()
openai_response = self._convert_bedrock_response_to_openai_format(bedrock_response)
openai_response = self._convert_bedrock_response_to_openai_format(
bedrock_response
)
return openai_response
def create(
self,
model: str,
messages: List[Dict[str, str]],
max_tokens: int,
temperature: float,
stream: Optional[bool] = True,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
**kwargs
) -> OpenAIResponse:
self,
model: str,
messages: List[Dict[str, str]],
max_tokens: int,
temperature: float,
stream: Optional[bool] = True,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
**kwargs,
) -> OpenAIResponse:
# Main entry point for chat completion
bedrock_tools = []
if tools is not None:
bedrock_tools = self._convert_openai_tools_to_bedrock_format(tools)
if stream:
return self._invoke_bedrock_stream(model, messages, max_tokens, temperature, bedrock_tools, tool_choice, **kwargs)
return self._invoke_bedrock_stream(
model,
messages,
max_tokens,
temperature,
bedrock_tools,
tool_choice,
**kwargs,
)
else:
return self._invoke_bedrock(model, messages, max_tokens, temperature, bedrock_tools, tool_choice, **kwargs)
return self._invoke_bedrock(
model,
messages,
max_tokens,
temperature,
bedrock_tools,
tool_choice,
**kwargs,
)
+1 -1
View File
@@ -18,6 +18,7 @@ from tenacity import (
wait_random_exponential,
)
from app.bedrock import BedrockClient
from app.config import LLMSettings, config
from app.exceptions import TokenLimitExceeded
from app.logger import logger # Assuming a logger is set up in your app
@@ -28,7 +29,6 @@ from app.schema import (
Message,
ToolChoice,
)
from app.bedrock import BedrockClient
REASONING_MODELS = ["o1", "o3-mini"]
+49
View File
@@ -0,0 +1,49 @@
SYSTEM_PROMPT = """
You are a LaTeX Beamer Presentation Generator. Your task is to generate a complete, informative, and ready-to-compile Beamer slide deck in LaTeX, based on the task description and any past drafts or feedback.
## Goals:
- Each slide must be **self-contained**, meaning the audience should understand the slide without external explanations.
- The presentation must **teach** or **explain** the topic in sufficient detail using structured LaTeX slides.
- Each slide must contribute meaningfully to the overall structure and flow of the presentation.
## Requirements:
1. Preamble & Setup
- Start with `\\documentclass{beamer}`.
- Use packages such as `amsmath`, `amsfonts`, and `graphicx`.
- Use the `Madrid` theme unless otherwise specified.
- Include full metadata: `\\title{}`, `\\author{}`, and `\\date{\\today}`.
2. Slide Design
- MUST mark each slide with a comment indicating its number, `% Slide 1`, `% Slide 2`.
- - Slides must follow a **logical order** that ensures smooth flow and coherence.
- AIM for a **minimum of 300 words per slide* Contain **enough detail** (text, bullets, equations, definitions, or examples)
3. Depth of Content
- For important concept, include motivation problem intuitive explanation mathematical formulation or equation (if applicable)
- practical example or application can also be included
4. Completeness & Validity
- Reflect all provided feedback and correct deficiencies from past versions.
- MUST No placeholders or incomplete content.
- Your output will be used directly. Therefore, it must be a ready-to-use result.
- Include `\\end{document}`.
- Ensure valid LaTeX syntax.
5. Style & Clarity
- Maintain consistent formatting and indentation.
- Use bullet points or short paragraphs for clarity.
- Keep math readable and contextualized with supporting text.
**Only output the final LaTeX source code. Do not include explanations, notes, or comments.**
"""
USER_CONTENT = """
## Task
{request}
## Past Drafts & Feedback
{history}
"""
+42
View File
@@ -0,0 +1,42 @@
TEXT_VALIDATION_PROMPT = """
You are a task result evaluator responsible for determining whether a task result meets the task requirements, if not, you need to improve it.
# Objective and Steps
1. **Completeness and Quality Check:**
- Verify that the result includes all required elements of the task.
- Evaluate whether the output meets overall quality criteria (accuracy, clarity, formatting, and completeness).
2. **Change Detection:**
- If this is a subsequent result, compare it with previous iterations.
- If the differences are minimal or the result has not significantly improved, consider it "good enough" for finalization.
3. **Feedback and Escalation:**
- If the result meets the criteria or the improvements are negligible compared to previous iterations, return **"No further feedback"**.
- Otherwise, provide **direct and precise feedback** and **output the improved result in the required format** for finalization.
4. **Ensure Completeness:**
- Your output must meet all requirements of the task.
- Include all necessary details so that the output is self-contained and can be directly used as input for downstream tasks.
5. **Do NOT:**
- Leave any section with placeholders (e.g., "TODO", "Add content here").
- Include any commentary or reminders to the writer or user (e.g., "We can add more later").
- Output partial slides or omit essential details assuming future input.
- **If the result meets the standard:**
- Return **"No further feedback."**.
- **If the result does not meet the standard:**
- add detailed jusification for the change start with "here are some feedbacks" and directly write an improved new result start with "here are the changes".
# Note that: Any output containing incomplete sections, placeholders is not allowed.
"""
USER_CONTENT = """
## Current Task Requirement:
{request}
---
## Current Task Latest Result:
{history}
"""
+38
View File
@@ -96,6 +96,35 @@ class Message(BaseModel):
message["base64_image"] = self.base64_image
return message
def to_string(self) -> str:
"""
Convert the Message instance into a human-readable string format.
Returns:
str: A formatted string representing the message.
"""
# Format the header with role and name
role_str = self.role.upper() if self.role else "UNKNOWN"
name_str = f" ({self.name})" if self.name else ""
header = f"[{role_str}{name_str}]"
# Start with the message content
content = self.content or ""
# Append tool call details if available
if self.tool_calls:
tool_calls_str = "\n".join(
f" ↳ ToolCall: {tc.function.name}({tc.function.arguments}) [id={tc.id}]"
for tc in self.tool_calls
)
content += "\n" + tool_calls_str
# Append information about attached image if any
if self.base64_image:
content += "\n ↳ [Image Attached: base64 content hidden]"
return f"{header}\n{content.strip()}"
@classmethod
def user_message(
cls, content: str, base64_image: Optional[str] = None
@@ -182,3 +211,12 @@ class Memory(BaseModel):
def to_dict_list(self) -> List[dict]:
"""Convert messages to list of dicts"""
return [msg.to_dict() for msg in self.messages]
def to_string(self) -> str:
"""
Convert the memory's list of messages to a readable string format.
Returns:
str: A formatted string representing the entire conversation history.
"""
return "\n\n".join(msg.to_string() for msg in self.messages)
+5
View File
@@ -2,10 +2,12 @@ from app.tool.base import BaseTool
from app.tool.bash import Bash
from app.tool.browser_use_tool import BrowserUseTool
from app.tool.create_chat_completion import CreateChatCompletion
from app.tool.latex_generator import LatexGenerator
from app.tool.planning import PlanningTool
from app.tool.str_replace_editor import StrReplaceEditor
from app.tool.terminate import Terminate
from app.tool.tool_collection import ToolCollection
from app.tool.validator import Validator
__all__ = [
@@ -13,8 +15,11 @@ __all__ = [
"Bash",
"BrowserUseTool",
"Terminate",
"ValiTerminate",
"StrReplaceEditor",
"ToolCollection",
"CreateChatCompletion",
"PlanningTool",
"Validator",
"LatexGenerator",
]
+38
View File
@@ -0,0 +1,38 @@
from pydantic import Field
from app.llm import LLM
from app.prompt.latex_generator import SYSTEM_PROMPT, USER_CONTENT
from app.tool.base import BaseTool
_Latex_Generator_DESCRIPTION = """
This agent generates complete, high-quality LaTeX documents with a focus on Beamer presentations. It accepts topic-specific input and produces fully self-contained LaTeX source code, including all required packages, structures, and rich content elements such as equations, figures, and formatted text. The agent ensures completeness by avoiding any placeholders or incomplete sections.
In addition to generation, the agent supports iterative refinement: it evaluates and improves the generated LaTeX code based on validation feedback to ensure correctness, formatting quality, and logical structure. The final output is ready for immediate compilation and professional presentation use.
"""
class LatexGenerator(BaseTool):
llm: LLM = Field(default_factory=LLM, description="Language model instance")
name: str = "latexgenerator"
description: str = _Latex_Generator_DESCRIPTION
parameters: dict = {}
async def generate(self, request: str, history: str = ""):
"""Abstract method for result validate logic.
Args:
step_result: The result string to validate.
"""
system_content = SYSTEM_PROMPT
user_content = USER_CONTENT.format(request=request, history=history)
feedback = await self.llm.ask(
messages=[{"role": "user", "content": user_content}],
system_msgs=[{"role": "system", "content": system_content}],
)
return feedback
async def execute(self, request: str, history: str = "") -> str:
"""Finish the current execution"""
return await self.generate(request, history)
+40
View File
@@ -0,0 +1,40 @@
from pydantic import Field
from app.llm import LLM
from app.prompt.validator import TEXT_VALIDATION_PROMPT, USER_CONTENT
from app.tool.base import BaseTool
_VALIDATE_DESCRIPTION = """
This tool evaluates the quality and completeness of a subtask result against a set of predefined criteria.
It checks whether the result fully satisfies task requirements, maintains high quality in terms of clarity, accuracy, and formatting,
and determines whether improvements have been made in comparison to prior versions.
If the result is satisfactory or improvements are minimal, it returns "The step result has already reached the requirement.".
Otherwise, it provides detailed feedback and a revised version of the result that meets all requirements and is ready for downstream use.
"""
class Validator(BaseTool):
llm: LLM = Field(default_factory=LLM, description="Language model instance")
name: str = "validator"
description: str = _VALIDATE_DESCRIPTION
parameters: dict = {}
async def validate(self, request: str, history: str):
"""Abstract method for result validate logic.
Args:
step_result: The result string to validate.
"""
system_content = TEXT_VALIDATION_PROMPT
user_content = USER_CONTENT.format(request=request, history=history)
feedback = await self.llm.ask(
messages=[{"role": "user", "content": user_content}],
system_msgs=[{"role": "system", "content": system_content}],
)
return feedback
async def execute(self, request: str, history: str) -> str:
"""Finish the current execution"""
return await self.validate(request, history)
+30
View File
@@ -0,0 +1,30 @@
import asyncio
from app.agent.ppt import PPTAgent
from app.logger import logger
async def main():
agent = PPTAgent()
try:
prompt = """
1. Lecture slide:
I am a lecturer. I am teaching the machine learning coure for research students. Please generate latex code for lecture slide for different reinforcement learning algorithms.
Note that:
1). Note that the lecture duration is 2 hour, so we need to generate 30 pages.
2). for each reinforcement learning algorithms, the slide should include motivation, problem and intuitive solution and detailed math equations.
3). Please make sure the the lecture have a good self-contain.
"""
if not prompt.strip():
logger.warning("Empty prompt provided.")
return
logger.warning("Processing your request...")
await agent.run(prompt)
logger.info("Request processing completed.")
except KeyboardInterrupt:
logger.warning("Operation interrupted.")
if __name__ == "__main__":
asyncio.run(main())

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB