unruffle commited on
Commit
fcedb1b
·
verified ·
1 Parent(s): b1e1473

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +38 -0
  2. app.py +104 -0
  3. requirements.txt +7 -0
Dockerfile ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive \
4
+ PYTHONDONTWRITEBYTECODE=1 \
5
+ PYTHONUNBUFFERED=1 \
6
+ PIP_NO_CACHE_DIR=1 \
7
+ PYSTOW_HOME=/app/cache/pystow \
8
+ PYTHONIOENCODING=UTF-8 \
9
+ PORT=7860
10
+
11
+ RUN apt-get update && apt-get install -y --no-install-recommends \
12
+ curl ca-certificates \
13
+ libstdc++6 libgomp1 \
14
+ libxrender1 libxext6 \
15
+ tini \
16
+ && rm -rf /var/lib/apt/lists/*
17
+
18
+ WORKDIR /app
19
+ RUN useradd -m -u 1000 user
20
+
21
+ COPY requirements.txt /app/requirements.txt
22
+ RUN pip install --upgrade pip && \
23
+ pip install -r requirements.txt
24
+
25
+ # 创建缓存目录并赋予权限
26
+ RUN mkdir -p /app/cache/pystow && chown -R user:user /app/cache
27
+
28
+ COPY --chown=user:user . /app
29
+
30
+ # 切换到非root用户执行后续操作
31
+ USER user
32
+
33
+ # 预下载STOUT模型权重,避免API首次请求超时
34
+ RUN python -c "from STOUT import translate_forward; print('Pre-loading model...'); translate_forward('C'); print('Complete.')"
35
+
36
+ EXPOSE $PORT
37
+ ENTRYPOINT ["/usr/bin/tini", "--"]
38
+ CMD ["sh", "-c", "uvicorn app:app --host 0.0.0.0 --port ${PORT:-7860}"]
app.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import gradio as gr
4
+ from fastapi import FastAPI
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from pydantic import BaseModel
7
+ from typing import List
8
+
9
+ from rdkit import Chem
10
+ from rdkit.Chem import MolToSmiles
11
+ from STOUT import translate_forward
12
+
13
+ app = FastAPI(title="SMILES → IUPAC (STOUT-V2)")
14
+
15
+ app.add_middleware(
16
+ CORSMiddleware,
17
+ allow_origins=["*"],
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
21
+
22
+ def canonicalize_smiles(s: str) -> str:
23
+ mol = Chem.MolFromSmiles(s)
24
+ if mol is None:
25
+ raise ValueError(f"非法SMILES,RDKit无法解析:{s!r}")
26
+ return MolToSmiles(mol, canonical=True)
27
+
28
+ class SMILESItem(BaseModel):
29
+ smiles: str
30
+ canonicalize: bool = True
31
+
32
+ class BatchRequest(BaseModel):
33
+ inputs: List[SMILESItem]
34
+
35
+ @app.get("/healthz")
36
+ def healthz():
37
+ return {"ok": True}
38
+
39
+ @app.post("/api/smiles2iupac")
40
+ def api_smiles2iupac(req: SMILESItem):
41
+ try:
42
+ s = (req.smiles or "").strip()
43
+ if not s:
44
+ return {"success": False, "error": "输入为空"}
45
+
46
+ s_norm = canonicalize_smiles(s) if req.canonicalize else s
47
+ name = translate_forward(s_norm)
48
+
49
+ return {
50
+ "success": True,
51
+ "input": s,
52
+ "smiles_processed": s_norm,
53
+ "iupac": name,
54
+ }
55
+ except Exception as e:
56
+ return {"success": False, "error": str(e)}
57
+
58
+ @app.post("/api/smiles2iupac/batch")
59
+ def api_smiles2iupac_batch(req: BatchRequest):
60
+ out = []
61
+ for item in req.inputs:
62
+ try:
63
+ s = (item.smiles or "").strip()
64
+ if not s:
65
+ out.append({"success": False, "error": "输入为空"})
66
+ continue
67
+
68
+ s_norm = canonicalize_smiles(s) if item.canonicalize else s
69
+ name = translate_forward(s_norm)
70
+ out.append({
71
+ "success": True,
72
+ "input": s,
73
+ "smiles_processed": s_norm,
74
+ "iupac": name,
75
+ })
76
+ except Exception as e:
77
+ out.append({"success": False, "input": item.smiles, "error": str(e)})
78
+ return out
79
+
80
+ def gradio_fn(s: str, canonicalize: bool):
81
+ if not (s or "").strip():
82
+ return "", "输入为空"
83
+ try:
84
+ s_norm = canonicalize_smiles(s) if canonicalize else s
85
+ name = translate_forward(s_norm)
86
+ return name, f"输入SMILES: {s}\n规范化SMILES: {s_norm}"
87
+ except Exception as e:
88
+ return "", f"Error: {e}"
89
+
90
+ demo = gr.Interface(
91
+ fn=gradio_fn,
92
+ inputs=[
93
+ gr.Textbox(label="输入SMILES", placeholder="支持任意合法SMILES写法"),
94
+ gr.Checkbox(label="自动RDKit规范化", value=True),
95
+ ],
96
+ outputs=[
97
+ gr.Textbox(label="IUPAC名称", interactive=True),
98
+ gr.Textbox(label="调试信息"),
99
+ ],
100
+ title="SMILES → IUPAC (STOUT-V2)",
101
+ description="Model: Kohulan/STOUT-V2 | 基于TensorFlow的翻译模型",
102
+ )
103
+
104
+ app = gr.mount_gradio_app(app, demo, path="/")
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ gradio>=4.0
4
+ pydantic
5
+ rdkit-pypi
6
+ STOUT-pypi
7
+ tensorflow-cpu