jwf-common-python 1.0.0__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 (60) hide show
  1. jwf_common_python/__init__.py +14 -0
  2. jwf_common_python/db_utils/__init__.py +6 -0
  3. jwf_common_python/db_utils/clickhouse.py +124 -0
  4. jwf_common_python/db_utils/elasticsearch.py +196 -0
  5. jwf_common_python/db_utils/mysql.py +64 -0
  6. jwf_common_python/eudic/__init__.py +5 -0
  7. jwf_common_python/eudic/wordbook.py +211 -0
  8. jwf_common_python/file_storage/__init__.py +2 -0
  9. jwf_common_python/file_storage/file_interface.py +29 -0
  10. jwf_common_python/file_storage/local.py +50 -0
  11. jwf_common_python/file_storage/tencent_cos.py +65 -0
  12. jwf_common_python/geography/__init__.py +8 -0
  13. jwf_common_python/geography/district_amap.py +181 -0
  14. jwf_common_python/geography/search_amap.py +84 -0
  15. jwf_common_python/github/__init__.py +5 -0
  16. jwf_common_python/github/webhooks.py +87 -0
  17. jwf_common_python/language_utils/__init__.py +34 -0
  18. jwf_common_python/language_utils/core.py +203 -0
  19. jwf_common_python/language_utils/files.py +52 -0
  20. jwf_common_python/language_utils/web.py +117 -0
  21. jwf_common_python/llm/__init__.py +1 -0
  22. jwf_common_python/llm/aliyun_qwen.py +10 -0
  23. jwf_common_python/llm/azure_gpt.py +66 -0
  24. jwf_common_python/llm/baidu_ernie.py +112 -0
  25. jwf_common_python/message_queue/__init__.py +7 -0
  26. jwf_common_python/message_queue/interface.py +49 -0
  27. jwf_common_python/message_queue/kafka.py +145 -0
  28. jwf_common_python/message_queue/rabbitmq.py +113 -0
  29. jwf_common_python/oauth2/__init__.py +0 -0
  30. jwf_common_python/oauth2/google.py +158 -0
  31. jwf_common_python/parse_subtitle/__init__.py +6 -0
  32. jwf_common_python/parse_subtitle/aa.vtt +2364 -0
  33. jwf_common_python/parse_subtitle/parse_srt_file.py +60 -0
  34. jwf_common_python/pdf/__init__.py +0 -0
  35. jwf_common_python/pdf/aaa.py +38 -0
  36. jwf_common_python/pdf/local_parse.py +259 -0
  37. jwf_common_python/sms/__init__.py +1 -0
  38. jwf_common_python/sms/interface.py +7 -0
  39. jwf_common_python/sms/tencent_sms.py +117 -0
  40. jwf_common_python/translate/__init__.py +152 -0
  41. jwf_common_python/translate/_youdao.py +234 -0
  42. jwf_common_python/translate/microsoft.py +696 -0
  43. jwf_common_python/translate/tencent.py +194 -0
  44. jwf_common_python/tts/__init__.py +0 -0
  45. jwf_common_python/tts/microsoft.py +87 -0
  46. jwf_common_python/utils/__init__.py +0 -0
  47. jwf_common_python/utils/image_utils.py +18 -0
  48. jwf_common_python/utils/tencent_utils.py +51 -0
  49. jwf_common_python/utils/utils.py +46 -0
  50. jwf_common_python/video/__init__.py +5 -0
  51. jwf_common_python/video/merge.py +276 -0
  52. jwf_common_python/weather/__init__.py +0 -0
  53. jwf_common_python/weather/amap.py +127 -0
  54. jwf_common_python/weather/wanwei.py +58 -0
  55. jwf_common_python/youdao/__init__.py +13 -0
  56. jwf_common_python/youdao/wordbook.py +88 -0
  57. jwf_common_python-1.0.0.dist-info/METADATA +669 -0
  58. jwf_common_python-1.0.0.dist-info/RECORD +60 -0
  59. jwf_common_python-1.0.0.dist-info/WHEEL +4 -0
  60. jwf_common_python-1.0.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,203 @@
