File size: 6,548 Bytes
62c1330
 
 
 
 
 
 
 
 
462525d
62c1330
 
 
462525d
 
 
 
62c1330
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462525d
62c1330
 
 
 
 
 
 
 
462525d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62c1330
 
 
162bf50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62c1330
162bf50
 
 
62c1330
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162bf50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
from gevent import pywsgi
import sys
import time
import argparse
import uvicorn
from typing import Union
from pydantic import BaseModel
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import os
import openedai
import numpy as np
import asyncio
from urllib.parse import urlparse
import nacos
import configparser



app = openedai.OpenAIStub()
moderation = None
device = "cuda" if torch.cuda.is_available() else "cpu"
#device = "cpu"

labels = ['hate', 
          'hate_threatening', 
          'harassment', 
          'harassment_threatening', 
          'self_harm', 
          'self_harm_intent', 
          'self_harm_instructions',
          'sexual',
          'sexual_minors',
          'violence',
          'violence_graphic',
         ]

label2id = {l:i for i, l in enumerate(labels)}
id2label = {i:l for i, l in enumerate(labels)}
model_name = "duanyu027/moderation_0628"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=len(labels),id2label=id2label, label2id=label2id, problem_type = "multi_label_classification")
model.to(device)
model.eval()
#model = torch.quantization.quantize_dynamic(
#	model, {torch.nn.Linear}, dtype=torch.qint8
#)
torch.set_num_threads(1)
def register_service(client,service_name,service_ip,service_port,cluster_name,health_check_interval,weight,http_proxy,domain,protocol,direct_domain):
    try:
        # 初始化 metadata
        metadata = {}
        
        # 如果 http_proxy 为 True,添加额外的 metadata 键值对
        if http_proxy:
            metadata["http_proxy"] = True
            if direct_domain:
                metadata["domain"] = f"{protocol}://{service_ip}:{service_port}"
            else:
                metadata["domain"] = f"{domain}/port/{service_port}"
        else:
            metadata["http_proxy"] = False
            metadata["domain"] = f"{protocol}://{service_ip}:{service_port}"
        response = client.add_naming_instance(
            service_name,
            service_ip,
            service_port,
            cluster_name,
            weight,
            metadata,
            enable=True,
            healthy=True,
            ephemeral=True,
            heartbeat_interval=health_check_interval
        )
        return response
    except Exception as e:
        print(f"Error registering service to Nacos: {e}")
        return True
        
class ModerationsRequest(BaseModel):
	model: str = "text-moderation-latest" # or "text-moderation-stable"
	input: Union[str, list[str]]
@app.on_event("startup")
def startup_event():
    # 创建配置解析器
    config = configparser.ConfigParser()
    # 读取配置文件
    if not config.read('config.ini'):
        raise RuntimeError("配置文件不存在")
    
    # Nacos server and other configurations
    NACOS_SERVER = config['nacos']['nacos_server']
    NAMESPACE = config['nacos']['namespace']
    CLUSTER_NAME = config['nacos']['cluster_name']
    client = nacos.NacosClient(NACOS_SERVER, namespace=NAMESPACE, username=config['nacos']['username'], password=config['nacos']['password'])
    SERVICE_NAME = config['nacos']['service_name']
    HEALTH_CHECK_INTERVAL = int(config['nacos']['health_check_interval'])
    WEIGHT = int(config.get('nacos', 'weight', fallback='1'))
    HTTP_PROXY = config.getboolean('server', 'http_proxy')
    DOMAIN = config['server']['domain']
    PROTOCOL = config['server']['protocol']
    DIRECT_DOMAIN = config.getboolean('server', 'direct_domain')
    
    # Parse AutoDLServiceURL
    autodl_url = os.environ.get('AutoDLServiceURL')
    if not autodl_url:
        raise RuntimeError("Error: AutoDLServiceURL environment variable is not set.")
    
    parsed_url = urlparse(autodl_url)
    SERVICE_IP = parsed_url.hostname
    SERVICE_PORT = parsed_url.port
    if not SERVICE_IP or not SERVICE_PORT:
        raise RuntimeError("Error: Invalid AutoDLServiceURL format.")

    # Register service with Nacos
    if not register_service(client, SERVICE_NAME, SERVICE_IP, SERVICE_PORT, CLUSTER_NAME, HEALTH_CHECK_INTERVAL, WEIGHT, HTTP_PROXY, DOMAIN, PROTOCOL, DIRECT_DOMAIN):
        raise RuntimeError("Service is healthy but failed to register.")
@app.post("/v1/moderations")
async def moderations(request: ModerationsRequest):
    results = {
        "id": f"modr-{int(time.time()*1e9)}",
        "model": "text-moderation-005",
        "results": [],
    }
    if isinstance(request.input, str):
        request.input = [request.input]
    
    thresholds = {
        "sexual": 0.5,
        "hate": 0.5,
        "harassment": 0.5,
        "self_harm": 0.5,
        "sexual_minors": 0.9,
        "hate_threatening": 0.9,
        "violence_graphic": 0.9,
        "self_harm_intent": 0.9,
        "self_harm_instructions": 0.9,
        "harassment_threatening": 0.9,
        "violence": 0.5,
    }

    for text in request.input:
        predictions = await predict(text, model, tokenizer)
        category_scores = {labels[i]: predictions[0][i].item() for i in range(len(labels))}
        detect = {key: score > thresholds[key] for key, score in category_scores.items()}
        detected = any(detect.values())

        results['results'].append({
            'flagged': detected,
            'categories': detect,
            'category_scores': category_scores,
        })

    return results
def sigmoid(x):
   return 1/(1 + np.exp(-x))
    
def parse_args(argv):
	parser = argparse.ArgumentParser(description='Moderation API')
	parser.add_argument('--host', type=str, default='0.0.0.0')
	parser.add_argument('--port', type=int, default=5002)
	parser.add_argument('--test-load', action='store_true')
	return parser.parse_args(argv)
    
async def predict(text, model, tokenizer):
    encoding = tokenizer.encode_plus(
        text,
        return_tensors='pt'
    )
    input_ids = encoding['input_ids'].to(device)
    attention_mask = encoding['attention_mask'].to(device)
    
    # 运行模型预测在独立的线程中
    def _predict():
        with torch.no_grad():
            outputs = model(input_ids, attention_mask=attention_mask)
        return torch.sigmoid(outputs.logits)
    
    loop = asyncio.get_running_loop()
    predictions = await loop.run_in_executor(None, _predict)
    
    # 清理 GPU 内存
    del input_ids
    del attention_mask
    torch.cuda.empty_cache()
    
    return predictions
# Main
if __name__ == "__main__":

    uvicorn.run("moderations:app", host="0.0.0.0", port=6006, reload=True)