@3-/aiapi 0.1.4 → 0.1.6

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.
package/README.md CHANGED
@@ -1,141 +1 @@
1
1
  # @3-/aiapi
2
-
3
- [test/main.coffee](./test/main.coffee) :
4
-
5
- ```coffee
6
- #!/usr/bin/env coffee
7
-
8
- > @3-/aiapi
9
- @3-/read
10
- path > join
11
-
12
- ROOT = import.meta.dirname
13
- MAX_TOKENS = 8192
14
-
15
- content = """
16
- 请阅读下文:
17
- ---
18
- #{read(join(ROOT, 'test.txt')).split('\n').map((i)=>i.trim()).filter((i)=>i.length>0).join('\n').slice(0,MAX_TOKENS*2)}
19
- ---
20
- 请按以下示意先输出上文标签,后输出摘要,格式为json,用```包裹。
21
- ```
22
- {"tags":[],"summary":""}
23
- ```
24
- 标签不超过7个,用词要简短;
25
- 摘要不超过450字,纯文本格式,分多段。
26
- """
27
-
28
- {
29
- POST
30
- } = aiapi(
31
- 'https://api.siliconflow.cn/v1/'
32
- [
33
- process.env.SF_KEY
34
- ]
35
- )
36
-
37
- MODEL = 'Qwen/Qwen3-8B'
38
-
39
- console.log content
40
- body = {
41
- model: MODEL
42
- messages: [
43
- {
44
- role: "user",
45
- content
46
- }
47
- ]
48
- thinking_budget: 4096
49
- enable_thinking: true
50
- stream: false
51
- max_tokens: MAX_TOKENS
52
- # stop: ""
53
- # temperature: 0.7
54
- # top_p: 0.7
55
- # top_k: 50
56
- # frequency_penalty: 0.5
57
- # response_format: {
58
- # type: "text"
59
- # }
60
- }
61
-
62
- r = await POST(
63
- 'chat/completions'
64
- body
65
- )
66
-
67
- console.log r.choices[0].message
68
- ```
69
-
70
- output :
71
-
72
- ```
73
- 请阅读下文:
74
- ---
75
- 新能源车电池能用20年?燃油车最后的堡垒也被攻破了?
76
- 新能源汽车电池不耐用,居然是错误认知?
77
- 在讨论新能源汽车和燃油车该买哪个时,总有人担心新能源汽车,尤其是纯电动车,达到一定里程后,因电池容量衰减,续航里程会大幅下降,换电池相当于买辆新车。
78
- 的确,电池存在循环寿命和日历寿命两大问题,其中循环使用寿命是指电池最大容量会随着充电次数的增加而降低,日历寿命则指因材料老化,电池即便不使用最大容量也会逐渐降低。但最近英国的一份研究报告却告诉我们,担心电池寿命衰减完全是杞人忧天。
79
- 据美国汽车媒体Motor1报道,英国远程信息处理公司Geotab基于样本超过1万辆电动车的研究显示,电动车平均每年衰减约1.8%,若不出现重大事故,20年后电池最大容量仍相当于新车的64%。续航虽下降了不少,但不至于无法日常使用,比美国汽车平均寿命还要多出6年。
80
- 然而2024年11月中汽研发布的报告却显示,2019款Model 3在行驶了11.6万公里后,电池健康度还剩下89.3%,2020款理想ONE行驶10.3万公里后,电池健康度仅剩下75.6%。即便按照年限来看,2019款Model 3每年电池衰减也略超2%。两份报告似乎出现了冲突,究竟哪份更可信?
81
- 电池寿命比车更长,纯电动车能放心买了?
82
- 消费者对于新能源汽车电池衰减的认知,可能源自常见的电子设备,如手机、平板电脑等。重度手机用户,一天两到三充才够用,一年循环充电次数少说数百次,多则上千次,一年时间电池最大容量衰减10%左右很正常。
83
- 汽车则不同,按照车企普遍提供的质保限制一年30000公里计算,平均每天不到100公里,续航500公里的车一年大约需要循环充电60到80次,循环次数远小于数码产品。如果依据汽车流通协会给出的2023年国内私家车平均行驶里程1.2万公里计算,一年循环充电次数仅为30次左右。
84
- 在中汽研的测试中,理想ONE比Model Y衰减更严重,也有增程式汽车电池较小的原因。电池容量小的新能源汽车需要更加频繁地充电,更容易导致电池寿命下降。这里也提醒我们,搭载大容量电池的新能源汽车,优势不只在于续航更长,还能有效提高电池寿命。
85
- 至于Geotab和中汽研的报告哪个更可信,电车通更认可Geotab,原因在于中汽研的报告基于少量产品的实测结果,存在偶发性。Geotab则调研了超过1万个样本,庞大的数量能够避免个体差异对结果造成影响。
86
- 事实上,车企、电池企业都知道电池寿命的重要性,技术升级迭代一直围绕循环寿命、快充功率、能量密度等消费者感知较深的方向。例如全球电池行业龙头企业宁德时代官方页面显示,电池寿命最高可达16年或200万公里。需要注意的是,电池寿命是指设计寿命内的充放电循环中,容量衰减不超过20%。
87
- 深蓝汽车所搭载的金钟罩2.0系统电池组,号称循环次数可达3000次。吉利汽车自研的神盾短刀电池,循环次数可达3500次,电池寿命长达100万公里。同样按照私家车平均每年行驶里程1.2万公里计算,一款车使用20年,因循环充电造成的电量衰减不超过30%,而车辆的其他零部件恐怕还撑不到20年。
88
- 在各大平台,消费者也普遍表示,自己或朋友的新能源汽车开了许多年,电池衰减并不严重。如知乎平台一位网友晒出了他在2016年购买的帝豪EV,开了10万公里后,电池健康度依然高达95%。一位微博网友表示,他打车时打到了一辆行驶里程44万公里的出租车,司机师傅称电池衰减不到20%。
89
- 出租车的年行驶里程远高于私家车,如今新能源汽车已成为出租车司机的主流选择,尤其是在广州地区,基本打不到蓝牌车,可见新能源汽车电池耐用性已经得到了出租车司机的认可。
90
- 另一方面,为了让消费者能够更加安心地购买新能源汽车,车企还为消费者提供了三电质保服务,比亚迪、奇瑞、吉利等头部品牌的部分车型,已加入三电终身质保。未提供三电终身质保服务的车企或车型,一般也会有时长在8年左右,续航里程在15万公里左右的质保服务。质保期内电池衰减严重,车企会免费更换电池。
91
- 技术提升、服务升级之下,电池衰减早已不再是困扰消费者的问题,这并不意味着燃油车使用时间更长的遮羞布被撕下,更不意味着新能源汽车将彻底取代燃油车。
92
- 取代燃油车,新能源汽车还有很长的路要走
93
- 电池衰减不再困扰消费者后,向行业发出了一个积极的信号,即二手新能源汽车可以放心买了。中国汽车流通协会公布的数据显示,目前燃油车三年平均保值率为52%,纯电动车仅有45%。二手新能源汽车保值率低有两大原因,一是用户担心二手新能源汽车三电系统不可靠,失去质保后不敢买(三电质保基本仅限首任车主),二是新能源汽车升级迭代太快,用户担心遭遇背刺。
94
- 在电池循环使用寿命得到保障,产品度过快速升级迭代期后,二手新能源汽车市场正快速发展,今年1-4月交易量同比增长32.3%。商务部办公厅发布的组织开展2025年千县万镇新能源汽车消费季活动通知,也提到要致力于扩大二手新能源汽车交易规模,方便用户以旧换新。
95
- 然而尽管有诸多政策支持,新能源汽车提高渗透率的难度也将越来越高。乘联会数据显示,2024年7月,中国新能源汽车渗透率首次超过50%,但这一态势却没能长期延续下去。乘联会秘书长崔东树在其公众号发文称,今年1-5月新能源汽车产量为564万台,渗透率为44%。
96
- 究其原因,续航焦虑、购入成本等因素,限制了新能源汽车渗透率的快速提升。虽然电池企业和车企能够消除用户对于电池衰减的担忧,却无法彻底解决续航焦虑。车企宣传的续航里程通常基于CLTC工况,与实际能耗存在出入,又容易受到温度影响,哪怕如今高端纯电车的续航可以达到600公里以上,也无法完全消除用户的续航焦虑。
97
- 电池高昂的成本则反映在售价上,如今别克君威、福特蒙迪欧、现代索纳塔等B级车起步价都降到了10万左右,新能源B级车这个价位的产品却不多。再加上新能源汽车购置税从免除改为减半,导致消费者购买新能源汽车的成本也进一步增加。
98
- 若是不考虑智能化,仅考虑驾驶感受、空间大小等因素,现阶段燃油车的性价比较新能源汽车更胜一筹。
99
- 可以预见的是,新能源汽车渗透率整体仍将保持同比上涨趋势,即将到来的汽车销售旺季,有望复现去年下半年连续多个月份渗透率超过50%的盛况。但新能源汽车与燃油车将长期处于平分秋色的状态,新能源汽车渗透率距离突破60%还有很长的路要走。解决续航问题、下沉市场、升级体验并行的新能源汽车,才能加快蚕食燃油车份额的速度。
100
- 固态电池,不是压死燃油车的最后一根稻草
101
- 电池寿命不再是困扰消费者的问题,但续航焦虑却始终没有完善的解决方案。当前车企和电池企业较为看好固态电池。固态电池拥有更高的能量密度,宁德时代、比亚迪、奇瑞、奔驰等国内外企业,已在开发和测试这种电池。
102
- 2024年全球创新大会上,奇瑞率先推出了全固态电池,该产品能量密度高达600Wh/kg,几乎是三元锂电池的两倍,续航里程可达1500+公里,2026年即可实现量产。
103
- 不过固态电池虽能解决纯电动车的续航问题,却无法成为压死燃油车的最后一根稻草,毕竟它太贵了。比亚迪高管孙华军曾表示,2030年固态电池才能降低到与液态电池相同的价格。即便国内车企于2026年到2027年实现了固态电池上车,一块100kWh电池高达20万元的成本可不是开玩笑,只有豪车才能用上。
104
- 在各种材料的电池中,钠离子电池成本最为便宜,但能量密度较低的特点,又令其难以实现超长续航。看来,短期内长续航与低成本仍不可兼得。
105
- 燃油车与新能源汽车的竞争将是持久且艰巨的,Geotab的报告打破了消费者对新能源汽车电池寿命会锐减的认知,将对新能源汽车渗透率的提高起到积极作用,让更多消费者可以放心购买新能源汽车。
106
- ---
107
- 请按以下示意先输出上文标签,后输出摘要,格式为json,用```包裹。
108
- ```
109
- {"tags":[],"summary":""}
110
- ```
111
- 标签不超过7个,用词要简短;
112
- 摘要不超过450字,纯文本格式,分多段。
113
- ```
114
-
115
- ## About
116
-
117
- This project is an open-source component of [i18n.site ⋅ Internationalization Solution](https://i18n.site).
118
-
119
- * [i18 : MarkDown Command Line Translation Tool](https://i18n.site/i18)
120
-
121
- The translation perfectly maintains the Markdown format.
122
-
123
- It recognizes file changes and only translates the modified files.
124
-
125
- The translated Markdown content is editable; if you modify the original text and translate it again, manually edited translations will not be overwritten (as long as the original text has not been changed).
126
-
127
- * [i18n.site : MarkDown Multi-language Static Site Generator](https://i18n.site/i18n.site)
128
-
129
- Optimized for a better reading experience
130
-
131
- ## 关于
132
-
133
- 本项目为 [i18n.site ⋅ 国际化解决方案](https://i18n.site) 的开源组件。
134
-
135
- * [i18 : MarkDown命令行翻译工具](https://i18n.site/i18)
136
-
137
- 翻译能够完美保持 Markdown 的格式。能识别文件的修改,仅翻译有变动的文件。
138
-
139
- Markdown 翻译内容可编辑;如果你修改原文并再次机器翻译,手动修改过的翻译不会被覆盖(如果这段原文没有被修改)。
140
-
141
- * [i18n.site : MarkDown多语言静态站点生成器](https://i18n.site/i18n.site) 为阅读体验而优化。
@@ -0,0 +1,40 @@
1
+ import TYPE from './TYPE.js';
2
+
3
+ export default {
4
+ type: TYPE.ARRAY,
5
+ description: '讨论话题的数组,每个话题都包含相关的问答对',
6
+ items: {
7
+ type: TYPE.OBJECT,
8
+ properties: {
9
+ 话题: {
10
+ type: TYPE.STRING,
11
+ description: '将多个问答合并为一个组,用简短的标题描述'
12
+ },
13
+ 问答: {
14
+ type: TYPE.ARRAY,
15
+ description: '与该话题相关的一系列问答对',
16
+ minItems: 1,
17
+ items: {
18
+ type: TYPE.OBJECT,
19
+ properties: {
20
+ 题: {
21
+ type: TYPE.STRING,
22
+ description: '问答的短标题'
23
+ },
24
+ 问: {
25
+ type: TYPE.STRING,
26
+ description: '提问'
27
+ },
28
+ 答: {
29
+ type: TYPE.STRING,
30
+ description: '针对该问题的回答,重新分段,方便阅读'
31
+ }
32
+ },
33
+ required: ['题', '问', '答'],
34
+ propertyOrdering: ['问', '答']
35
+ }
36
+ }
37
+ },
38
+ required: ['话题', '问答']
39
+ }
40
+ };
package/TYPE.js ADDED
@@ -0,0 +1 @@
1
+ export default {'ARRAY': 'ARRAY', 'OBJECT': 'OBJECT', 'STRING': 'STRING', 'NUMBER': 'NUMBER', 'INTEGER': 'INTEGER'};
package/fmt.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
2
+ import fmtJson from './fmtJson.js';
3
+
4
+ import fmtJsonMd from './fmtJsonMd.js';
5
+
6
+ export default async(chat, txt) => {
7
+ return fmtJsonMd((await fmtJson(chat, txt)));
8
+ };
package/fmtJson.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
2
+ import fmtSeg from './fmtSeg.js';
3
+
4
+ import partition from './partition.js';
5
+
6
+ import seg from './seg.js';
7
+
8
+ export default async(chat, txt) => {
9
+ var gen, i, pli, result, split_li, sum, tmp, txt_li;
10
+ txt_li = txt.split('\n');
11
+ split_li = (await seg(chat, txt_li));
12
+ pli = partition(txt_li, split_li);
13
+ result = [];
14
+ sum = 0;
15
+ tmp = [];
16
+ gen = () => {
17
+ result.push(fmtSeg(chat, tmp.join('\n')));
18
+ };
19
+ for (i of pli) {
20
+ tmp.push(i);
21
+ sum += i.length;
22
+ if (sum > 3000) {
23
+ gen();
24
+ sum = 0;
25
+ tmp = [];
26
+ }
27
+ }
28
+ if (tmp.length) {
29
+ gen();
30
+ }
31
+ return Promise.all(result);
32
+ };
package/fmtJsonMd.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
2
+ import txtFmt from '@3-/txt_li/txtFmt.js';
3
+
4
+ export default (json_li) => {
5
+ var li, md_li, x, y, 答, 话题, 问, 问答, 题;
6
+ md_li = [];
7
+ for (li of json_li) {
8
+ for (x of li) {
9
+ ({话题, 问答} = x);
10
+ md_li.push('## ' + 话题);
11
+ for (y of 问答) {
12
+ ({题, 问, 答} = y);
13
+ md_li.push('### ' + 题 + '\n问: ' + txtFmt(问) + '\n答: ' + txtFmt(答));
14
+ }
15
+ }
16
+ }
17
+ return md_li.join('\n');
18
+ };
package/fmtSeg.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
2
+ import TOPIC_SCHEMA from './TOPIC_SCHEMA.js';
3
+
4
+ export default (chat, txt) => {
5
+ return chat(`将以下语音对话转为JSON数组,要确保所有问答都被包含,不要遗漏:\n` + txt, TOPIC_SCHEMA, "你是专业资深的秘书");
6
+ };
package/gemini.js ADDED
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
2
+ import sleep from '@3-/sleep';
3
+
4
+ export default (token_li) => {
5
+ var _NEXT_TOKEN, _nextToken, nextToken;
6
+ token_li.sort(() => {
7
+ return Math.random() - 0.5;
8
+ });
9
+ _nextToken = function*() {
10
+ var i;
11
+ while (true) {
12
+ for (i of token_li) {
13
+ yield i;
14
+ }
15
+ }
16
+ };
17
+ _NEXT_TOKEN = _nextToken();
18
+ nextToken = () => {
19
+ return _NEXT_TOKEN.next().value;
20
+ };
21
+ return async(text, schema, system) => {
22
+ var body, err, error, message, r, status;
23
+ body = {
24
+ generationConfig: {
25
+ responseMimeType: 'application/json',
26
+ responseJsonSchema: schema
27
+ },
28
+ contents: [
29
+ {
30
+ parts: [{text}]
31
+ }
32
+ ]
33
+ };
34
+ if (system) {
35
+ body.system_instruction = {
36
+ parts: [
37
+ {
38
+ text: system
39
+ }
40
+ ]
41
+ };
42
+ }
43
+ // console.log JSON.stringify body,null,2
44
+ body = JSON.stringify(body);
45
+ while (true) {
46
+ try {
47
+ r = (await fetch('https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent', {
48
+ headers: {
49
+ 'X-goog-api-key': nextToken(),
50
+ 'Content-Type': 'application/json'
51
+ },
52
+ method: 'POST',
53
+ body
54
+ }));
55
+ ({status} = r);
56
+ } catch (error1) {
57
+ err = error1;
58
+ console.error(err);
59
+ await sleep(1000);
60
+ continue;
61
+ }
62
+ if (status !== 200) {
63
+ text = (await r.text());
64
+ if (status === 429) {
65
+ try {
66
+ ({error} = JSON.parse(text));
67
+ console.warn(error.status, error.message);
68
+ } catch (error1) {
69
+ console.warn(text);
70
+ }
71
+ continue;
72
+ }
73
+ if (status === 503) {
74
+ try {
75
+ ({
76
+ error: {message}
77
+ } = JSON.parse(text));
78
+ console.warn(status, message);
79
+ } catch (error1) {
80
+ console.warn(text);
81
+ }
82
+ await sleep(1000);
83
+ continue;
84
+ }
85
+ throw new Error(text);
86
+ }
87
+ r = ((await r.json())).candidates[0].content.parts[0].text;
88
+ try {
89
+ r = JSON.parse(r);
90
+ } catch (error1) {
91
+ err = error1;
92
+ console.error(r);
93
+ throw err;
94
+ }
95
+ return r;
96
+ }
97
+ };
98
+ };
package/lib.js CHANGED
@@ -8,6 +8,9 @@ export default (prefix, key_li) => {
8
8
  var curl, key_len, n;
9
9
  n = 0;
10
10
  key_len = key_li.length;
11
+ key_li.sort(() => {
12
+ return Math.random() - 0.5;
13
+ });
11
14
  curl = async(url, opt) => {
12
15
  var err, headers, key, r, retry, status;
13
16
  if (opt) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3-/aiapi",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://atomgit.com/i18n/lib.git"
@@ -19,6 +19,9 @@
19
19
  "@3-/read": "^0.1.4"
20
20
  },
21
21
  "type": "module",
22
- "dependencies": {},
22
+ "dependencies": {
23
+ "@3-/sleep": "^0.0.4",
24
+ "@3-/txt_li": "^0.1.4"
25
+ },
23
26
  "scripts": {}
24
27
  }
package/partition.js ADDED
@@ -0,0 +1,27 @@
1
+ export default (lines, title_number) => {
2
+ let title = ''
3
+ // 对行号进行升序排序
4
+ const order = title_number.sort((a, b) => a[1] - b[1]);
5
+
6
+ // 存储最终结果的数组
7
+ const result = [];
8
+ // 追踪上一个分割点的索引
9
+ let start = 0;
10
+
11
+ // 遍历排序后的行号
12
+ for (const [t, row] of order) {
13
+ // 将行号转换为从0开始的数组索引
14
+ const index = row - 1;
15
+ console.log(title)
16
+ result.push(title + lines.slice(start, index).join('\n'));
17
+ title = '# ' + t + '\n'
18
+ start = index;
19
+ }
20
+
21
+ // 添加从最后一个分割点到末尾的剩余文本
22
+ if (start < lines.length) {
23
+ result.push(title + lines.slice(start).join('\n'));
24
+ }
25
+
26
+ return result;
27
+ };
package/seg.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
2
+ import TYPE from './TYPE.js';
3
+
4
+ export default async(chat, txt_li) => {
5
+ var split_li, 提示词;
6
+ 提示词 = `下文第1列为行号,第2列为对话内容,以tab分隔。
7
+ 请划分章节,每章以提问开始,包含多组问答。
8
+ 输出章节标题和每章首个问题的行号及原文,输出格式为JSON数组:\n` + txt_li.map((i, pos) => {
9
+ return (pos + 1) + '\t' + i.trim();
10
+ }).join('\n');
11
+ return split_li = (await chat(提示词, {
12
+ type: TYPE.ARRAY,
13
+ description: '章节和行号的列表',
14
+ items: {
15
+ type: TYPE.ARRAY,
16
+ items: false,
17
+ minItems: 3,
18
+ maxItems: 3,
19
+ prefixItems: [
20
+ {
21
+ description: '章节标题,1-2个词即可',
22
+ type: TYPE.STRING
23
+ },
24
+ {
25
+ description: '该章首行行号',
26
+ type: TYPE.INTEGER
27
+ },
28
+ {
29
+ description: '该章首行提问的原文',
30
+ type: TYPE.STRING
31
+ }
32
+ ]
33
+ }
34
+ }, '你是专业资深的秘书'));
35
+ };