MeUtils 2025.4.9.10.39.6__py3-none-any.whl → 2025.4.11.17.37.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {MeUtils-2025.4.9.10.39.6.dist-info → MeUtils-2025.4.11.17.37.3.dist-info}/METADATA +266 -266
  2. {MeUtils-2025.4.9.10.39.6.dist-info → MeUtils-2025.4.11.17.37.3.dist-info}/RECORD +40 -37
  3. examples/_openaisdk/openai_google.py +38 -31
  4. meutils/apis/{google_apis → google}/audios.py +5 -2
  5. meutils/apis/google/chat.py +372 -0
  6. meutils/apis/google/files.py +29 -0
  7. meutils/apis/{google_apis → google}/google2openai.py +8 -4
  8. meutils/apis/google/images.py +27 -0
  9. meutils/apis/{google_apis → google}/search.py +7 -5
  10. meutils/apis/jimeng/images.py +41 -8
  11. meutils/apis/search/metaso.py +2 -2
  12. meutils/apis/siliconflow/images.py +5 -3
  13. meutils/caches/acache.py +1 -1
  14. meutils/common.py +1 -0
  15. meutils/data/VERSION +1 -1
  16. meutils/decorators/catch.py +0 -8
  17. meutils/decorators/common.py +86 -19
  18. meutils/decorators/contextmanagers.py +103 -14
  19. meutils/decorators/demo.py +76 -14
  20. meutils/io/files_utils.py +2 -3
  21. meutils/io/openai_files.py +11 -6
  22. meutils/llm/check_utils.py +20 -1
  23. meutils/llm/openai_polling/__init__.py +11 -0
  24. meutils/llm/openai_polling/chat.py +45 -0
  25. meutils/{apis/google_apis → llm/openai_polling}/images.py +4 -2
  26. meutils/llm/openai_utils/common.py +1 -1
  27. meutils/notice/feishu.py +5 -2
  28. meutils/schemas/image_types.py +26 -3
  29. meutils/schemas/oneapi/common.py +12 -0
  30. meutils/schemas/openai_types.py +10 -0
  31. meutils/serving/fastapi/dependencies/__init__.py +4 -1
  32. meutils/serving/fastapi/dependencies/auth.py +10 -6
  33. meutils/serving/fastapi/exceptions/http_error.py +2 -2
  34. meutils/str_utils/__init__.py +30 -2
  35. meutils/apis/google_apis/common.py +0 -243
  36. meutils/apis/google_apis/files.py +0 -19
  37. {MeUtils-2025.4.9.10.39.6.dist-info → MeUtils-2025.4.11.17.37.3.dist-info}/LICENSE +0 -0
  38. {MeUtils-2025.4.9.10.39.6.dist-info → MeUtils-2025.4.11.17.37.3.dist-info}/WHEEL +0 -0
  39. {MeUtils-2025.4.9.10.39.6.dist-info → MeUtils-2025.4.11.17.37.3.dist-info}/entry_points.txt +0 -0
  40. {MeUtils-2025.4.9.10.39.6.dist-info → MeUtils-2025.4.11.17.37.3.dist-info}/top_level.txt +0 -0
  41. /meutils/apis/{google_apis → google}/__init__.py +0 -0
  42. /meutils/apis/{google_apis → google}/gemini_sdk.py +0 -0
@@ -8,7 +8,6 @@
8
8
  # @Software : PyCharm
9
9
  # @Description :
10
10
  from meutils.pipe import *
11
- from meutils.log_utils import logger4wecom
12
11
 
13
12
 
14
13
  def wecom_hook(title='Task Done', text=None, hook_url=None):
@@ -30,12 +29,6 @@ def wecom_hook(title='Task Done', text=None, hook_url=None):
30
29
 
31
30
  logger.info(f"{title} done in {mins} m")
32
31
 
33
- logger4wecom(
34
- title=title,
35
- text=f"**{wrapped.__name__}:** {r if text is None else text}\n耗时 {mins} m",
36
- hook_url=hook_url
37
- )
38
-
39
32
  return r
40
33
 
41
34
  return wrapper