1
+ """纯文本和词表处理,不读写文件,不修改输入,不配置全局日志。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from collections import Counter
7
+ from collections.abc import Iterable, Iterator
8
+ from urllib.parse import urlsplit
9
+
10
+ WORD_PATTERN = re.compile(r"[A-Za-z]+")
11
+
12
+
13
+ def _clean_words(words: Iterable[str]) -> Iterator[str]:
14
+ if isinstance(words, (str, bytes)):
15
+ raise TypeError("词表必须为字符串的可迭代对象,请将多行文本先 splitlines()")
16
+ for word in words:
17
+ if not isinstance(word, str):
18
+ raise TypeError("词表中的元素必须为字符串")
19
+ word = word.strip()
20
+ if word:
21
+ yield word
22
+
23
+
24
+ def _text_chunks(text: str | Iterable[str]) -> Iterator[str]:
25
+ if isinstance(text, str):
26
+ yield text
27
+ else:
28
+ for chunk in text:
29
+ if not isinstance(chunk, str):
30
+ raise TypeError("文本内容必须为字符串")
31
+ yield chunk
32
+
33
+
34
+ def filter_words(
35
+ words: Iterable[str],
36
+ *,
37
+ exclude_words: Iterable[str] = (),
38
+ known_words: Iterable[str] = (),
39
+ case_sensitive: bool = False,
40
+ min_length: int = 1,
41
+ unique: bool = False,
42
+ ) -> list[str]:
43
+ """排除无效/已知词,保留原文拼写和顺序,默认保留重复项。
44
+
45
+ 默认以 casefold 忽略大小写比较;unique=True 按同一规则去重。
46
+ 空白词忽略,长度按去除首尾空白后的字符串计算。
47
+ """
48
+ if (
49
+ isinstance(min_length, bool)
50
+ or not isinstance(min_length, int)
51
+ or min_length < 1
52
+ ):
53
+ raise ValueError("min_length 必须为正整数")
54
+ key = (lambda word: word) if case_sensitive else str.casefold
55
+ excluded = {
56
+ key(word)
57
+ for group in (exclude_words, known_words)
58
+ for word in _clean_words(group)
59
+ }
60
+ seen = set()
61
+ result = []
62
+ for word in _clean_words(words):
63
+ normalized = key(word)
64
+ if (
65
+ len(word) >= min_length
66
+ and normalized not in excluded
67
+ and (not unique or normalized not in seen)
68
+ ):
69
+ result.append(word)
70
+ if unique:
71
+ seen.add(normalized)
72
+ return result
73
+
74
+
75
+ def extract_words(text: str | Iterable[str], **options) -> list[str]:
76
+ """提取英文 A-Z/a-z 片段,保留原分词规则,再通过 filter_words 过滤。
77
+
78
+ 连字符、撇号、数字等均为分隔符,不进行词典查验或词形还原。
79
+ """
80
+ words = (
81
+ match.group()
82
+ for chunk in _text_chunks(text)
83
+ for match in WORD_PATTERN.finditer(chunk)
84
+ )
85
+ return filter_words(words, **options)
86
+
87
+
88
+ def extract_chapter(
89
+ text: str, start_title: str, end_title: str | None = None, *, occurrence: int = -1
90
+ ) -> str:
91
+ """按完整标题行提取正文,不包含标题。
92
+
93
+ 去除标题行首尾空白后匹配;默认选择最后一次出现的起始标题,跳过目录。
94
+ 结束位置为该起始标题之后的第一个结束标题;end_title=None 表示直到文末。
95
+ occurrence 使用 Python 索引规则,缺失标题或越界时抛出 ValueError。
96
+ """
97
+ if not isinstance(text, str):
98
+ raise TypeError("text 必须为字符串")
99
+ if not isinstance(start_title, str) or not start_title.strip():
100
+ raise ValueError("start_title 不能为空")
101
+ if end_title is not None and (
102
+ not isinstance(end_title, str) or not end_title.strip()
103
+ ):
104
+ raise ValueError("end_title 必须为非空字符串或 None")
105
+ if isinstance(occurrence, bool) or not isinstance(occurrence, int):
106
+ raise ValueError("occurrence 必须为整数")
107
+ lines = text.splitlines()
108
+ starts = [
109
+ index for index, line in enumerate(lines) if line.strip() == start_title.strip()
110
+ ]
111
+ try:
112
+ start = starts[occurrence]
113
+ except IndexError as exc:
114
+ raise ValueError("找不到指定次数的起始标题") from exc
115
+ end = len(lines)
116
+ if end_title is not None:
117
+ end = next(
118
+ (
119
+ index
120
+ for index in range(start + 1, len(lines))
121
+ if lines[index].strip() == end_title.strip()
122
+ ),
123
+ -1,
124
+ )
125
+ if end == -1:
126
+ raise ValueError("起始标题之后找不到结束标题")
127
+ return "\n".join(lines[start + 1 : end])
128
+
129
+
130
+ def get_chapter_words(
131
+ text: str,
132
+ start_title: str,
133
+ end_title: str | None = None,
134
+ *,
135
+ occurrence: int = -1,
136
+ **options,
137
+ ) -> list[str]:
138
+ return extract_words(
139
+ extract_chapter(text, start_title, end_title, occurrence=occurrence), **options
140
+ )
141
+
142
+
143
+ def word_frequencies(
144
+ words: Iterable[str], *, top_n: int | None = None, case_sensitive: bool = False
145
+ ) -> list[tuple[str, int]]:
146
+ """按词频降序返回 (词, 次数),同频按首次出现顺序;默认合并大小写。
147
+
148
+ 合并大小写时返回 casefold 后的词形。top_n=None 表示全部,0 返回空列表。
149
+ """
150
+ if top_n is not None and (
151
+ isinstance(top_n, bool) or not isinstance(top_n, int) or top_n < 0
152
+ ):
153
+ raise ValueError("top_n 必须为非负整数或 None")
154
+ cleaned = _clean_words(words)
155
+ return Counter(
156
+ word if case_sensitive else word.casefold() for word in cleaned
157
+ ).most_common(top_n)
158
+
159
+
160
+ def format_words(words: Iterable[str], **options) -> str:
161
+ """按词频排序并返回去重后的逐行词表文本,替代旧写文件函数。"""
162
+ return "\n".join(word for word, _ in word_frequencies(words, **options))
163
+
164
+
165
+ def group_words_by_length(words: Iterable[str]) -> dict[int, list[str]]:
166
+ """按长度分组,保留各组输入顺序和重复项。"""
167
+ groups = {}
168
+ for word in _clean_words(words):
169
+ groups.setdefault(len(word), []).append(word)
170
+ return groups
171
+
172
+
173
+ def sort_words_by_length(words: Iterable[str], *, descending: bool = True) -> list[str]:
174
+ """按长度稳定排序,保留同长度的原顺序,不修改输入。"""
175
+ return sorted(_clean_words(words), key=len, reverse=descending)
176
+
177
+
178
+ def is_title_case_word(word: str) -> bool:
179
+ """保留旧判断规则:Dog 为 True,A、DOG、DoG 和带标点的文本为 False。"""
180
+ return isinstance(word, str) and re.fullmatch(r"[A-Z][a-z]+", word) is not None
181
+
182
+
183
+ def parse_urls(text: str | Iterable[str]) -> list[str]:
184
+ """逐行读取 HTTP(S) URL,忽略空行和 # 注释,按首次出现顺序去重。"""
185
+ result = []
186
+ seen = set()
187
+ for chunk in _text_chunks(text):
188
+ for line in chunk.splitlines():
189
+ url = line.strip()
190
+ if not url or url.startswith("#"):
191
+ continue
192
+ parts = urlsplit(url)
193
+ _ = parts.port # 触发端口格式及范围校验。
194
+ if (
195
+ parts.scheme not in ("http", "https")
196
+ or not parts.hostname
197
+ or any(char.isspace() for char in url)
198
+ ):
199
+ raise ValueError(f"无效的 HTTP(S) URL:{url}")
200
+ if url not in seen:
201
+ seen.add(url)
202
+ result.append(url)
203
+ return result
@@ -0,0 +1,52 @@
1
+ """只在显式调用时读取指定路径,不创建目录或写文件。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+ from pathlib import Path
7
+
8
+ from .core import extract_words, filter_words, parse_urls
9
+
10
+
11
+ def read_words(path: str | Path, *, encoding: str = "utf-8", **options) -> list[str]:
12
+ """读取指定文本文件并取词,options 同 extract_words。"""
13
+ return extract_words(Path(path).read_text(encoding=encoding), **options)
14
+
15
+
16
+ def read_word_lists(
17
+ paths: Iterable[str | Path],
18
+ *,
19
+ encoding: str = "utf-8",
20
+ case_sensitive: bool = False,
21
+ ) -> list[str]:
22
+ """合并多个词表文件并去重,保留首次出现拼写,修复旧代码只保留最后文件的问题。"""
23
+ if isinstance(paths, (str, Path)):
24
+ raise TypeError("paths 必须为路径的可迭代对象,例如 [path]")
25
+ words = (word for path in paths for word in read_words(path, encoding=encoding))
26
+ return filter_words(words, unique=True, case_sensitive=case_sensitive)
27
+
28
+
29
+ def read_urls(path: str | Path, *, encoding: str = "utf-8") -> list[str]:
30
+ return parse_urls(Path(path).read_text(encoding=encoding))
31
+
32
+
33
+ def generate_file_menu(
34
+ directory: str | Path, *, prefix: str = "", recursive: bool = True
35
+ ) -> tuple[list[str], str]:
36
+ """列出 .txt 文件,返回排序后的相对路径列表及换行连接的菜单文本。
37
+
38
+ prefix 筛选首级文件或目录名,例如旧数据的 wanglu_ 前缀。
39
+ 相对子路径保留,避免不同目录下的同名文件混淆。
40
+ """
41
+ root = Path(directory)
42
+ if not root.exists():
43
+ raise FileNotFoundError(root)
44
+ if not root.is_dir():
45
+ raise NotADirectoryError(root)
46
+ candidates = root.rglob("*.txt") if recursive else root.glob("*.txt")
47
+ paths = sorted(
48
+ path.relative_to(root).as_posix()
49
+ for path in candidates
50
+ if path.is_file() and path.relative_to(root).parts[0].startswith(prefix)
51
+ )
52
+ return paths, "\n".join(paths)
@@ -0,0 +1,117 @@
1
+ """网页取词,等待全部任务结束后按 URL 输入顺序返回结果。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from collections.abc import Iterable
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from html.parser import HTMLParser
9
+
10
+ import requests
11
+
12
+ from .core import _clean_words, extract_words, filter_words, parse_urls
13
+
14
+
15
+ class _TextParser(HTMLParser):
16
+ HIDDEN_TAGS = {"script", "style", "noscript", "template"}
17
+ BLOCK_TAGS = {
18
+ "p",
19
+ "div",
20
+ "br",
21
+ "hr",
22
+ "li",
23
+ "ul",
24
+ "ol",
25
+ "section",
26
+ "article",
27
+ "header",
28
+ "footer",
29
+ "main",
30
+ "nav",
31
+ "pre",
32
+ "blockquote",
33
+ "table",
34
+ "tr",
35
+ "td",
36
+ "th",
37
+ "h1",
38
+ "h2",
39
+ "h3",
40
+ "h4",
41
+ "h5",
42
+ "h6",
43
+ }
44
+
45
+ def __init__(self):
46
+ super().__init__(convert_charrefs=True)
47
+ self.chunks = []
48
+ self.hidden_depth = 0
49
+
50
+ def handle_starttag(self, tag, attrs):
51
+ if tag in self.HIDDEN_TAGS:
52
+ self.hidden_depth += 1
53
+ elif not self.hidden_depth and tag in self.BLOCK_TAGS:
54
+ self.chunks.append("\n")
55
+
56
+ def handle_endtag(self, tag):
57
+ if tag in self.HIDDEN_TAGS and self.hidden_depth:
58
+ self.hidden_depth -= 1
59
+ elif not self.hidden_depth and tag in self.BLOCK_TAGS:
60
+ self.chunks.append("\n")
61
+
62
+ def handle_data(self, data):
63
+ if not self.hidden_depth:
64
+ self.chunks.append(data)
65
+
66
+
67
+ def fetch_words_from_pages(
68
+ urls: str | Iterable[str],
69
+ *,
70
+ max_workers: int = 3,
71
+ timeout: float = 30,
72
+ exclude_words: Iterable[str] = (),
73
+ known_words: Iterable[str] = (),
74
+ min_length: int = 2,
75
+ unique: bool = True,
76
+ case_sensitive: bool = False,
77
+ ) -> dict[str, list[str]]:
78
+ """获取 HTML 中的英文片段,移除标签及 script/style/noscript/template 内容。
79
+
80
+ 不执行 JavaScript;结果不代表已通过词典校验。默认过滤单字母并去重,
81
+ 不更改输入拼写。任何 HTTP/网络错误直接抛出,不返回不完整的成功结果。
82
+ """
83
+ if (
84
+ isinstance(max_workers, bool)
85
+ or not isinstance(max_workers, int)
86
+ or max_workers <= 0
87
+ ):
88
+ raise ValueError("max_workers 必须为正整数")
89
+ if (
90
+ isinstance(timeout, bool)
91
+ or not isinstance(timeout, (int, float))
92
+ or not math.isfinite(timeout)
93
+ or timeout <= 0
94
+ ):
95
+ raise ValueError("timeout 必须为有限的正数")
96
+ targets = parse_urls(urls)
97
+ excluded = tuple(_clean_words(exclude_words))
98
+ known = tuple(_clean_words(known_words))
99
+ options = dict(
100
+ exclude_words=excluded,
101
+ known_words=known,
102
+ min_length=min_length,
103
+ unique=unique,
104
+ case_sensitive=case_sensitive,
105
+ )
106
+ filter_words([], **options) # 在发起网络请求前检查取词参数。
107
+
108
+ def fetch(url):
109
+ with requests.get(url, timeout=timeout) as response:
110
+ response.raise_for_status()
111
+ parser = _TextParser()
112
+ parser.feed(response.text)
113
+ parser.close()
114
+ return extract_words("".join(parser.chunks), **options)
115
+
116
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
117
+ return dict(zip(targets, executor.map(fetch, targets)))
@@ -0,0 +1 @@
1
+ # pip install openai
@@ -0,0 +1,10 @@
1
+ # 百炼是阿里云推出的一站式大模型服务平台
2
+ # 通义千问是阿里云自主研发的大语言模型,属于通义大模型系列的一部分
3
+ # 千问大模型是通义千问的一部分,是基于Transformer架构构建的
4
+
5
+
6
+ class LLMAliyunQwen:
7
+ """阿里云出品的千问大模型"""
8
+
9
+ def __init__(self):
10
+ pass
@@ -0,0 +1,66 @@
1
+ import base64
2
+ import os
3
+
4
+ import requests
5
+
6
+ # [参考1](https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line%2Ctypescript%2Cpython-new&pivots=rest-api)
7
+ # [参考2](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference)
8
+ # Configuration
9
+ API_KEY = "xxx"
10
+ # IMAGE_PATH = "YOUR_IMAGE_PATH"
11
+ # encoded_image = base64.b64encode(open(IMAGE_PATH, "rb").read()).decode("ascii")
12
+ headers = {
13
+ "Content-Type": "application/json",
14
+ "api-key": API_KEY,
15
+ }
16
+
17
+ # Payload for the request
18
+ payload = {
19
+ "messages": [
20
+ {
21
+ "role": "system",
22
+ "content": [{"type": "text", "text": "你是一个帮助用户查找信息的 AI 助手。"}],
23
+ }
24
+ ],
25
+ "temperature": 0.7,
26
+ "top_p": 0.95,
27
+ "max_tokens": 800,
28
+ }
29
+
30
+ ENDPOINT = "https://pro-chatgpt-4.openai.azure.com/openai/deployments/gpt-4o/chat/completions?api-version=2024-02-15-preview"
31
+
32
+ # Send request
33
+ try:
34
+ response = requests.post(ENDPOINT, headers=headers, json=payload)
35
+ response.raise_for_status() # Will raise an HTTPError if the HTTP request returned an unsuccessful status code
36
+ except requests.RequestException as e:
37
+ raise SystemExit(f"Failed to make the request. Error: {e}")
38
+
39
+ # Handle the response as needed (e.g., print or process)
40
+ print(response.json())
41
+
42
+ # 2222
43
+ import os
44
+
45
+ from openai import AzureOpenAI
46
+
47
+ client = AzureOpenAI(
48
+ azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
49
+ api_key=os.getenv("AZURE_OPENAI_API_KEY"),
50
+ api_version="2024-02-01",
51
+ )
52
+
53
+ response = client.chat.completions.create(
54
+ model="gpt-35-turbo", # model = "deployment_name".
55
+ messages=[
56
+ {"role": "system", "content": "You are a helpful assistant."},
57
+ {"role": "user", "content": "Does Azure OpenAI support customer managed keys?"},
58
+ {
59
+ "role": "assistant",
60
+ "content": "Yes, customer managed keys are supported by Azure OpenAI.",
61
+ },
62
+ {"role": "user", "content": "Do other Azure AI services support this too?"},
63
+ ],
64
+ )
65
+
66
+ print(response.choices[0].message.content)
@@ -0,0 +1,112 @@
1
+ # ------ 百度出品 ------:
2
+ # 千帆是百度推出的一站式大模型平台,支持多种大模型:
3
+ # - 包括ERNIE模型
4
+ # - 可以接入其他大模型如Llama2、ChatGLM2等
5
+ # ERNIE(Enhanced Representation through kNowledge IntEgration)是一种深度语义表示模型,基于Transformer架构
6
+ # 文心一言是基于ERNIE模型开发的生成式对话产品,能够进行文学创作、商业文案创作、数理逻辑推理等多种任务
7
+ import datetime
8
+
9
+ import requests
10
+
11
+
12
+ class LLMBaiduErnie:
13
+ """百度出品的ERNIE模型"""
14
+
15
+ def __init__(self, client_id: str, client_secret: str):
16
+ self.client_id = client_id
17
+ self.client_secret = client_secret
18
+ self.grant_type = "client_credentials"
19
+
20
+ def get_token_info(self) -> dict:
21
+ """获取百度大模型token
22
+
23
+ - access_token: 使用大模型的access_token
24
+ - expire_time: token过期时间,通常是30天
25
+ """
26
+ url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type\
27
+ ={self.grant_type}&client_id={self.client_id}&client_secret={self.client_secret}"
28
+ response = requests.post(url, headers={"Content-Type": "application/json"})
29
+ if response.status_code != 200:
30
+ raise Exception(
31
+ f"百度大模型登录获取token失败: status code: {response.status_code}, response: {response.text}"
32
+ )
33
+ res = response.json()
34
+
35
+ # 1. 获取access_token
36
+ access_token = res["access_token"]
37
+
38
+ # 当前时间 xxx 秒后到期
39
+ expires_in = res["expires_in"]
40
+ # 当前时间
41
+ current_time = datetime.datetime.now()
42
+ # 2. 过期时间
43
+ expire_time = current_time + datetime.timedelta(seconds=expires_in)
44
+
45
+ self.token_info = {"access_token": access_token, "expire_time": expire_time}
46
+ return self.token_info
47
+
48
+ def send_model_request(self, text: str, temperature: float = 0.95):
49
+ """发送模型请求
50
+ 温度越低,生成的文本越保守,
51
+ 温度越高,生成的文本越有创意
52
+ """
53
+ # check1
54
+ if not hasattr(self, "token_info"):
55
+ # 当 token没有被获取时 重新获取token
56
+ self.get_token_info()
57
+
58
+ # check2
59
+ access_token = self.token_info["access_token"]
60
+ _expire_time = self.token_info["expire_time"]
61
+ if _expire_time <= datetime.datetime.now():
62
+ # 当 token过期时 重新获取token
63
+ self.get_token_info()
64
+ access_token = self.token_info["access_token"]
65
+
66
+ # 3. 构造请求参数
67
+ url = f"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/\
68
+ completions_pro?access_token={access_token}"
69
+ json_data = {
70
+ "messages": [{"role": "user", "content": text}],
71
+ "temperature": temperature,
72
+ "top_p": 0.8,
73
+ "penalty_score": 1,
74
+ "enable_system_memory": False,
75
+ "disable_search": False,
76
+ "enable_citation": False,
77
+ }
78
+
79
+ # 4. 发送模型请求
80
+ response = requests.post(
81
+ url,
82
+ headers={"Content-Type": "application/json"},
83
+ json=json_data,
84
+ )
85
+ if response.status_code != 200:
86
+ raise Exception(
87
+ f"百度大模型请求失败: status code: {response.status_code}, response: {response.text}"
88
+ )
89
+
90
+ # 5. 返回结果
91
+ res = response.json()
92
+ msg = res["result"]
93
+ return msg
94
+
95
+ def is_natural_language(self, text: str) -> bool:
96
+ """判断文本是自然语言还是非自然语言
97
+
98
+ True: 自然语言
99
+ False: 非自然语言
100
+ """
101
+ prompt = f"""请判断以下文本是普通自然语言还是代码、配置文件等非自然语言.
102
+ 如果是自然语言那就返回字符串 "Natural Language",如果是非自然语言,那就返回字符串 "Non-Natural Language",但是不要返回包裹字符串的引号.
103
+ 需要判断的文本:\n {text} """
104
+
105
+ res_status = self.send_model_request(prompt, temperature=0.1)
106
+ match res_status:
107
+ case "Natural Language":
108
+ return True
109
+ case "Non-Natural Language":
110
+ return False
111
+ case _:
112
+ raise Exception(f"未知的返回值: {res_status}")
@@ -0,0 +1,7 @@
1
+ """Kafka 和 RabbitMQ 消息队列;使用对应后端时才加载其可选依赖。"""
2
+
3
+ from .interface import MessageCallback, MessageQueue
4
+ from .kafka import KafkaQueue
5
+ from .rabbitmq import RabbitmqQueue
6
+
7
+ __all__ = ["MessageCallback", "MessageQueue", "KafkaQueue", "RabbitmqQueue"]
@@ -0,0 +1,49 @@
1
+ """消息队列的同步接口。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from collections.abc import Callable
7
+ from typing import Any
8
+
9
+ MessageCallback = Callable[[bytes | None], None]
10
+
11
+
12
+ class MessageQueue(ABC):
13
+ """消息以 bytes 传递(Kafka 删除标记为 None)。
14
+
15
+ 回调正常返回表示处理成功,抛出异常表示失败。
16
+ 消费采用至少一次处理语义,业务回调应能处理重复消息。
17
+ 实例不支持跨线程共享,使用 with 或 close() 释放连接。
18
+ """
19
+
20
+ def __init__(self) -> None:
21
+ self._closed = False
22
+
23
+ def _ensure_open(self) -> None:
24
+ if self._closed:
25
+ raise RuntimeError("消息队列已关闭")
26
+
27
+ @staticmethod
28
+ def _validate_item(item: bytes) -> None:
29
+ if not isinstance(item, bytes):
30
+ raise TypeError("item 必须为 bytes")
31
+
32
+ @abstractmethod
33
+ def enqueue(self, item: bytes) -> dict[str, Any] | None:
34
+ """等待服务端确认发送;失败时抛出原始 SDK 异常。"""
35
+
36
+ @abstractmethod
37
+ def consumer(self, callback: MessageCallback) -> None:
38
+ """阻塞消费;只在 callback 成功后确认消息,异常向调用方传播。"""
39
+
40
+ @abstractmethod
41
+ def close(self) -> None:
42
+ """释放资源,可重复调用。"""
43
+
44
+ def __enter__(self):
45
+ self._ensure_open()
46
+ return self
47
+
48
+ def __exit__(self, exc_type, exc_value, traceback) -> None:
49
+ self.close()