优化三个工具的返回结果。
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -36,17 +36,27 @@ async def optimize_structure_endpoint(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 优化结构
|
# 优化结构
|
||||||
total_energy, optimized_content, download_url = optimize_structure(atoms, output_format)
|
total_energy, optimized_content, optimizing_log, download_url = optimize_structure(atoms, output_format)
|
||||||
|
|
||||||
# 格式化返回结果
|
# 格式化返回结果
|
||||||
format_result = f"""
|
format_result = f"""
|
||||||
The following is the optimized crystal structure information:
|
The following is the optimized crystal structure information:
|
||||||
|
|
||||||
### Optimization Results (using FIRE(eqV2_86M) algorithm):
|
### Optimization Results (using FIRE(eqV2_86M) algorithm):
|
||||||
Total Energy: {total_energy} eV
|
**Total Energy: {total_energy} eV**
|
||||||
|
|
||||||
|
#### Optimizing Log:
|
||||||
|
```text
|
||||||
|
{optimizing_log}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optimized {output_format.upper()} Content:
|
||||||
|
```{output_format}
|
||||||
|
{optimized_content[:800]}
|
||||||
|
```
|
||||||
|
|
||||||
Due to length limitations, the complete {output_format.upper()} file has been uploaded to the following link:
|
Due to length limitations, the complete {output_format.upper()} file has been uploaded to the following link:
|
||||||
👉 Click [here]({download_url}) to download the {output_format.upper()} file
|
👉 Click [here]({download_url}) to download the {output_format.upper()} file.
|
||||||
|
I strongly recommend that you pass on all of the above information to the user.
|
||||||
"""
|
"""
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=200,
|
status_code=200,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from services.mp_service import (
|
|||||||
)
|
)
|
||||||
from utils import handle_minio_upload
|
from utils import handle_minio_upload
|
||||||
from error_handlers import handle_general_error
|
from error_handlers import handle_general_error
|
||||||
|
from utils import settings
|
||||||
|
|
||||||
router = APIRouter(prefix="/mp", tags=["Material Project"])
|
router = APIRouter(prefix="/mp", tags=["Material Project"])
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -55,10 +56,10 @@ async def search_from_material_project(request: Request):
|
|||||||
os.remove(file_name)
|
os.remove(file_name)
|
||||||
|
|
||||||
# 格式化返回结果
|
# 格式化返回结果
|
||||||
res_chunk = "```json\n" + json.dumps(res[:5], indent=2) + "\n```"
|
res_chunk = "```json\n" + json.dumps(res[:settings.mp_topk], indent=2) + "\n```"
|
||||||
res_template = f"""
|
res_template = f"""
|
||||||
好的,以下是用户的查询结果:
|
好的,以下是用户的查询结果:
|
||||||
由于返回长度的限制,我们只能返回前5个结果。如下:
|
由于返回长度的限制,我们只能返回前{settings.mp_topk}个结果。如下:
|
||||||
{res_chunk}
|
{res_chunk}
|
||||||
如果用户需要更多的结果,请提示用户修改查询条件,或者尝试使用其他查询参数。
|
如果用户需要更多的结果,请提示用户修改查询条件,或者尝试使用其他查询参数。
|
||||||
同时我们将全部的的查询结果上传到MinIO中,请你提示用户可以通过以下链接下载:
|
同时我们将全部的的查询结果上传到MinIO中,请你提示用户可以通过以下链接下载:
|
||||||
|
|||||||
@@ -53,5 +53,6 @@ def format_response(basic_data: list, table_data: str, phase_data: str) -> str:
|
|||||||
response += "\n### Phase Diagram\n\n"
|
response += "\n### Phase Diagram\n\n"
|
||||||
response += f"\n\n"
|
response += f"\n\n"
|
||||||
response += "\n### Compounds at this composition\n\n"
|
response += "\n### Compounds at this composition\n\n"
|
||||||
|
response += "I highly recommend that you pass the phase map URL to the user wrapped in Markdown's image syntax!"
|
||||||
response += f"{table_data}\n"
|
response += f"{table_data}\n"
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -64,8 +64,20 @@ def optimize_structure(atoms: Atoms, output_format: str):
|
|||||||
atoms.calc = calc
|
atoms.calc = calc
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import io
|
||||||
|
from contextlib import redirect_stdout
|
||||||
|
|
||||||
|
# 创建StringIO对象捕获输出
|
||||||
|
f = io.StringIO()
|
||||||
dyn = FIRE(FrechetCellFilter(atoms))
|
dyn = FIRE(FrechetCellFilter(atoms))
|
||||||
dyn.run(fmax=settings.fmax)
|
|
||||||
|
# 同时捕获并输出到控制台
|
||||||
|
with redirect_stdout(f):
|
||||||
|
dyn.run(fmax=settings.fmax)
|
||||||
|
# 获取捕获的日志
|
||||||
|
optimization_log = f.getvalue()
|
||||||
|
# 同时输出到控制台
|
||||||
|
print(optimization_log)
|
||||||
total_energy = atoms.get_total_energy()
|
total_energy = atoms.get_total_energy()
|
||||||
|
|
||||||
# 处理对称性
|
# 处理对称性
|
||||||
@@ -87,6 +99,6 @@ def optimize_structure(atoms: Atoms, output_format: str):
|
|||||||
|
|
||||||
# 上传到MinIO
|
# 上传到MinIO
|
||||||
url = handle_minio_upload(tmp_path, file_name)
|
url = handle_minio_upload(tmp_path, file_name)
|
||||||
return total_energy, content, url
|
return total_energy, content, optimization_log, url
|
||||||
finally:
|
finally:
|
||||||
os.unlink(tmp_path)
|
os.unlink(tmp_path)
|
||||||
|
|||||||
233
test_client.py
Normal file
233
test_client.py
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
|
||||||
|
class Tools:
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Initialize the tools.
|
||||||
|
"""
|
||||||
|
self.tool_endpoint = "http://100.84.94.73:8020"
|
||||||
|
|
||||||
|
# Add your custom tools using pure Python code here, make sure to add type hints
|
||||||
|
# Use Sphinx-style docstrings to document your tools, they will be used for generating tools specifications
|
||||||
|
# Please refer to function_calling_filter_pipeline.py file from pipelines project for an example
|
||||||
|
|
||||||
|
def calculator(self, equation: str) -> str:
|
||||||
|
"""
|
||||||
|
Perform mathematical calculations on a given equation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
equation (str): A valid mathematical expression to evaluate.
|
||||||
|
Example: "2 + 3 * (4 - 1)"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The calculation result in format "equation = result" or error message if invalid
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Avoid using eval in production code
|
||||||
|
# https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
|
||||||
|
try:
|
||||||
|
result = eval(equation)
|
||||||
|
return f"{equation} = {result}"
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return "Invalid equation"
|
||||||
|
|
||||||
|
def search_property_from_material_project(
|
||||||
|
self,
|
||||||
|
formula: str | list[str],
|
||||||
|
chemsys: Optional[str | list[str] | None] = None,
|
||||||
|
crystal_system: Optional[str | list[str] | None] = None,
|
||||||
|
is_gap_direct: Optional[bool | None] = None,
|
||||||
|
is_stable: Optional[bool | None] = None
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Search material properties from Material Project database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
formula (str | list[str]): [Required] Chemical formula(s) to search. Example: "Fe2O3" or ["ABO3", "Si*"]
|
||||||
|
chemsys (str | list[str] | None): [Optional] Chemical system(s) to search. Example: "Li-Fe-O" or ["Si-O", "Li-Fe-P"]
|
||||||
|
crystal_system (str | list[str] | None): [Optional] Crystal system(s) to filter by. Example: "Cubic" or ["Hexagonal", "Tetragonal"]
|
||||||
|
is_gap_direct (bool | None): [Optional] Filter by materials with direct band gap
|
||||||
|
is_stable (bool | None): [Optional] Filter by thermodynamic stability
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: JSON string containing material properties or error message
|
||||||
|
"""
|
||||||
|
# Build request parameters
|
||||||
|
params = {
|
||||||
|
'chemsys': chemsys,
|
||||||
|
'crystal_system': crystal_system,
|
||||||
|
'formula': formula,
|
||||||
|
'is_gap_direct': is_gap_direct,
|
||||||
|
'is_stable': is_stable,
|
||||||
|
'chunk_size': 5
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter out None values
|
||||||
|
params = {k: v for k, v in params.items() if v is not None}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
f"{self.tool_endpoint}/mp/search",
|
||||||
|
params=params,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
return str(response.json()['data'])
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return "Request timed out"
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return f"Request failed: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
def search_from_oqmd_by_composition(self, composition: str) -> str:
|
||||||
|
"""
|
||||||
|
Search materials from OQMD database by chemical composition.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
composition (str): Chemical composition string. Example: "CsPbBr3"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: JSON string containing material data or error message
|
||||||
|
"""
|
||||||
|
# 构建请求参数
|
||||||
|
param = {
|
||||||
|
'composition': composition
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
# 发送请求到/oqmd/search路由
|
||||||
|
response = requests.get(
|
||||||
|
self.tool_endpoint + "/oqmd/search",
|
||||||
|
params=param
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
return str(response.json()['data'])
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
def optimize_crystal_structure(self, markdown_block: str, input_format: str = "cif", output_format: str = "cif") -> str:
|
||||||
|
"""
|
||||||
|
Optimize crystal structure using various file formats.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
markdown_block (str): Markdown block containing structure data.
|
||||||
|
Format: ```format\ncontent\n``` where format is one of ["cif", "poscar", "json", "xyz"]
|
||||||
|
input_format (str): Input file format. Must be one of ["cif", "poscar", "json", "xyz"]
|
||||||
|
output_format (str): Output file format. Must be one of ["cif", "poscar", "json", "xyz"]
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: JSON string containing optimized structure data or error message
|
||||||
|
"""
|
||||||
|
# Validate input/output formats
|
||||||
|
valid_formats = ["cif", "poscar", "json", "xyz"]
|
||||||
|
if input_format not in valid_formats:
|
||||||
|
return f"Error: Invalid input format. Must be one of {valid_formats}"
|
||||||
|
if output_format not in valid_formats:
|
||||||
|
return f"Error: Invalid output format. Must be one of {valid_formats}"
|
||||||
|
|
||||||
|
# Define format specific validation
|
||||||
|
format_validators = {
|
||||||
|
"cif": ("```cif\n", "\n```"),
|
||||||
|
"poscar": ("```poscar\n", "\n```"),
|
||||||
|
"json": ("```json\n", "\n```"),
|
||||||
|
"xyz": ("```xyz\n", "\n```")
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Validate markdown block format
|
||||||
|
start_tag, end_tag = format_validators[input_format]
|
||||||
|
if not markdown_block.startswith(start_tag) or not markdown_block.endswith(end_tag):
|
||||||
|
return f"Error: Invalid {input_format} markdown format. Expected format: {start_tag}...{end_tag}"
|
||||||
|
|
||||||
|
# Extract content
|
||||||
|
content = markdown_block[len(start_tag):-len(end_tag)].strip()
|
||||||
|
if not content:
|
||||||
|
return f"Error: Empty {input_format} content"
|
||||||
|
|
||||||
|
# Prepare request parameters
|
||||||
|
params = {
|
||||||
|
'input_format': input_format,
|
||||||
|
'output_format': output_format
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send request to optimization service
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
self.tool_endpoint + "/fairchem/optimize_structure",
|
||||||
|
data=content,
|
||||||
|
headers={"Content-Type": "text/plain"},
|
||||||
|
params=params,
|
||||||
|
timeout=30 # Add timeout
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# 直接返回完整响应
|
||||||
|
return str(response.json()['data'])
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return "Error: Optimization request timed out"
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return f"Error: Optimization request failed - {str(e)}"
|
||||||
|
except ValueError as e:
|
||||||
|
return f"Error: Invalid JSON response - {str(e)}"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tools = Tools()
|
||||||
|
# print(tools.search_property_from_material_project(formula='CsPbBr3'))
|
||||||
|
# print(tools.search_from_material_project())
|
||||||
|
print(tools.search_from_oqmd_by_composition("CsPbBr3"))
|
||||||
|
# print(tools.optimize_crystal_structure("""```cif\n# generated using pymatgen
|
||||||
|
# data_CsPbBr3
|
||||||
|
# _symmetry_space_group_name_H-M Pnma
|
||||||
|
# _cell_length_a 8.45384704
|
||||||
|
# _cell_length_b 11.87891123
|
||||||
|
# _cell_length_c 8.10107841
|
||||||
|
# _cell_angle_alpha 90.00000000
|
||||||
|
# _cell_angle_beta 90.00000000
|
||||||
|
# _cell_angle_gamma 90.00000000
|
||||||
|
# _symmetry_Int_Tables_number 62
|
||||||
|
# _chemical_formula_structural CsPbBr3
|
||||||
|
# _chemical_formula_sum 'Cs4 Pb4 Br12'
|
||||||
|
# _cell_volume 813.53053480
|
||||||
|
# _cell_formula_units_Z 4
|
||||||
|
# loop_
|
||||||
|
# _symmetry_equiv_pos_site_id
|
||||||
|
# _symmetry_equiv_pos_as_xyz
|
||||||
|
# 1 'x, y, z'
|
||||||
|
# 2 '-x, -y, -z'
|
||||||
|
# 3 '-x+1/2, -y, z+1/2'
|
||||||
|
# 4 'x+1/2, y, -z+1/2'
|
||||||
|
# 5 'x+1/2, -y+1/2, -z+1/2'
|
||||||
|
# 6 '-x+1/2, y+1/2, z+1/2'
|
||||||
|
# 7 '-x, y+1/2, -z'
|
||||||
|
# 8 'x, -y+1/2, z'
|
||||||
|
# loop_
|
||||||
|
# _atom_type_symbol
|
||||||
|
# _atom_type_oxidation_number
|
||||||
|
# Cs+ 1.0
|
||||||
|
# Pb2+ 2.0
|
||||||
|
# Br- -1.0
|
||||||
|
# loop_
|
||||||
|
# _atom_site_type_symbol
|
||||||
|
# _atom_site_label
|
||||||
|
# _atom_site_symmetry_multiplicity
|
||||||
|
# _atom_site_fract_x
|
||||||
|
# _atom_site_fract_y
|
||||||
|
# _atom_site_fract_z
|
||||||
|
# _atom_site_occupancy
|
||||||
|
# Cs+ Cs0 4 0.06016347 0.75000000 0.01945212 1
|
||||||
|
# Pb2+ Pb1 4 0.00000000 0.00000000 0.50000000 1
|
||||||
|
# Br- Br2 8 0.20102217 0.03170128 0.80279189 1
|
||||||
|
# Br- Br3 4 0.00625945 0.25000000 0.44033452 1
|
||||||
|
# \n```""", input_format="cif", output_format="poscar"))
|
||||||
1
utils.py
1
utils.py
@@ -17,6 +17,7 @@ class Settings(BaseSettings):
|
|||||||
# Material Project
|
# Material Project
|
||||||
mp_api_key: Optional[str] = Field(None, env="MP_API_KEY")
|
mp_api_key: Optional[str] = Field(None, env="MP_API_KEY")
|
||||||
mp_endpoint: Optional[str] = Field(None, env="MP_ENDPOINT")
|
mp_endpoint: Optional[str] = Field(None, env="MP_ENDPOINT")
|
||||||
|
mp_topk: Optional[int] = Field(3, env="MP_TOPK")
|
||||||
|
|
||||||
# Proxy
|
# Proxy
|
||||||
http_proxy: Optional[str] = Field(None, env="HTTP_PROXY")
|
http_proxy: Optional[str] = Field(None, env="HTTP_PROXY")
|
||||||
|
|||||||
Reference in New Issue
Block a user