sin30 commited on
Commit
00cfefb
·
verified ·
1 Parent(s): 06fc250

Upload mt_openapi.py

Browse files
Files changed (1) hide show
  1. mt_openapi.py +156 -0
mt_openapi.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tenacity import retry, stop_after_delay, wait_fixed, retry_if_result
2
+ import requests
3
+ import json
4
+ import os
5
+
6
+
7
+ class MTOpenApiClient:
8
+
9
+ def __init__(self, api_name, api_key=None, cost_attribution=None):
10
+ """
11
+ Initialize MTOpenApiClient with credentials.
12
+
13
+ Args:
14
+ api_name (str): API endpoint name
15
+ api_key (str, optional): Direct API key for credential lookup
16
+ cost_attribution (str, optional): Cost attribution key for AI flow credentials
17
+
18
+ Raises:
19
+ ValueError: If credentials are invalid or missing
20
+ FileNotFoundError: If configuration files are not found
21
+ """
22
+ self.api_name = api_name
23
+
24
+ # Load credentials based on provided parameters
25
+ if api_key is not None:
26
+ self._load_credentials_by_api_key(api_key)
27
+ elif cost_attribution is not None:
28
+ self._load_credentials_by_cost_attribution(cost_attribution)
29
+ else:
30
+ raise ValueError("Either api_key or cost_attribution must be provided")
31
+
32
+ # Initialize API URLs
33
+ self._initialize_urls()
34
+
35
+ def _get_config_path(self, filename):
36
+ """Get the full path to a configuration file."""
37
+ dir_path = os.path.dirname(os.path.abspath(__file__))
38
+ return os.path.join(dir_path, 'config', filename)
39
+
40
+ def _load_json_config(self, filename):
41
+ """Load and parse a JSON configuration file."""
42
+ config_path = self._get_config_path(filename)
43
+ try:
44
+ with open(config_path, 'r', encoding='utf-8') as f:
45
+ return json.load(f)
46
+ except FileNotFoundError:
47
+ raise FileNotFoundError(f"Configuration file not found: {config_path}")
48
+ except json.JSONDecodeError as e:
49
+ raise ValueError(f"Invalid JSON in configuration file {filename}: {e}")
50
+
51
+ def _validate_credentials(self):
52
+ """Validate that required credentials are present and non-empty."""
53
+ if not hasattr(self, 'api_key') or not self.api_key:
54
+ raise ValueError("api_key is missing or empty")
55
+ if not hasattr(self, 'api_secret') or not self.api_secret:
56
+ raise ValueError("api_secret is missing or empty")
57
+
58
+ def _load_credentials_by_api_key(self, api_key):
59
+ """Load credentials using direct API key lookup."""
60
+ self.api_key = api_key
61
+ data = self._load_json_config('ak_sk_mapping.json')
62
+ self.api_secret = data.get(api_key, "")
63
+ self._validate_credentials()
64
+
65
+ def _load_credentials_by_cost_attribution(self, cost_attribution):
66
+ """Load credentials using cost attribution lookup."""
67
+ data = self._load_json_config('ai_flow_ak_sk_mapping.json')
68
+ credentials = data.get(cost_attribution, {})
69
+ self.api_key = credentials.get("ak", "")
70
+ self.api_secret = credentials.get("sk", "")
71
+ self.token = credentials.get("token", "")
72
+ self._validate_credentials()
73
+
74
+ def _initialize_urls(self):
75
+ """Initialize API URLs with credentials."""
76
+ base_url = "https://openapi.mtlab.meitu.com/v1"
77
+ auth_params = f"api_key={self.api_key}&api_secret={self.api_secret}"
78
+
79
+ # 用于异步接口的结果获取
80
+ self.query_url = f"{base_url}/query?{auth_params}"
81
+ # API endpoint URL
82
+ self.url = f"{base_url}/{self.api_name}?{auth_params}"
83
+
84
+ def fetch_response(self, msg_id):
85
+ url = f"{self.query_url}&msg_id={msg_id}"
86
+ headers = {"Content-Type": "application/json"}
87
+ data={"msg_id": msg_id}
88
+ response = requests.post(url, json=data, headers=headers)
89
+ response.raise_for_status() # Raise HTTP errors
90
+ return response.json()
91
+
92
+ @retry(
93
+ stop=stop_after_delay(1000), # Stop after 100 seconds
94
+ wait=wait_fixed(1), # Wait 1 second between retries
95
+ retry=retry_if_result(lambda res: res.get("error_code") != 0 and res.get("error_code") != 2) # Retry if error_code != 0
96
+ )
97
+ def get_res(self, msg_id):
98
+ """
99
+ Fetch the result with automatic retries and timeout.
100
+ """
101
+ return self.fetch_response(msg_id)
102
+
103
+ def async_request(self, data: dict, max_retries: int = 20):
104
+ """
105
+ 发起异步请求并轮询获取结果
106
+
107
+ Args:
108
+ data (dict): 请求数据体,需包含图片URL等必要参数
109
+ 注意:异步接口仅支持图片URL格式,不支持base64编码图片
110
+ max_retries (int): 最大重试次数,默认20次
111
+
112
+ Returns:
113
+ dict: 通过msg_id轮询获取的最终处理结果
114
+
115
+ 流程说明:
116
+ 1. 发送POST请求获取msg_id
117
+ 2. 通过msg_id轮询获取最终响应数据
118
+ """
119
+ headers = {"Content-Type": "application/json"}
120
+
121
+ for attempt in range(max_retries):
122
+ try:
123
+ response = requests.post(self.url, json=data, headers=headers)
124
+ response.raise_for_status()
125
+
126
+ msg_id = response.json().get("msg_id", "")
127
+ if not msg_id:
128
+ continue
129
+
130
+ result = self.get_res(msg_id=msg_id)
131
+ if result.get("error_code", 0) == 0:
132
+ return result
133
+
134
+ except requests.RequestException:
135
+ if attempt == max_retries - 1:
136
+ raise
137
+ continue
138
+
139
+ return result if 'result' in locals() else {"error_code": -1, "error_msg": "Max retries exceeded"}
140
+
141
+ def request(self, data: dict):
142
+ """
143
+ 发起同步请求并返回即时响应
144
+
145
+ Params:
146
+ data (dict): 请求数据体,可包含base64编码的图片数据
147
+ 注意:同步接口支持base64编码图片,异步接口需使用URL
148
+
149
+ Return:
150
+ dict: 原始响应JSON数据
151
+ """
152
+ # 异步接口只支持输入图片url, 同步接口才支持图片base64
153
+ headers = {"Content-Type": "application/json"}
154
+ response = requests.post(self.url, json=data, headers=headers)
155
+ response.raise_for_status() # Raise HTTP errors
156
+ return response.json()