@@ -54,7 +47,6 @@ def wecom_catch(hook_url=None, more_info=True):
54
47
  {info.strip()}
55
48
  ```
56
49
  """.strip()
57
- logger4wecom(wrapped.__name__, text, hook_url)
58
50
 
59
51
  return wrapper
60
52
 
@@ -7,19 +7,20 @@
7
7
  # @WeChat : 313303303
8
8
  # @Software : PyCharm
9
9
  # @Description :
10
- import importlib
11
10
  import os
12
11
  import sys
13
12
  import time
14
- from functools import wraps
15
-
13
+ import wrapt
14
+ import asyncio
15
+ import inspect
16
16
  import schedule
17
17
  import threading
18
18
  import traceback
19
- import wrapt
19
+ import importlib
20
20
 
21
21
  from loguru import logger
22
22
  from tqdm.auto import tqdm
23
+ from functools import wraps
23
24
  from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
24
25
  from contextlib import contextmanager, asynccontextmanager
25
26
  from typing import *
@@ -148,14 +149,6 @@ def do_nothing(func, *args, **kwargs):
148
149
  return func(*args, **kwargs)
149
150
 
150
151
 
151
- @decorator
152
- def try_catch(func, is_trace=False, *args, **kwargs):
153
- try:
154
- return func(*args, **kwargs)
155
- except Exception as e:
156
- return {'error': traceback.format_exc().strip() if is_trace else e}
157
-
158
-
159
152
  @decorator
160
153
  def timeout(func, seconds=1, *args, **kwargs):
161
154
  future = ThreadPoolExecutor(1).submit(func, *args, **kwargs)
@@ -398,12 +391,64 @@ def limit(limit_value: str = '1/second', key_func: Callable[..., str] = None, er
398
391
  return inner
399
392
 
400
393
 
394
+ def try_catch(
395
+ task: Optional[str] = None,
396
+ callback: Optional[Callable] = None,
397
+ is_trace: bool = False,
398
+ ):
399
+ from meutils.notice.feishu import send_message_for_try_catch
400
+
401
+ callback = callback or send_message_for_try_catch # logger.error
402
+
403
+ def decorator(func):
404
+ @wraps(func)
405
+ def wrapper(*args, **kwargs):
406
+ task_name = task or f"{func.__module__}.{func.__name__}"
407
+
408
+ data = {
409
+ "task_name": task_name,
410
+ # "args": args,
411
+ # "kwargs": kwargs,
412
+ # "defined_args": inspect.getfullargspec(func).args, # .varargs
413
+ }
414
+
415
+ logger.debug(data)
416
+
417
+ async def async_wrapper():
418
+ try:
419
+ if asyncio.iscoroutinefunction(func):
420
+ return await func(*args, **kwargs)
421
+ else:
422
+ return func(*args, **kwargs)
423
+ except Exception as e:
424
+
425
+ if is_trace:
426
+ error_msg = f"{task_name}:\n{traceback.format_exc()}"
427
+ else:
428
+ error_msg = f"{task_name}: {e}"
429
+
430
+ data["error"] = error_msg
431
+ callback(data)
432
+
433
+ raise
434
+
435
+ if asyncio.iscoroutinefunction(func):
436
+ return async_wrapper()
437
+ else:
438
+ try:
439
+ return asyncio.get_event_loop().run_until_complete(async_wrapper())
440
+ except RuntimeError:
441
+ # 如果没有事件循环(例如在同步环境中),则直接运行
442
+ return async_wrapper()
443
+
444
+ return wrapper
445
+
446
+ return decorator
401
447
 
402
448
 
403
449
  if __name__ == '__main__':
404
450
  import time
405
451
 
406
-
407
452
  # @timeout()
408
453
  # def ff():
409
454
  # import time
@@ -476,11 +521,33 @@ if __name__ == '__main__':
476
521
  # while 1:
477
522
  # pass
478
523
 
479
- def f():
480
- return 1 / 0
481
- # return 1
524
+ # def f():
525
+ # return 1 / 0
526
+ # # return 1
527
+ #
528
+ #
529
+ # with tryer('try'):
530
+ # a = f()
531
+ # print(a)
532
+
533
+ from meutils.pipe import *
534
+ from meutils.notice.feishu import send_message_for_try_catch
535
+
536
+
537
+ @try_catch(is_trace=False)
538
+ async def my_async_function(x=1, y=1):
539
+ # 您的异步函数代码
540
+ pass
541
+ 1 / 0
482
542
 
483
543
 
484
- with tryer('try'):
485
- a = f()
486
- print(a)
544
+ async def main():
545
+ await my_async_function(x=111, y=10000)
546
+
547
+
548
+ # @async_try_catch(task="Custom Task Name", fallback=some_fallback_function)
549
+ # async def another_async_function():
550
+ # # 另一个异步函数的代码
551
+ # pass
552
+
553
+ arun(main())
@@ -9,6 +9,7 @@
9
9
  # @Description :
10
10
 
11
11
  from meutils.pipe import *
12
+ from meutils.notice.feishu import send_message_for_try_catch
12
13
 
13
14
  from contextlib import contextmanager, asynccontextmanager
14
15
  from concurrent.futures import ThreadPoolExecutor, as_completed, TimeoutError
@@ -53,12 +54,13 @@ async def atry_catcher(task="Task", fallback: Callable = None, is_trace: bool =
53
54
  except Exception as e:
54
55
  error = traceback.format_exc() if is_trace else e
55
56
  logger.error(f"{task}: {error}")
57
+
56
58
  if fallback:
57
59
  yield await fallback()
58
60
 
59
61
 
60
62
  @contextmanager
61
- def timeout_task_executor(timeout: float=3, max_workers: int = None):
63
+ def timeout_task_executor(timeout: float = 3, max_workers: int = None):
62
64
  """
63
65
  一个上下文管理器,用于执行任务并设置超时时间。
64
66
  :param timeout: 超时时间(秒)。
@@ -85,6 +87,90 @@ def timeout_task_executor(timeout: float=3, max_workers: int = None):
85
87
  executor.shutdown(wait=False) # 不等待未完成的任务,直接关闭
86
88
 
87
89
 
90
+ @asynccontextmanager
91
+ async def atry_catcher(task="Task", fallback: Callable = None, is_trace: bool = False):
92
+ try:
93
+ yield
94
+ except Exception as e:
95
+ error = traceback.format_exc() if is_trace else e
96
+ logger.error(f"{task}: {error}")
97
+ if fallback:
98
+ yield await fallback()
99
+
100
+
101
+ @asynccontextmanager
102
+ async def atry_catch(
103
+ task: Optional[str] = None,
104
+ callback: Optional[Callable] = None,
105
+ is_trace: bool = False,
106
+ **kwargs,
107
+ ):
108
+ callback = callback or send_message_for_try_catch
109
+
110
+ task_name = task or "Unnamed TryCatch Task"
111
+
112
+ data = {
113
+ "task_name": task_name,
114
+ }
115
+ for k, v in kwargs.items():
116
+ if isinstance(v, BaseModel):
117
+ v = v.model_dump(exclude_none=True)
118
+ data[k] = v
119
+
120
+ # logger.debug(data)
121
+
122
+ try:
123
+ yield
124
+ except Exception as e:
125
+ if is_trace:
126
+ error_msg = f"""{traceback.format_exc()}"""
127
+ else:
128
+ error_msg = f"{e}"
129
+
130
+ data["error"] = error_msg
131
+
132
+ callback(data)
133
+ raise
134
+
135
+
136
+ @contextmanager
137
+ def try_catch(
138
+ task: Optional[str] = None,
139
+ callback: Optional[Callable] = None,
140
+ is_trace: bool = False,
141
+ **kwargs,
142
+ ):
143
+ callback = callback or send_message_for_try_catch
144
+
145
+ task_name = task or "Unnamed TryCatch Task"
146
+
147
+ data = {
148
+ "task_name": task_name,
149
+ }
150
+ for k, v in kwargs.items():
151
+ if isinstance(v, BaseModel):
152
+ v = v.model_dump(exclude_none=True)
153
+ elif isinstance(v, typing.Mapping):
154
+ v = dict(v)
155
+
156
+ data[k] = v
157
+
158
+ logger.debug(data)
159
+
160
+ try:
161
+ yield
162
+ except Exception as e:
163
+ if is_trace:
164
+ error_msg = f"""{traceback.format_exc()}"""
165
+ else:
166
+ error_msg = f"{e}"
167
+
168
+ data["error"] = error_msg
169
+
170
+ callback(data)
171
+ raise e
172
+
173
+
88
174
  if __name__ == '__main__':
89
175
  # async def f():
90
176
  # return 1/0
@@ -93,16 +179,19 @@ if __name__ == '__main__':
93
179
  # with try_catcher("test"):
94
180
  # arun(f())
95
181
 
96
- def example_task():
97
- print("Starting task...")
98
- time.sleep(4) # 模拟耗时任务
99
- print("Task completed!")
100
- return "Done"
101
-
102
-
103
- with timeout_task_executor(timeout=3) as execute:
104
- try:
105
- result = execute(example_task)
106
- print(f"Task result: {result}")
107
- except TimeoutError:
108
- print("Task did not complete in time.")
182
+ # def example_task():
183
+ # print("Starting task...")
184
+ # time.sleep(4) # 模拟耗时任务
185
+ # print("Task completed!")
186
+ # return "Done"
187
+ #
188
+ #
189
+ # with timeout_task_executor(timeout=3) as execute:
190
+ # try:
191
+ # result = execute(example_task)
192
+ # print(f"Task result: {result}")
193
+ # except TimeoutError:
194
+ # print("Task did not complete in time.")
195
+
196
+ with try_catch("test"):
197
+ 1 / 0
@@ -7,26 +7,88 @@
7
7
  # @WeChat : 313303303
8
8
  # @Software : PyCharm
9
9
  # @Description :
10
+ import asyncio
11
+ import traceback
12
+ from typing import Optional, Callable
13
+ from contextlib import asynccontextmanager, contextmanager
10
14
 
15
+ from meutils.notice.feishu import send_message_for_try_catch
11
16
 
12
- from meutils.decorators.feishu import *
13
17
 
14
- from meutils.decorators.retry import retrying
15
- import asyncio
18
+ @asynccontextmanager
19
+ async def atry_catch(
20
+ task: Optional[str] = None,
21
+ callback: Optional[Callable] = None,
22
+ is_trace: bool = False,
23
+ ):
24
+ callback = callback or send_message_for_try_catch
25
+
26
+ task_name = task or "Unnamed TryCatch Task"
27
+
28
+ data = {
29
+ "task_name": task_name,
30
+ }
31
+
32
+ # logger.debug(data)
33
+
34
+ try:
35
+ yield
36
+ except Exception as e:
37
+ if is_trace:
38
+ error_msg = f"""{task_name}:\n{traceback.format_exc()}"""
39
+ else:
40
+ error_msg = f"{task_name}: {e}"
41
+
42
+ data["error"] = error_msg
43
+
44
+ callback(data)
45
+ raise
46
+
16
47
 
17
- import openai
48
+ @contextmanager
49
+ def try_catch(
50
+ task: Optional[str] = None,
51
+ callback: Optional[Callable] = None,
52
+ is_trace: bool = False,
53
+ ):
54
+ callback = callback or send_message_for_try_catch
18
55
 
19
- @retrying
20
- async def my_coroutine_function():
21
- # 在这里执行协程函数的逻辑
22
- # 如果发生异常,tenacity将自动重试
23
- await asyncio.sleep(1)
24
- raise Exception("Something went wrong")
56
+ task_name = task or "Unnamed TryCatch Task"
57
+
58
+ data = {
59
+ "task_name": task_name,
60
+ }
61
+
62
+ # logger.debug(data)
25
63
 
26
- async def main():
27
64
  try:
28
- await my_coroutine_function()
65
+ yield
29
66
  except Exception as e:
30
- print(f"Exception: {e}")
67
+ if is_trace:
68
+ error_msg = f"""{task_name}:\n{traceback.format_exc()}"""
69
+ else:
70
+ error_msg = f"{task_name}: {e}"
71
+
72
+ data["error"] = error_msg
73
+
74
+ callback(data)
75
+ raise
76
+
77
+
78
+ if __name__ == '__main__':
79
+ from meutils.pipe import *
80
+
81
+
82
+ async def f():
83
+ return 1
84
+
85
+
86
+ async def main():
87
+ # with atry_catch_context(task="test", is_trace=True):
88
+ # 1 / 0
89
+ with try_catch(task="test", is_trace=True):
90
+ logger.debug(await f())
91
+ 1 / 0
92
+
31
93
 
32
- asyncio.run(main())
94
+ asyncio.run(main())
meutils/io/files_utils.py CHANGED
@@ -50,7 +50,6 @@ def base64_to_bytes(base64_image_string):
50
50
 
51
51
 
52
52
  @retrying()
53
- @rcache(ttl=300, serializer='pickle') # todo: UploadFile不一定兼容
54
53
  async def to_bytes(
55
54
  file: Union[UploadFile, str, bytes],
56
55
  headers: Optional[dict] = None
@@ -79,12 +78,12 @@ async def to_bytes(
79
78
  resp = await cilent.get(file)
80
79
  file_bytes = resp.content
81
80
 
82
- elif isinstance(file, str) and len(file) > 256: # base64
81
+ elif isinstance(file, str) and len(file) > 1024: # base64
83
82
  logger.debug(f"FileType: BASE64")
84
83
 
85
84
  file_bytes = base64_to_bytes(file)
86
85
 
87
- elif isinstance(file, str) and len(file) < 256 and Path(file).is_file(): # file
86
+ elif isinstance(file, str) and len(file) < 1024 and Path(file).is_file(): # file
88
87
  logger.debug(f"FileType: PATH")
89
88
 
90
89
  file_bytes = Path(file).read_bytes()
@@ -50,13 +50,15 @@ async def delete_files(client, threshold: int = 666):
50
50
 
51
51
 
52
52
  @rcache(ttl=7 * 24 * 3600)
53
- async def _file_extract(file):
53
+ async def file_extract(file):
54
54
  """
55
55
 
56
56
  :param file: url bytes path
57
57
  :return:
58
58
  """
59
59
  # url
60
+ if isinstance(file, list):
61
+ return await asyncio.gather(*map(file_extract, file))
60
62
 
61
63
  filename = Path(file).name if isinstance(file, str) else 'untitled'
62
64
  mime_type = guess_mime_type(file)
@@ -108,12 +110,14 @@ async def _file_extract(file):
108
110
  return data
109
111
 
110
112
 
111
- async def file_extract(files):
112
- if isinstance(files, str):
113
- return await _file_extract(files)
113
+ # http://admin.ilovechatgpt.top/file/boshihouyanjiurenyuankaitipingshenbiaochugaofanyidocx_88256801.docx
114
114
 
115
- tasks = [_file_extract(file) for file in files]
116
- return await asyncio.gather(*tasks)
115
+ # async def file_extract(files):
116
+ # if isinstance(files, str):
117
+ # return await _file_extract(files)
118
+ #
119
+ # tasks = [_file_extract(file) for file in files]
120
+ # return await asyncio.gather(*tasks)
117
121
 
118
122
 
119
123
  # FileObject(id='1741136989_8dd96cbee6274251b7e4c9568779bd6a', bytes=82947, created_at=1741136989, filename='kling_watermark.png', object='file', status=None, status_details=None)
@@ -153,6 +157,7 @@ if __name__ == '__main__':
153
157
  file = "http://admin.ilovechatgpt.top/file/yuzhicaizaibutongnianlingquntixiaofeixingweijishichangdiaochawenjuanweishanjianbanpptx_59787479.pptx"
154
158
  file = "https://oss.ffire.cc/files/百炼系列手机产品介绍.docx"
155
159
  # file = "https://app.yinxiang.com/fx/8b8bba1e-b254-40ff-81e1-fa3427429efe"
160
+ file = "http://admin.ilovechatgpt.top/file/boshihouyanjiurenyuankaitipingshenbiaochugaofanyidocx_88256801.docx"
156
161
 
157
162
  print(guess_mime_type(file))
158
163
 
@@ -130,6 +130,25 @@ async def check_token_for_moonshot(api_key, threshold: float = 0):
130
130
  return False
131
131
 
132
132
 
133
+ @retrying()
134
+ async def check_token_for_gemini(api_key):
135
+ if not isinstance(api_key, str):
136
+ return await check_tokens(api_key, check_token_for_gemini)
137
+ try:
138
+ client = AsyncOpenAI(
139
+ api_key=api_key,
140
+ base_url=os.getenv("GOOGLE_BASE_URL"),
141
+ )
142
+ await client.models.list()
143
+ return True
144
+ except TimeoutException as e:
145
+ raise
146
+
147
+ except Exception as e:
148
+ logger.error(f"Error: {e}\n{api_key}")
149
+ return False
150
+
151
+
133
152
  if __name__ == '__main__':
134
153
  from meutils.config_utils.lark_utils import get_next_token_for_polling
135
154
 
@@ -149,4 +168,4 @@ if __name__ == '__main__':
149
168
  arun(check_token_for_siliconflow("sk-vpeietyomqjvizlzfuztzthggcqvutowgbmhjggsmwuhsomg"))
150
169
  "https://xchatllm.feishu.cn/sheets/Bmjtst2f6hfMqFttbhLcdfRJnNf?sheet=79272d"
151
170
 
152
- # arun(check_token_for_moonshot("sk-Qnr87vtf2Q6MEfc2mVNkVZ4qaoZg3smH9527I25QgcFe7HrT"))
171
+ # arun(check_token_for_moonshot("sk-Qnr87vtf2Q6MEfc2mVNkVZ4qaoZg3smH9527I25QgcFe7HrT"))
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # @Project : AI. @by PyCharm
4
+ # @File : __init__.py
5
+ # @Time : 2025/4/9 11:23
6
+ # @Author : betterme
7
+ # @WeChat : meutils
8
+ # @Software : PyCharm
9
+ # @Description :
10
+
11
+ from meutils.pipe import *
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # @Project : AI. @by PyCharm
4
+ # @File : chat
5
+ # @Time : 2025/4/10 16:06
6
+ # @Author : betterme
7
+ # @WeChat : meutils
8
+ # @Software : PyCharm
9
+ # @Description :
10
+
11
+ from meutils.pipe import *
12
+ from meutils.llm.clients import AsyncOpenAI
13
+ from meutils.llm.openai_utils import to_openai_params
14
+
15
+ from meutils.schemas.openai_types import CompletionRequest
16
+
17
+
18
+ class Completions(object):
19
+
20
+ def __init__(self, base_url: Optional[str] = None, api_key: Optional[str] = None):
21
+ self.client = AsyncOpenAI(base_url=base_url, api_key=api_key)
22
+
23
+ async def create(self, request: CompletionRequest):
24
+ data = to_openai_params(request)
25
+ if 'gemini' in request.model:
26
+ data.pop("seed", None)
27
+ data.pop("presence_penalty", None)
28
+ data.pop("frequency_penalty", None)
29
+ data.pop("extra_body", None)
30
+
31
+ return await self.client.chat.completions.create(**data)
32
+
33
+
34
+ if __name__ == '__main__':
35
+ # 测试
36
+
37
+ request = CompletionRequest(
38
+ model="gpt-4o-mini",
39
+ messages=[
40
+ {"role": "system", "content": "你是一个助手"},
41
+ {"role": "user", "content": "你好"}
42
+ ],
43
+ stream=False
44
+ )
45
+ arun(Completions().create(request))
@@ -2,10 +2,12 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  # @Project : AI. @by PyCharm
4
4
  # @File : images
5
- # @Time : 2025/4/7 13:07
5
+ # @Time : 2025/4/10 16:07
6
6
  # @Author : betterme
7
7
  # @WeChat : meutils
8
8
  # @Software : PyCharm
9
- # @Description : D3
9
+ # @Description :
10
10
 
11
11
  from meutils.pipe import *
12
+
13
+
@@ -193,7 +193,7 @@ async def create_chat_completion_chunk(
193
193
  Iterator[Union[str, ChatCompletionChunk]],
194
194
  AsyncIterator[Union[str, ChatCompletionChunk]]
195
195
  ],
196
- redirect_model: str = ' ',
196
+ redirect_model: str = ' ', # todo: response_model
197
197
  chat_id: Optional[str] = None
198
198
  ):
199
199
  """ todo: 替换 前缀
meutils/notice/feishu.py CHANGED
@@ -108,8 +108,11 @@ def catch(
108
108
  send_message_for_images = partial(send_message, url=IMAGES)
109
109
  send_message_for_kling = partial(send_message, url=KLING)
110
110
 
111
- httpexception_feishu_url = "https://open.feishu.cn/open-apis/bot/v2/hook/d1c7b67d-b0f8-4067-a2f5-109f20eeb696"
112
- send_message_for_httpexception = partial(send_message, url=httpexception_feishu_url)
111
+ http_feishu_url = "https://open.feishu.cn/open-apis/bot/v2/hook/d1c7b67d-b0f8-4067-a2f5-109f20eeb696"
112
+ send_message_for_http = partial(send_message, url=http_feishu_url)
113
+
114
+ try_catch_feishu_url = "https://open.feishu.cn/open-apis/bot/v2/hook/887fe4d3-8bcd-4cfb-bac9-62f776091ca2"
115
+ send_message_for_try_catch = partial(send_message, url=try_catch_feishu_url)
113
116
 
114
117
  if __name__ == '__main__':
115
118
  # send_message("xxx", title=None)
@@ -9,6 +9,7 @@
9
9
  # @Description : todo: 通用比例适配
10
10
 
11
11
  from meutils.pipe import *
12
+ from meutils.str_utils.regular_expression import parse_url
12
13
 
13
14
  from pydantic import constr
14
15
  from openai.types import ImagesResponse as _ImagesResponse, Image
@@ -43,7 +44,7 @@ ASPECT_RATIOS = {
43
44
  class ImagesResponse(_ImagesResponse):
44
45
  created: int = Field(default_factory=lambda: int(time.time()))
45
46
 
46
- data: Optional[List[Union[Image, dict]]] = None
47
+ data: Optional[List[Union[Image, dict]]] = []
47
48
 
48
49
  image: Optional[Union[str, List[str]]] = None
49
50
 
@@ -99,6 +100,20 @@ class ImageRequest(BaseModel): # openai
99
100
  if self.size:
100
101
  self.size = self.size if 'x' in self.size else '512x512'
101
102
 
103
+ @cached_property
104
+ def image_and_prompt(self): # image prompt 目前是单图
105
+ if self.prompt.startswith('http') and (prompts := self.prompt.split(maxsplit=1)):
106
+ if len(prompts) == 2:
107
+ return prompts
108
+ else:
109
+ return prompts + [' ']
110
+
111
+ elif "http" in self.prompt and (images := parse_url(self.prompt)):
112
+ return images[0], self.prompt.replace(images[0], "")
113
+
114
+ else:
115
+ return None, self.prompt
116
+
102
117
  class Config:
103
118
  extra = "allow"
104
119
 
@@ -502,5 +517,13 @@ if __name__ == '__main__':
502
517
 
503
518
  # print(ImagesResponse(data=[{'url': 1}]))
504
519
 
505
- print(RecraftImageRequest(prompt="").model_dump_json())
506
- print(RecraftImageRequest(prompt=""))
520
+ # print(RecraftImageRequest(prompt="").model_dump_json())
521
+ # print(RecraftImageRequest(prompt=""))
522
+
523
+ prompt = "https://oss.ffire.cc/files/kling_watermark.png 带个眼镜"
524
+ # prompt = "带个眼镜 https://oss.ffire.cc/files/kling_watermark.png"
525
+ prompt = "https://oss.ffire.cc/files/kling_watermark.png"
526
+ prompt = "画条狗"
527
+
528
+ request = ImageRequest(prompt=prompt)
529
+ print(request.image_and_prompt)