@3-/aiapi 0.1.69 → 0.1.70
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/gemini.js +2 -1
- package/package.json +1 -1
- package/factCheck.js +0 -189
- package/fmtJson.js +0 -19
- package/fmtJsonMd.js +0 -26
- package/fmtSeg.js +0 -12
- package/lib.js +0 -70
- package/seg.js +0 -33
package/gemini.js
CHANGED
|
@@ -3,7 +3,8 @@ import sleep from '@3-/sleep';
|
|
|
3
3
|
|
|
4
4
|
import merge from 'lodash-es/merge.js';
|
|
5
5
|
|
|
6
|
-
export default (token_li, model = 'gemini-3-
|
|
6
|
+
export default (token_li, model = 'gemini-3-flash-preview') => { // model='gemini-3-pro-preview'
|
|
7
|
+
// model='gemini-2.5-pro'
|
|
7
8
|
var _NEXT_TOKEN, _nextToken, generate_content_url, nextToken;
|
|
8
9
|
generate_content_url = 'https://generativelanguage.googleapis.com/v1beta/models/' + model + ':generateContent';
|
|
9
10
|
token_li.sort(() => {
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@3-/aiapi","version":"0.1.
|
|
1
|
+
{"name":"@3-/aiapi","version":"0.1.70","repository":{"type":"git","url":"git+https://atomgit.com/i18n/lib.git"},"homepage":"https://atomgit.com/i18n/lib/tree/dev/aiapi","author":"i18n.site@gmail.com","license":"MulanPSL-2.0","exports":{".":"./lib.js","./*":"./*"},"files":["./*"],"devDependencies":{"@3-/read":"^0.1.4"},"scripts":{},"type":"module","dependencies":{"@3-/retry":"^0.0.2","@3-/rm_cn_space":"^0.1.1","@3-/sleep":"^0.0.4","@3-/txt_li":"^0.1.5","@3-/utf8":"^0.0.4","lodash-es":"^4.17.21"}}
|
package/factCheck.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
|
|
2
|
-
var PREFIX, SHOW_KIND, addUrl, circle, realUrl, txtLi, urlLi;
|
|
3
|
-
|
|
4
|
-
import retry from '@3-/retry';
|
|
5
|
-
|
|
6
|
-
import utf8d from '@3-/utf8/utf8d.js';
|
|
7
|
-
|
|
8
|
-
import utf8e from '@3-/utf8/utf8e.js';
|
|
9
|
-
|
|
10
|
-
// @3-/read
|
|
11
|
-
// @3-/write
|
|
12
|
-
SHOW_KIND = ['严重失实', '不正确'];
|
|
13
|
-
|
|
14
|
-
PREFIX = `对下文问答中提及的事实做核查(不核查观点)。
|
|
15
|
-
数据偏差不超过30%都当做基本一致,找不到信息当无法核实。
|
|
16
|
-
必须搜索到明确的证伪证据并才算失实。
|
|
17
|
-
只输出不正确/严重失实的核查。
|
|
18
|
-
输出文本必须是简体中文,格式如下:
|
|
19
|
-
- id:
|
|
20
|
-
标题: 要简短,几个词即可
|
|
21
|
-
观点:
|
|
22
|
-
事实: 须标注搜索到的事实网页, 完整描述观点错误所在(不引用其他核查)
|
|
23
|
-
失实度: ${SHOW_KIND.join(' / ')} / 基本一致 / 无法核实
|
|
24
|
-
---\n`;
|
|
25
|
-
|
|
26
|
-
realUrl = async(url) => {
|
|
27
|
-
var err, r, status;
|
|
28
|
-
if (url.startsWith('http')) {
|
|
29
|
-
try {
|
|
30
|
-
r = (await fetch(url, {
|
|
31
|
-
method: 'HEAD',
|
|
32
|
-
redirect: 'follow'
|
|
33
|
-
}));
|
|
34
|
-
({status} = r);
|
|
35
|
-
if (status === 200) {
|
|
36
|
-
({url} = r);
|
|
37
|
-
} else {
|
|
38
|
-
console.error(status + ' :' + url);
|
|
39
|
-
}
|
|
40
|
-
} catch (error) {
|
|
41
|
-
err = error;
|
|
42
|
-
console.error(err);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return url;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
circle = (num) => {
|
|
49
|
-
switch (false) {
|
|
50
|
-
case !(num > 0 && num <= 10):
|
|
51
|
-
return String.fromCodePoint(0x245F + num);
|
|
52
|
-
default:
|
|
53
|
-
return `[${num}]`;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
urlLi = (id_li, url_li) => {
|
|
58
|
-
var i, r;
|
|
59
|
-
r = [];
|
|
60
|
-
for (i of id_li) {
|
|
61
|
-
r.push('[' + circle(i + 1) + '](' + url_li[i] + ')');
|
|
62
|
-
}
|
|
63
|
-
return r.join('');
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
addUrl = (text, items) => {
|
|
67
|
-
var bin, chunks, end, entry, j, len, output, pos, push, sorted, start;
|
|
68
|
-
output = '';
|
|
69
|
-
push = (bin) => {
|
|
70
|
-
output += utf8d(bin);
|
|
71
|
-
};
|
|
72
|
-
pos = 0;
|
|
73
|
-
sorted = items.sort(function(a, b) {
|
|
74
|
-
return a.segment.startIndex - b.segment.startIndex;
|
|
75
|
-
});
|
|
76
|
-
bin = utf8e(text);
|
|
77
|
-
for (j = 0, len = sorted.length; j < len; j++) {
|
|
78
|
-
entry = sorted[j];
|
|
79
|
-
({
|
|
80
|
-
segment: {
|
|
81
|
-
startIndex: start,
|
|
82
|
-
endIndex: end
|
|
83
|
-
},
|
|
84
|
-
groundingChunkIndices: chunks
|
|
85
|
-
} = entry);
|
|
86
|
-
if (start > pos) {
|
|
87
|
-
push(bin.slice(pos, start));
|
|
88
|
-
}
|
|
89
|
-
push(bin.slice(start, end));
|
|
90
|
-
output += chunks;
|
|
91
|
-
pos = end;
|
|
92
|
-
}
|
|
93
|
-
if (pos < bin.length) {
|
|
94
|
-
push(bin.slice(pos));
|
|
95
|
-
}
|
|
96
|
-
return output;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
txtLi = (txt) => {
|
|
100
|
-
var all_tag, i, id, pre, r, reset, t, tag, trim, txt_li;
|
|
101
|
-
r = [];
|
|
102
|
-
reset = () => {
|
|
103
|
-
all_tag = new Set(['标题', '观点', '事实', '失实度']);
|
|
104
|
-
if (t) {
|
|
105
|
-
r.push(t);
|
|
106
|
-
}
|
|
107
|
-
pre = t = void 0;
|
|
108
|
-
};
|
|
109
|
-
txt_li = txt.replaceAll('**', '').split('\n');
|
|
110
|
-
out: //;
|
|
111
|
-
for (i of txt_li) {
|
|
112
|
-
if (i.startsWith('- id:')) {
|
|
113
|
-
reset();
|
|
114
|
-
id = parseInt(i.slice(5).trim());
|
|
115
|
-
if (id) {
|
|
116
|
-
t = {id};
|
|
117
|
-
}
|
|
118
|
-
} else if (t) {
|
|
119
|
-
trim = i.replace('- ', '').trimStart();
|
|
120
|
-
for (tag of all_tag) {
|
|
121
|
-
if (trim.startsWith(tag + ':')) {
|
|
122
|
-
all_tag.delete(tag);
|
|
123
|
-
pre = tag;
|
|
124
|
-
t[tag] = trim.slice(tag.length + 1).trim();
|
|
125
|
-
if (tag === '失实度' && all_tag.size === 0) {
|
|
126
|
-
reset();
|
|
127
|
-
}
|
|
128
|
-
continue out;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
if (pre && trim) {
|
|
132
|
-
t[pre] += '\n' + trim;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (t) {
|
|
137
|
-
r.push(t);
|
|
138
|
-
}
|
|
139
|
-
return r;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
export default retry(async(chat, li) => {
|
|
143
|
-
var content, groundingChunks, groundingSupports, i, map, msg, out, ref, ref1, uri, url_li, x;
|
|
144
|
-
msg = PREFIX + li.map((i, pos) => {
|
|
145
|
-
return 'id:' + (pos + 1) + '\n' + i;
|
|
146
|
-
}).join('\n---\n');
|
|
147
|
-
out = (await chat(msg, 0, 0, {
|
|
148
|
-
tools: {
|
|
149
|
-
google_search: {}
|
|
150
|
-
}
|
|
151
|
-
}));
|
|
152
|
-
({
|
|
153
|
-
// write '/tmp/fact.json',JSON.stringify out
|
|
154
|
-
// out = JSON.parse read '/tmp/fact.json'
|
|
155
|
-
content,
|
|
156
|
-
groundingMetadata: {groundingChunks, groundingSupports}
|
|
157
|
-
} = out);
|
|
158
|
-
if (!groundingChunks) {
|
|
159
|
-
return new Map();
|
|
160
|
-
}
|
|
161
|
-
content = content.parts[0].text;
|
|
162
|
-
console.log(content);
|
|
163
|
-
url_li = [];
|
|
164
|
-
for (x of groundingChunks) {
|
|
165
|
-
({
|
|
166
|
-
web: {uri}
|
|
167
|
-
} = x);
|
|
168
|
-
url_li.push(realUrl(uri));
|
|
169
|
-
}
|
|
170
|
-
url_li = (await Promise.all(url_li));
|
|
171
|
-
for (i of groundingSupports) {
|
|
172
|
-
i.groundingChunkIndices = urlLi(i.groundingChunkIndices, url_li);
|
|
173
|
-
}
|
|
174
|
-
map = new Map();
|
|
175
|
-
ref = txtLi(addUrl(content, groundingSupports));
|
|
176
|
-
for (i of ref) {
|
|
177
|
-
console.log(i);
|
|
178
|
-
if (!SHOW_KIND.includes((ref1 = i.失实度) != null ? ref1.split('[')[0] : void 0)) {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
li = map.get(i.id);
|
|
182
|
-
if (!li) {
|
|
183
|
-
li = [];
|
|
184
|
-
map.set(i.id, li);
|
|
185
|
-
}
|
|
186
|
-
li.push(i);
|
|
187
|
-
}
|
|
188
|
-
return map;
|
|
189
|
-
});
|
package/fmtJson.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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 RmCnSpace from '@3-/rm_cn_space';
|
|
7
|
-
|
|
8
|
-
export default async(chat, txt) => {
|
|
9
|
-
var pli;
|
|
10
|
-
if (!txt) {
|
|
11
|
-
return [];
|
|
12
|
-
}
|
|
13
|
-
txt = RmCnSpace(txt);
|
|
14
|
-
pli = (await partition(chat, txt));
|
|
15
|
-
return Promise.all(pli.map(async([title, li]) => {
|
|
16
|
-
console.log('\n---\n→ ' + title + '\n' + li.join('\n') + '\n---\n');
|
|
17
|
-
return [title, (await fmtSeg(chat, li.join('\n')))];
|
|
18
|
-
}));
|
|
19
|
-
};
|
package/fmtJsonMd.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env -S node --trace-uncaught --expose-gc --unhandled-rejections=strict --experimental-wasm-modules
|
|
2
|
-
var txtFmt;
|
|
3
|
-
|
|
4
|
-
import TxtLi from '@3-/txt_li';
|
|
5
|
-
|
|
6
|
-
txtFmt = (txt) => {
|
|
7
|
-
return TxtLi(txt).join('\n\n');
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default (title_json_li) => {
|
|
11
|
-
var li, md_li, title, x, y, 答, 问, 题;
|
|
12
|
-
md_li = [];
|
|
13
|
-
for (x of title_json_li) {
|
|
14
|
-
[title, li] = x;
|
|
15
|
-
md_li.push('# ' + title);
|
|
16
|
-
for (y of li) {
|
|
17
|
-
({题, 问, 答} = y);
|
|
18
|
-
答 = txtFmt(答).trim();
|
|
19
|
-
if ((答.startsWith('1. ')) || 答.endsWith(':') || 答.endsWith(':')) {
|
|
20
|
-
答 = '\n' + 答;
|
|
21
|
-
}
|
|
22
|
-
md_li.push('## ' + 题 + '\n问: ' + txtFmt(问).trimEnd() + '\n\n答: ' + 答 + '\n');
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return md_li.join('\n');
|
|
26
|
-
};
|
package/fmtSeg.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
import retry from '@3-/retry';
|
|
5
|
-
|
|
6
|
-
export default retry(async(chat, txt) => {
|
|
7
|
-
return (await chat(`将以下语音对话转为JSON数组,确保所有对话都被覆盖,不可遗漏。优化换行、排版,删除语气助词,在忠于原文的基础上增强可读性,严禁脑补术语。:\n` + txt, TOPIC_SCHEMA, "你是专业资深的秘书", {
|
|
8
|
-
generationConfig: {
|
|
9
|
-
temperature: 0
|
|
10
|
-
}
|
|
11
|
-
}));
|
|
12
|
-
});
|
package/lib.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
var MAX_RETRY;
|
|
2
|
-
|
|
3
|
-
import sleep from '@3-/sleep';
|
|
4
|
-
|
|
5
|
-
MAX_RETRY = 9;
|
|
6
|
-
|
|
7
|
-
export default (prefix, key_li) => {
|
|
8
|
-
var curl, key_len, n;
|
|
9
|
-
n = 0;
|
|
10
|
-
key_len = key_li.length;
|
|
11
|
-
key_li.sort(() => {
|
|
12
|
-
return Math.random() - 0.5;
|
|
13
|
-
});
|
|
14
|
-
curl = async(url, opt) => {
|
|
15
|
-
var err, headers, key, r, retry, status;
|
|
16
|
-
if (opt) {
|
|
17
|
-
({headers} = opt);
|
|
18
|
-
} else {
|
|
19
|
-
opt = {};
|
|
20
|
-
}
|
|
21
|
-
if (!headers) {
|
|
22
|
-
headers = opt.headers = {};
|
|
23
|
-
}
|
|
24
|
-
headers['Content-Type'] ??= 'application/json';
|
|
25
|
-
retry = 0;
|
|
26
|
-
while (true) {
|
|
27
|
-
key = key_li[n];
|
|
28
|
-
headers.Authorization = 'Bearer ' + key;
|
|
29
|
-
n = (n + 1) % key_len;
|
|
30
|
-
try {
|
|
31
|
-
r = (await fetch(prefix + url, opt));
|
|
32
|
-
({status} = r);
|
|
33
|
-
if (status === 200) {
|
|
34
|
-
return r.json();
|
|
35
|
-
} else if ([401, 403, 400].includes(status)) {
|
|
36
|
-
retry = MAX_RETRY;
|
|
37
|
-
console.error(prefix + ' api key ' + key + ' ' + r.status + ' ' + r.statusText);
|
|
38
|
-
console.error((await r.text()));
|
|
39
|
-
throw new Error(r);
|
|
40
|
-
} else if (status === 429) {
|
|
41
|
-
console.error(prefix + ' api key ' + key + ' TOO_MANY_REQUESTS, WAIT 9s');
|
|
42
|
-
try {
|
|
43
|
-
console.error((await r.text()));
|
|
44
|
-
} catch (error) {
|
|
45
|
-
err = error;
|
|
46
|
-
console.error(err);
|
|
47
|
-
}
|
|
48
|
-
await sleep(9e3);
|
|
49
|
-
} else {
|
|
50
|
-
throw new Error(status + ' : ' + (await r.text()));
|
|
51
|
-
}
|
|
52
|
-
} catch (error) {
|
|
53
|
-
err = error;
|
|
54
|
-
if (++retry < MAX_RETRY) {
|
|
55
|
-
console.error(retry, prefix + ' api key ' + key, ':', err);
|
|
56
|
-
await sleep(9e3);
|
|
57
|
-
}
|
|
58
|
-
throw err;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
return {
|
|
63
|
-
GET: curl,
|
|
64
|
-
POST: (url, body, opt = {}) => {
|
|
65
|
-
opt.body = JSON.stringify(body);
|
|
66
|
-
opt.method = 'POST';
|
|
67
|
-
return curl(url, opt);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
};
|
package/seg.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
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
|
-
split_li = (await chat(提示词, {
|
|
12
|
-
type: TYPE.ARRAY,
|
|
13
|
-
description: '章节和行号的列表',
|
|
14
|
-
minItems: 1,
|
|
15
|
-
items: {
|
|
16
|
-
type: TYPE.OBJECT,
|
|
17
|
-
properties: {
|
|
18
|
-
题: {
|
|
19
|
-
description: '章节标题,要概括章节内容',
|
|
20
|
-
type: TYPE.STRING
|
|
21
|
-
},
|
|
22
|
-
行: {
|
|
23
|
-
description: '该章最后一行的行号',
|
|
24
|
-
type: TYPE.INTEGER
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
required: ['题', '行']
|
|
28
|
-
}
|
|
29
|
-
}, '你是专业资深的秘书'));
|
|
30
|
-
return split_li.map((i) => {
|
|
31
|
-
return [i.题, i.行];
|
|
32
|
-
});
|
|
33
|
-
};
|