gomyck-tools 1.0.0__py3-none-any.whl → 1.4.7__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 (107) hide show
  1. ctools/__init__.py +21 -0
  2. ctools/ai/__init__.py +4 -0
  3. ctools/ai/llm_chat.py +184 -0
  4. ctools/ai/llm_client.py +163 -0
  5. ctools/ai/llm_exception.py +17 -0
  6. ctools/ai/mcp/__init__.py +4 -0
  7. ctools/ai/mcp/mcp_client.py +326 -0
  8. ctools/ai/tools/__init__.py +4 -0
  9. ctools/ai/tools/json_extract.py +202 -0
  10. ctools/ai/tools/quick_tools.py +149 -0
  11. ctools/ai/tools/think_process.py +11 -0
  12. ctools/ai/tools/tool_use_xml_parse.py +40 -0
  13. ctools/ai/tools/xml_extract.py +15 -0
  14. ctools/application.py +50 -47
  15. ctools/aspect.py +65 -0
  16. ctools/auto/__init__.py +4 -0
  17. ctools/{browser_element_tools.py → auto/browser_element.py} +18 -8
  18. ctools/{plan_area_tools.py → auto/plan_area.py} +5 -7
  19. ctools/{pty_tools.py → auto/pty_process.py} +6 -3
  20. ctools/{resource_bundle_tools.py → auto/resource_bundle.py} +4 -4
  21. ctools/{screenshot_tools.py → auto/screenshot.py} +7 -6
  22. ctools/{win_canvas.py → auto/win_canvas.py} +10 -4
  23. ctools/{win_control.py → auto/win_control.py} +14 -5
  24. ctools/call.py +34 -49
  25. ctools/cdate.py +84 -0
  26. ctools/cdebug.py +146 -0
  27. ctools/cid.py +20 -0
  28. ctools/cipher/__init__.py +4 -0
  29. ctools/{aes_tools.py → cipher/aes_util.py} +10 -0
  30. ctools/{b64.py → cipher/b64.py} +2 -0
  31. ctools/cipher/czip.py +133 -0
  32. ctools/cipher/rsa.py +75 -0
  33. ctools/{sign.py → cipher/sign.py} +2 -1
  34. ctools/{sm_tools.py → cipher/sm_util.py} +20 -4
  35. ctools/cjson.py +10 -10
  36. ctools/cron_lite.py +109 -97
  37. ctools/database/__init__.py +4 -0
  38. ctools/{database.py → database/database.py} +93 -26
  39. ctools/dict_wrapper.py +21 -0
  40. ctools/ex.py +4 -0
  41. ctools/geo/__init__.py +4 -0
  42. ctools/geo/coord_trans.py +127 -0
  43. ctools/geo/douglas_rarefy.py +139 -0
  44. ctools/metrics.py +56 -63
  45. ctools/office/__init__.py +4 -0
  46. ctools/office/cword.py +30 -0
  47. ctools/{word_fill.py → office/word_fill.py} +3 -6
  48. ctools/patch.py +88 -0
  49. ctools/{work_path.py → path_info.py} +34 -2
  50. ctools/pkg/__init__.py +4 -0
  51. ctools/pkg/dynamic_imp.py +38 -0
  52. ctools/pools/__init__.py +4 -0
  53. ctools/pools/process_pool.py +41 -0
  54. ctools/{thread_pool.py → pools/thread_pool.py} +13 -4
  55. ctools/similar.py +25 -0
  56. ctools/stream/__init__.py +4 -0
  57. ctools/stream/ckafka.py +164 -0
  58. ctools/stream/credis.py +127 -0
  59. ctools/{mqtt_utils.py → stream/mqtt_utils.py} +20 -12
  60. ctools/sys_info.py +36 -13
  61. ctools/sys_log.py +46 -27
  62. ctools/util/__init__.py +4 -0
  63. ctools/util/cftp.py +76 -0
  64. ctools/util/cklock.py +118 -0
  65. ctools/util/config_util.py +52 -0
  66. ctools/util/env_config.py +63 -0
  67. ctools/{html_soup.py → util/html_soup.py} +0 -7
  68. ctools/{http_utils.py → util/http_util.py} +4 -2
  69. ctools/{images_tools.py → util/image_process.py} +10 -1
  70. ctools/util/jb_cut.py +54 -0
  71. ctools/{id_worker_tools.py → util/snow_id.py} +8 -23
  72. ctools/web/__init__.py +4 -0
  73. ctools/web/aio_web_server.py +186 -0
  74. ctools/web/api_result.py +56 -0
  75. ctools/web/bottle_web_base.py +239 -0
  76. ctools/web/bottle_webserver.py +191 -0
  77. ctools/web/bottle_websocket.py +79 -0
  78. ctools/web/ctoken.py +103 -0
  79. ctools/{download_tools.py → web/download_util.py} +14 -12
  80. ctools/web/params_util.py +46 -0
  81. ctools/{upload_tools.py → web/upload_util.py} +3 -2
  82. gomyck_tools-1.4.7.dist-info/METADATA +70 -0
  83. gomyck_tools-1.4.7.dist-info/RECORD +88 -0
  84. {gomyck_tools-1.0.0.dist-info → gomyck_tools-1.4.7.dist-info}/WHEEL +1 -1
  85. gomyck_tools-1.4.7.dist-info/licenses/LICENSE +13 -0
  86. ctools/bashPath.py +0 -13
  87. ctools/bottle_server.py +0 -49
  88. ctools/console.py +0 -55
  89. ctools/date_utils.py +0 -44
  90. ctools/enums.py +0 -4
  91. ctools/excelOpt.py +0 -36
  92. ctools/imgDialog.py +0 -44
  93. ctools/license.py +0 -37
  94. ctools/log.py +0 -28
  95. ctools/mvc.py +0 -56
  96. ctools/obj.py +0 -20
  97. ctools/pacth.py +0 -73
  98. ctools/ssh.py +0 -9
  99. ctools/strDiff.py +0 -20
  100. ctools/string_tools.py +0 -101
  101. ctools/token_tools.py +0 -13
  102. ctools/wordFill.py +0 -24
  103. gomyck_tools-1.0.0.dist-info/METADATA +0 -20
  104. gomyck_tools-1.0.0.dist-info/RECORD +0 -54
  105. /ctools/{word_fill_entity.py → office/word_fill_entity.py} +0 -0
  106. /ctools/{compile_tools.py → util/compile_util.py} +0 -0
  107. {gomyck_tools-1.0.0.dist-info → gomyck_tools-1.4.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/5/16 16:49'
5
+
6
+ import asyncio
7
+ import json
8
+ import os
9
+ import shutil
10
+ from contextlib import AsyncExitStack, asynccontextmanager
11
+ from typing import Any
12
+
13
+ from mcp import ClientSession, StdioServerParameters
14
+ from mcp.client.sse import sse_client
15
+ from mcp.client.stdio import stdio_client
16
+ from mcp.client.streamable_http import streamablehttp_client
17
+ from mcp.types import CallToolResult, TextContent, ImageContent
18
+
19
+ from ctools import sys_log, cdate
20
+ from ctools.ai.tools.tool_use_xml_parse import parse_tool_use
21
+
22
+ log = sys_log.flog
23
+
24
+ sys_prompt_4_mcp = """
25
+ 1. In this environment you have access to a set of tools you can use to answer the user's question.
26
+ 2. You can use one tool per message, and will receive the result of that tool use in the user's response.
27
+ 3. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
28
+ 4. Before solving the task, break it down into clear, logical steps. List and number the steps first. Then, execute them one by one, There is no need to explain each step as you go. Do not skip any steps. Wait for confirmation before proceeding to the next step, if needed.
29
+
30
+ ## Tool Use Formatting
31
+
32
+ Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
33
+ <tool_use>
34
+ <name>{{tool_name}}</name>
35
+ <arguments>{{json_arguments}}</arguments>
36
+ </tool_use>
37
+
38
+ The tool name should be the exact name of the tool you are using, and the arguments should be a JSON object containing the parameters required by that tool. For example:
39
+
40
+ <tool_use>
41
+ <name>say_hello</name>
42
+ <arguments>
43
+ {{
44
+ "content": "你好"
45
+ }}
46
+ </arguments>
47
+ </tool_use>
48
+
49
+ The result should be a string, which can represent a file or any other output type. You can use this result as input for the next action.
50
+
51
+ Always adhere to this format for the tool use to ensure proper parsing and execution.
52
+
53
+ ## Tool Use Available Tools
54
+ Above example were using notional tools that might not exist for you. You only have access to these tools:
55
+ {tools_description}
56
+
57
+ ## Tool Use Rules
58
+ Here are the rules you should always follow to solve your task:
59
+ 1. Always use the right arguments for the tools. Never use variable names as the action arguments, use the value instead.
60
+ 2. Call a tool only when needed: do not call the search agent if you do not need information, try to solve the task yourself.
61
+ 3. If no tool call is needed, just answer the question directly.
62
+ 4. Never re-do a tool call that you previously did with the exact same parameters.
63
+ 5. For tool use, MARK SURE use XML tag format as shown in the examples above. Do not use any other format.
64
+ 6. Parameter passing should never escape unicode, and this is done by default, do not convert Chinese to Unicode escape characters
65
+
66
+ # User Instructions
67
+ {user_system_prompt}
68
+
69
+ # Current time
70
+ {current_time}
71
+
72
+ Now Begin! If you solve the task correctly, you will receive a reward of $1,000,000.
73
+ """
74
+
75
+ tools_use_example = """
76
+ Here are a few examples using notional tools:
77
+
78
+ ---
79
+ User: "What is the result of the following operation: 5 + 3 + 1294.678?"
80
+
81
+ Assistant: I can use the python_interpreter tool to calculate the result of the operation.
82
+ <tool_use>
83
+ <name>python_interpreter</name>
84
+ <arguments>{"code": "5 + 3 + 1294.678"}</arguments>
85
+ </tool_use>
86
+
87
+ User: {
88
+ "tool_name": "python_interpreter",
89
+ "result": ["1302.678"]
90
+ }
91
+
92
+ Assistant: The result of the operation is 1302.678.
93
+
94
+ ---
95
+ User: "Which city has the highest population , Guangzhou or Shanghai?"
96
+
97
+ Assistant: I can use the search tool to find the population of Guangzhou.
98
+ <tool_use>
99
+ <name>search</name>
100
+ <arguments>{"query": "Population Guangzhou"}</arguments>
101
+ </tool_use>
102
+
103
+ User: {
104
+ "tool_name": "search",
105
+ "result": ["Guangzhou has a population of 15 million inhabitants as of 2021."]
106
+ }
107
+
108
+ Assistant: I can use the search tool to find the population of Shanghai.
109
+ <tool_use>
110
+ <name>search</name>
111
+ <arguments>{"query": "Population Shanghai"}</arguments>
112
+ </tool_use>
113
+
114
+ User: {
115
+ "tool_name": "search",
116
+ "result": ["26 million (2019)"]
117
+ }
118
+ Assistant: The population of Shanghai is 26 million, while Guangzhou has a population of 15 million. Therefore, Shanghai has the highest population.
119
+ """
120
+
121
+
122
+ async def get_tools_prompt(mcp_clients, user_system_prompt) -> str:
123
+ all_tools = []
124
+ for client in mcp_clients:
125
+ tools = await client.list_server_tools()
126
+ all_tools.extend(tools)
127
+ return sys_prompt_4_mcp.format(tools_description="\n".join([tool.format_for_llm() for tool in all_tools]), user_system_prompt=user_system_prompt, current_time=cdate.get_date())
128
+
129
+
130
+ class Tool:
131
+
132
+ def __init__(self, name: str, description: str, input_schema: dict[str, Any]) -> None:
133
+ self.name: str = name
134
+ self.description: str = description
135
+ self.input_schema: dict[str, Any] = input_schema
136
+
137
+ def format_for_llm(self) -> str:
138
+ args_desc = []
139
+ if "properties" in self.input_schema:
140
+ for param_name, param_info in self.input_schema["properties"].items():
141
+ arg_desc = f"- {param_name}({param_info.get('type', 'Any')}): {param_info.get('description', '')}"
142
+ if param_name in self.input_schema.get("required", []):
143
+ arg_desc += " (required)"
144
+ args_desc.append(arg_desc)
145
+ return f"""
146
+ Tool: {self.name}
147
+ Description: {self.description}
148
+ Args_Info:
149
+ {chr(10).join(args_desc)}
150
+ """
151
+
152
+
153
+ class MCPClient:
154
+
155
+ def __init__(self, name: str, config: dict[str, Any]) -> None:
156
+ self.name: str = name
157
+ self.config: dict[str, Any] = config
158
+ self.stdio_context: Any | None = None
159
+ self.session: ClientSession | None = None
160
+ self.exit_stack: AsyncExitStack = AsyncExitStack()
161
+ self.tools = []
162
+
163
+ async def initialize(self) -> None:
164
+ if self.config.get('server_type') is None or self.config.get('server_type') == 'stdio':
165
+ command = (shutil.which("npx") if self.config["command"] == "npx" else self.config["command"])
166
+ if command is None: raise ValueError("The command must be a valid string and cannot be None.")
167
+ env = os.environ.copy()
168
+ custom_env = self.config.get("env", {})
169
+ env.update(custom_env)
170
+ server_params = StdioServerParameters(
171
+ command=command,
172
+ args=self.config["args"],
173
+ env=env,
174
+ )
175
+ stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
176
+ read, write = stdio_transport
177
+ self.session = await self.exit_stack.enter_async_context(ClientSession(read, write))
178
+ await self.session.initialize()
179
+ elif self.config['server_type'] == 'SSE':
180
+ sse_transport = await self.exit_stack.enter_async_context(sse_client(
181
+ url=self.config["url"],
182
+ headers=self.config["headers"],
183
+ timeout=self.config["timeout"],
184
+ sse_read_timeout=self.config["sse_read_timeout"]))
185
+ read, write = sse_transport
186
+ self.session = await self.exit_stack.enter_async_context(ClientSession(read, write))
187
+ await self.session.initialize()
188
+ elif self.config['server_type'] == 'Streamable HTTP':
189
+ stream_transport = await self.exit_stack.enter_async_context(streamablehttp_client(
190
+ url=self.config["url"],
191
+ headers=self.config["headers"],
192
+ timeout=self.config["timeout"],
193
+ sse_read_timeout=self.config["sse_read_timeout"]))
194
+ read, write, session_id = stream_transport
195
+ self.session = await self.exit_stack.enter_async_context(ClientSession(read, write))
196
+ await self.session.initialize()
197
+
198
+ async def list_server_tools(self) -> list[Any]:
199
+ if not self.session: raise RuntimeError(f"Server {self.name} not initialized")
200
+ if self.tools: return self.tools
201
+ tools_response = await self.session.list_tools()
202
+ for item in tools_response:
203
+ if isinstance(item, tuple) and item[0] == "tools":
204
+ self.tools.extend(Tool(tool.name, tool.description, tool.inputSchema) for tool in item[1])
205
+ return self.tools
206
+
207
+ async def execute_tool(
208
+ self,
209
+ tool_name: str,
210
+ arguments: dict[str, Any],
211
+ retries: int = 3,
212
+ delay: float = 1.0,
213
+ ) -> Any:
214
+ if not self.session: raise RuntimeError(f"Server {self.name} not initialized")
215
+ attempt = 0
216
+ args = arguments
217
+ while attempt < retries:
218
+ try:
219
+ log.info(f"Executing {tool_name}...")
220
+ result = await self.session.call_tool(tool_name, args)
221
+ return result
222
+ except Exception as e:
223
+ attempt += 1
224
+ log.warning(f"Error executing tool: {e}. Attempt {attempt} of {retries}.")
225
+ if attempt < retries:
226
+ log.info(f"Retrying in {delay} seconds...")
227
+ await asyncio.sleep(delay)
228
+ else:
229
+ log.error("Max retries reached. Failing.")
230
+ raise
231
+
232
+ async def cleanup(self) -> None:
233
+ await self.exit_stack.aclose()
234
+ self.session = None
235
+ self.stdio_context = None
236
+ self.exit_stack = None
237
+
238
+
239
+ async def mcp_tool_call(mcp_clients: MCPClient, xml_info: str) -> str:
240
+ if not mcp_clients: return xml_info
241
+ final_result = {
242
+ "tool_name": "",
243
+ "result": []
244
+ }
245
+ try:
246
+ tool_call = parse_tool_use(xml_info)
247
+ if "tool" in tool_call and "arguments" in tool_call:
248
+ log.info(f"Executing tool: {tool_call['tool']} With arguments: {tool_call['arguments']}")
249
+ for client in mcp_clients:
250
+ tools = await client.list_server_tools()
251
+ if any(tool.name == tool_call["tool"] for tool in tools):
252
+ final_result["tool_name"] = tool_call["tool"]
253
+ try:
254
+ result: CallToolResult = await client.execute_tool(tool_call["tool"], tool_call["arguments"])
255
+ text_result = []
256
+ image_result = []
257
+ tools_call_content = result.content
258
+ for content in tools_call_content:
259
+ if type(content) == TextContent:
260
+ try:
261
+ text_result.append(json.loads(content.text))
262
+ except Exception as e:
263
+ text_result.append(content.text)
264
+ elif type(content) == ImageContent:
265
+ image_result.append({"mime_type": content.mimeType, "data": content.data})
266
+ text_result.extend(image_result)
267
+ final_result["result"] = text_result
268
+ return final_result
269
+ except Exception as e:
270
+ log.exception(e)
271
+ error_msg = f"Error executing tool: {str(e)}"
272
+ final_result["result"] = [error_msg]
273
+ return final_result
274
+ return f"No server found with tool: {tool_call['tool']}"
275
+ return xml_info
276
+ except Exception as e:
277
+ log.exception(e)
278
+ error_msg = f"Error executing tool: {str(e)}"
279
+ final_result["result"] = [error_msg]
280
+ return final_result
281
+
282
+
283
+ def res_has_img(llm_response) -> bool:
284
+ if type(llm_response) == str: return False
285
+ response: [] = llm_response.get("result")
286
+ for rep in response:
287
+ if is_image_content(rep):
288
+ return True
289
+ return False
290
+
291
+
292
+ def is_image_content(content: dict) -> bool:
293
+ try:
294
+ if content.get("mime_type") and content.get("data"):
295
+ return True
296
+ return False
297
+ except Exception:
298
+ return False
299
+
300
+
301
+ from contextlib import asynccontextmanager
302
+
303
+ @asynccontextmanager
304
+ async def init_mcp_clients(mcp_server_config: dict[str, Any]) -> list[MCPClient]:
305
+ mcp_clients = []
306
+ if not mcp_server_config["mcpServers"]:
307
+ yield mcp_clients
308
+ return # 这句必须有,避免进入 finally
309
+ try:
310
+ for name, sc in mcp_server_config["mcpServers"].items():
311
+ try:
312
+ mc = MCPClient(name, sc)
313
+ await mc.initialize()
314
+ mcp_clients.append(mc)
315
+ except Exception as e:
316
+ log.exception(f"Error initializing MCP server [{name}]: {e}")
317
+ yield mcp_clients # 只允许有一个 yield
318
+ finally:
319
+ for client in mcp_clients:
320
+ try:
321
+ print(client.name)
322
+ await client.cleanup()
323
+ except* Exception as eg:
324
+ #log.warning(f"ExceptionGroup unloading MCP server [{client.name}]: {eg}")
325
+ pass
326
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/5/22 15:57'
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/5/19 16:45'
5
+
6
+ import re
7
+
8
+ from ctools import cjson
9
+
10
+
11
+ def extract_json_from_text(text):
12
+ """
13
+ 从混杂文本中提取第一个完整的 JSON 对象
14
+ """
15
+ import json
16
+
17
+ # 方法1:尝试直接解析
18
+ try:
19
+ return json.loads(text)
20
+ except json.JSONDecodeError:
21
+ pass
22
+
23
+ # 方法2:字符级括号匹配提取 JSON
24
+ start = None
25
+ brace_count = 0
26
+ for i, char in enumerate(text):
27
+ if char == '{':
28
+ if start is None:
29
+ start = i
30
+ brace_count += 1
31
+ elif char == '}':
32
+ brace_count -= 1
33
+ if brace_count == 0 and start is not None:
34
+ json_candidate = text[start:i + 1]
35
+ try:
36
+ return json.loads(json_candidate)
37
+ except json.JSONDecodeError:
38
+ start = None # 重置继续寻找下一个可能的 JSON
39
+
40
+ # 方法3:尝试 JSONP 格式
41
+ match = re.search(r'\((\{[\s\S]*\})\)', text)
42
+ if match:
43
+ try:
44
+ return json.loads(match.group(1))
45
+ except json.JSONDecodeError:
46
+ pass
47
+
48
+ return None
49
+
50
+
51
+ if __name__ == '__main__':
52
+ xx = """
53
+ <think>
54
+
55
+ </think>
56
+
57
+ {
58
+ "sjbs": {
59
+ "xsbt": "东都公园业主集体信访事件",
60
+ "sjbh": "202406031234",
61
+ "jjcd": ["黄"]
62
+ },
63
+ "skys": {
64
+ "zxsj": [
65
+ {
66
+ "jqsj": "2024-06-03 09:00",
67
+ "sjms": "6月3日",
68
+ "sjlx": ["计划时间"]
69
+ }
70
+ ],
71
+ "zxdd": [
72
+ {
73
+ "bzdz": "黑龙江省哈尔滨市信访局",
74
+ "cslx": ["政府机关"]
75
+ }
76
+ ]
77
+ },
78
+ "ssqt": {
79
+ "qtms": ["哈尔滨市道外区东都公园业主"],
80
+ "qtgm": ["约5人以上,可能发展至群体性事件"],
81
+ "qtbq": ["房地产纠纷", "历史遗留问题"],
82
+ "zztz": ["有核心组织"]
83
+ },
84
+ "ryqd": [
85
+ {
86
+ "xm": ["杨开亮"],
87
+ "sfzh": ["2301251968101335**"],
88
+ "js": ["组织者"],
89
+ "hjd": ["哈尔滨市宾县满井镇永宁村崔海屯"],
90
+ "jzd": ["团结镇东都公元一区五栋二单元603"],
91
+ "lxdh": ["139366789**"],
92
+ "rybq": ["重点人"],
93
+ "wlzh": {
94
+ "wx": [],
95
+ "qq": []
96
+ },
97
+ "gjxx": [
98
+ {
99
+ "sj": ["2024-05-28 20:26"],
100
+ "dd": ["网络群聊"],
101
+ "xw": ["组织动员"]
102
+ }
103
+ ]
104
+ },
105
+ {
106
+ "xm": ["孙凤玲"],
107
+ "sfzh": ["2301041955121712**"],
108
+ "js": ["积极参与者"],
109
+ "hjd": ["哈尔滨市道外区迎新街好民居滨港水岸D15栋1单元14层4号"],
110
+ "jzd": ["道外区陶瓷小区D15-1-1404"],
111
+ "lxdh": ["17758887348"],
112
+ "rybq": [],
113
+ "wlzh": {
114
+ "wx": [],
115
+ "qq": []
116
+ },
117
+ "gjxx": [
118
+ {
119
+ "sj": ["2024-05-28 19:00"],
120
+ "dd": ["网络群聊"],
121
+ "xw": ["响应组织"]
122
+ }
123
+ ]
124
+ },
125
+ {
126
+ "xm": ["高秀艳"],
127
+ "sfzh": ["2323261982060762**"],
128
+ "js": ["积极参与者"],
129
+ "hjd": ["绥化市青冈县劳动乡北斗村丛喜云屯"],
130
+ "jzd": ["哈尔滨市道外区团结镇森桐木业"],
131
+ "lxdh": ["15846349146"],
132
+ "rybq": [],
133
+ "wlzh": {
134
+ "wx": [],
135
+ "qq": []
136
+ },
137
+ "gjxx": [
138
+ {
139
+ "sj": ["2024-05-28 20:00"],
140
+ "dd": ["网络群聊"],
141
+ "xw": ["响应组织"]
142
+ }
143
+ ]
144
+ },
145
+ {
146
+ "xm": ["高振凤"],
147
+ "sfzh": ["2323031974103046**"],
148
+ "js": ["一般参与者"],
149
+ "hjd": ["绥化市肇东市东发乡夕阳村郭家屯"],
150
+ "jzd": ["团结镇团结镇东都公园一区七栋一单元101"],
151
+ "lxdh": ["18004659805"],
152
+ "rybq": [],
153
+ "wlzh": {
154
+ "wx": [],
155
+ "qq": []
156
+ },
157
+ "gjxx": [
158
+ {
159
+ "sj": ["2024-05-28 19:30"],
160
+ "dd": ["网络群聊"],
161
+ "xw": ["响应组织"]
162
+ }
163
+ ]
164
+ },
165
+ {
166
+ "xm": ["陈立军"],
167
+ "sfzh": ["2301251980031907**"],
168
+ "js": ["组织者", "群主"],
169
+ "hjd": ["哈尔滨市宾县宾西镇一委六组"],
170
+ "jzd": [],
171
+ "lxdh": ["15776806667"],
172
+ "rybq": ["重点人"],
173
+ "wlzh": {
174
+ "wx": [],
175
+ "qq": []
176
+ },
177
+ "gjxx": [
178
+ {
179
+ "sj": ["2024-05-28 19:00"],
180
+ "dd": ["网络群聊"],
181
+ "xw": ["组织动员"]
182
+ }
183
+ ]
184
+ }
185
+ ],
186
+ "sjtz": {
187
+ "xwlx": ["集体信访", "网络串联"],
188
+ "sqnr": ["要求政府解决房产证办理问题,明确责任主体并推动政策落实"],
189
+ "dktz": [],
190
+ "zjly": ["自筹"]
191
+ },
192
+ "czjy": {
193
+ "zrdw": ["哈尔滨市公安局道外分局", "信访维稳专班"],
194
+ "yjcs": ["提前约谈重点人员", "加强网络群组监控", "部署现场警力"]
195
+ }
196
+ }
197
+ 20250603142141-INFO-8399134464-web_log(log:214) 127.0.0.1 [03/Jun/2025:14:18:39 +0800] "POST /chat/completion HTTP/1.1" 200 3670 "-" "python-requests/2.32.3"
198
+
199
+
200
+ """
201
+
202
+ print((cjson.dumps(extract_json_from_text(xx))))
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/6/9 09:49'
5
+
6
+ import asyncio
7
+ import base64
8
+ import json
9
+ import mimetypes
10
+ import sys
11
+ import uuid
12
+
13
+ from ctools.util.env_config import bool_env
14
+ from ctools.web.aio_web_server import get_stream_resp
15
+
16
+
17
+ class ROLE:
18
+ ASSISTANT = "assistant"
19
+ USER = "user"
20
+ SYSTEM = "system"
21
+
22
+
23
+ def build_message(role_type: ROLE, content):
24
+ """
25
+ 快速构建消息
26
+ Parameters
27
+ ----------
28
+ role_type 消息类型
29
+ content 消息内容
30
+
31
+ Returns 消息
32
+ -------
33
+
34
+ """
35
+ return {"role": role_type, "content": content}
36
+
37
+
38
+ def build_image_message(content: str, file: bytes = None, file_path: str = None):
39
+ """
40
+ 快速构建图片消息
41
+ Parameters
42
+ ----------
43
+ content 问题内容
44
+ file 图片文件
45
+ file_path 图片文件路径
46
+
47
+ Returns 消息
48
+ -------
49
+
50
+ """
51
+ rep = _get_image_data_and_mime(file, file_path)
52
+ img_content = [{
53
+ "type": "image_url",
54
+ "image_url": {
55
+ "url": f'data:{rep["mime_type"]};base64,{rep["data"]}'
56
+ }
57
+ }, {
58
+ "type": "text",
59
+ "text": content
60
+ }]
61
+ return build_message(ROLE.USER, img_content)
62
+
63
+
64
+ async def build_call_back(debug=None, request=None, SSE=True):
65
+ """
66
+ 快速构建回调函数
67
+ Parameters
68
+ ----------
69
+ debug 是否开启调试
70
+ request http请求
71
+
72
+ Returns 响应对象, 消息队列, 回调函数
73
+ -------
74
+ """
75
+ if not debug: debug = bool_env("LLM_DEBUG", False)
76
+ response = None
77
+ if request: response = await get_stream_resp(request)
78
+ call_id = uuid.uuid4()
79
+ message_queue = asyncio.Queue()
80
+
81
+ async def on_msg(cid, role, msg):
82
+ nonlocal response
83
+ if debug: print(msg, file=sys.__stdout__, end='', flush=True)
84
+ final_msg = {"id": cid, "role": role, "msg": msg}
85
+ await message_queue.put(msg)
86
+ if response:
87
+ if SSE:
88
+ await response.write(f"data: {json.dumps(final_msg)}\n\n".encode("utf-8"))
89
+ else:
90
+ await response.write(msg.encode("utf-8"))
91
+
92
+ async def on_final(cid, is_final, msg):
93
+ nonlocal response
94
+ if debug: print("\n", cid, "\n", is_final, "\n", msg, "\n", file=sys.__stdout__, flush=True)
95
+ if is_final:
96
+ await message_queue.put("[DONE]")
97
+ if response:
98
+ if SSE: await response.write(b"data: [DONE]\n\n")
99
+ await response.write_eof()
100
+ else:
101
+ nonlocal call_id
102
+ call_id = uuid.uuid4()
103
+
104
+ def get_call_id():
105
+ return call_id.hex
106
+
107
+ return response, message_queue, {"get_call_id": get_call_id, "get_event_msg_func": on_msg, "get_full_msg_func": on_final}
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
+ def _get_image_data_and_mime(file: bytes = None, file_path: str = None):
135
+ if file_path:
136
+ with open(file_path, "rb") as f:
137
+ file = f.read()
138
+ if not file:
139
+ raise ValueError("file 和 file_path 至少要提供一个")
140
+ mime_type = "application/octet-stream"
141
+ if file_path:
142
+ mime_type_guess, _ = mimetypes.guess_type(file_path)
143
+ if mime_type_guess:
144
+ mime_type = mime_type_guess
145
+ data = base64.b64encode(file).decode("utf-8")
146
+ return {
147
+ "mime_type": mime_type,
148
+ "data": data
149
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/6/3 14:30'
5
+
6
+ import re
7
+
8
+
9
+ def remove_think_blocks(text: str) -> str:
10
+ cleaned_text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
11
+ return cleaned_text.strip()