Merge pull request #1127 from KaeyoungLIAN/flow-auto-validate
Pre-commit checks / pre-commit-check (push) Failing after 1s
Pre-commit checks / pre-commit-check (push) Failing after 1s
Add Latex beamer Generate and auto validate
This commit is contained in:
@@ -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)
|
||||
|
||||
[](https://github.com/mannaandpoem/OpenManus/stargazers)
|
||||
 
|
||||
[](https://opensource.org/licenses/MIT)  
|
||||
[](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
|
||||
|
||||
[](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)
|
||||
|
||||
[](https://github.com/mannaandpoem/OpenManus/stargazers)
|
||||
 
|
||||
[](https://opensource.org/licenses/MIT)  
|
||||
[](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
|
||||
|
||||
[](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}},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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
@@ -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
@@ -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"]
|
||||
|
||||
@@ -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}
|
||||
"""
|
||||
@@ -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}
|
||||
"""
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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 |
Reference in New Issue
Block a user