This commit is contained in:
2025-12-08 15:27:18 +08:00
parent f87538350c
commit 02ef7b3476
12 changed files with 1 additions and 471 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@kevisual/ai",
"version": "0.0.18",
"version": "0.0.19",
"description": "AI Center Services",
"main": "index.js",
"basename": "/root/ai-center-services",

View File

@@ -1,59 +0,0 @@
import { BailianProvider } from '../../provider/index.ts'
import dotenv from 'dotenv';
dotenv.config();
import { App } from '@kevisual/router'
import util from 'node:util';
const ai = new BailianProvider({
apiKey: process.env.BAILIAN_API_KEY || '',
model: 'qwen-turbo-latest',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
})
// const res = await ai.chat([
// {
// role: 'user',
// content: `1+1等于多少`
// },
// ],
// )
// // console.log('AI Response:', res);
// const content = res.choices[0].message?.content || ''
// console.log(util.inspect(res, { depth: null }))
// console.log('responseText', ai.responseText)
// const res = await ai.chatStream([
// {
// role: 'user',
// content: `1+1等于多少`
// },
// ],
// )
// // console.log('AI Response:', res);
// export const readStream = async (chatStream) => {
// let buffer = '';
// for await (const chunk of chatStream) {
// // chunk 已经是解码后的字符串,直接拼接即可
// buffer += chunk;
// }
// console.log('AI Response:', buffer);
// };
// await readStream(res);
const embe = await ai.generateEmbeddingCore([
'你好,世界!',
'Hello, world!',
], {
model: 'text-embedding-v4'
});
console.log('Embedding Response:', util.inspect(embe, { depth: null }));

View File

@@ -1,65 +0,0 @@
import { getChunks } from '../../provider/utils/chunk.ts';
const str = 'Hello world this is a test 你好沙盒 very big';
const str2 = `不能直接使用 tiktokenOpenAI的分词器来计算 Qwen 模型的 Token 数量因为两者的分词规则Tokenization和词表Vocabulary完全不同。
为什么不能混用?
词表不同
tiktoken 是 OpenAI 为 GPT 系列设计的(如 gpt-3.5-turbo, gpt-4其词表针对英语和代码优化。
Qwen 使用独立训练的 BPE 词表,对中文、多语言的支持更友好,分词粒度可能不同。
分词结果差异大
同一段文本tiktoken 和 Qwen 的分词结果可能完全不同。例如:
OpenAI (tiktoken): "你好" → ['你', '好']2 Tokens
Qwen: "你好" → ['你好']1 Token如果词表中包含该组合
性能问题
即使强制使用 tiktoken 计算 Qwen 的 Token结果也不准确可能导致
输入超出模型上下文限制(因统计偏差)。
API 计费或本地推理时出现意外错误。
正确方法:用 Qwen 的分词器
通过 Hugging Face transformers 加载 Qwen 的原生分词器:
python
复制
from transformers import AutoTokenizer
# 加载 Qwen 的分词器(以 Qwen-7B 为例)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B", trust_remote_code=True)
text = "你好Qwen模型"
tokens = tokenizer.tokenize(text) # 查看分词结果
token_count = len(tokenizer.encode(text, add_special_tokens=False))
print("分词结果:", tokens)
print("Token数量:", token_count)
常见问题
为什么需要 trust_remote_code=True
Qwen 的分词器是自定义实现的(非 Hugging Face 原生),此参数允许从模型仓库加载运行代码。
其他语言的 Token 计算?
Qwen 对非英语(如中文、日文)的分词效率较高,但仍需用其原生分词器统计。
与 tiktoken 的速度对比?
tiktoken 是纯 Python 实现速度较快Qwen 的分词器基于 Hugging Face可能稍慢但对齐模型需求。
总结
禁止混用tiktoken ≠ Qwen 分词器。
始终使用模型配套工具Qwen 需通过 transformers 加载其官方分词器。
中文场景特别注意Qwen 对中文的分词更高效,直接使用可避免偏差。
如果需要验证分词规则,可通过 tokenizer.vocab 查看词表内容(但注意词表通常较大)。`
const chunks = getChunks(str2);
console.log(chunks);

View File

@@ -1,18 +0,0 @@
import { Jimen } from "../jimeng/index.ts"
import dotenv from 'dotenv';
dotenv.config();
const jimeng = new Jimen({
token: process.env.JIMENG_TOKEN,
})
console.log("Generating image...");
await jimeng.generateImage({
prompt: "创建一幅未来城市的数字艺术作品,充满科技感和创新元素,色彩鲜艳,细节丰富",
resolution: "2k"
}).then((res) => {
console.log("Image generation response:", res);
}).catch((err) => {
console.error("Error generating image:", err);
});

View File

@@ -1,9 +0,0 @@
import { encryptAES, decryptAES } from '../..//provider/utils/parse-config.ts';
const plainx = process.env.API_KEY;
const decryptKey = process.env.DECRYPT_KEY;
const encrypt = encryptAES(plainx, decryptKey);
console.log('encrypt', encrypt);
const decrypt = decryptAES(encrypt, decryptKey);
console.log(decrypt);

View File

@@ -1,35 +0,0 @@
curl --request POST \
--url https://api.siliconflow.cn/v1/chat/completions \
--header 'Authorization: Bearer sk-qbiigkzoaamuqxtwlgkugodncebkfbosemadfubjrseobpvx' \
--header 'Content-Type: application/json' \
--data '{
"model": "Qwen/Qwen3-14B",
"messages": [
{
"role": "user",
"content": "计算a+b的值"
}
],
"stream": false,
"max_tokens": 512,
"stop": null,
"temperature": 0.7,
"top_p": 0.7,
"top_k": 50,
"frequency_penalty": 0.5,
"n": 1,
"response_format": {
"type": "text"
},
"tools": [
{
"type": "function",
"function": {
"description": "计算abc算法的值a=1b=2c=3",
"name": "compouted",
"parameters": {},
"strict": false
}
}
]
}'

View File

@@ -1,116 +0,0 @@
import { SiliconFlow } from '../../provider/chat-adapter/siliconflow.ts';
import { Ollama } from '../../provider/chat-adapter/ollama.ts';
import dotenv from 'dotenv';
dotenv.config();
const siliconflow = new SiliconFlow({
apiKey: process.env.SILICONFLOW_API_KEY,
model: 'Qwen/Qwen3-14B',
});
const ollama = new Ollama({
model: 'qwen3:32b',
apiKey: process.env.OLLAMA_API_KEY,
baseURL: process.env.OLLAMA_BASE_URL,
});
const main = async () => {
const usage = await siliconflow.getUsageInfo();
console.log(usage);
};
// 1. 定义工具函数
const availableFunctions: Record<string, (args: any) => Promise<any>> = {
get_time: async (args: { location: string }) => {
// 模拟API调用
console.log('time', args);
return {
time: '2022-03-22 12:00:00',
};
},
get_location: async (args: { symbol: string }) => {
// 模拟API调用
console.log('location', args);
return {
city: 'Beijing',
};
},
};
// main();
const funcCall = async (model = siliconflow) => {
const tools = [
{
type: 'function',
function: {
name: 'get_time',
description: '获取当前时间',
parameters: {
type: 'object',
properties: {
place: {
type: 'string',
description: '位置',
},
},
required: ['place'],
},
},
},
{
type: 'function',
function: {
name: 'get_location',
description: '获取当前位置',
// parameters: {},
parameters: {},
strict: false,
},
},
];
const messages: any[] = [{ role: 'user', content: '获取当前位置的当前时间' }];
const res = await model.chat(messages, {
tools: tools as any,
});
console.log(res.choices[0]);
const assistantMessage = res.choices[0].message;
const finish_reason = res.choices[0].finish_reason;
messages.push(assistantMessage);
let toolCalls = assistantMessage.tool_calls;
console.log("toolCalls", JSON.stringify(toolCalls));
let maxRetries = 3;
while (toolCalls && toolCalls.length > 0) {
// 处理每个函数调用
for (const toolCall of toolCalls) {
const functionName = toolCall.function.name;
const functionArgs = JSON.parse(toolCall.function.arguments);
// 调用本地函数
const functionResponse = await availableFunctions[functionName](functionArgs);
// 将结果添加到消息历史
messages.push({
role: 'tool',
name: functionName,
content: JSON.stringify(functionResponse),
tool_call_id: toolCall.id,
});
}
// 第二次调用 - 将函数结果发送给模型获取最终回复
const secondResponse = await model.chat(messages, {
tools: tools as any,
});
const finalMessage = secondResponse.choices[0].message;
messages.push(finalMessage);
const _toolCalls = finalMessage.tool_calls;
console.log("toolCalls", JSON.stringify(toolCalls) ,finalMessage.role);
toolCalls = _toolCalls ? _toolCalls : [];
maxRetries--;
if (maxRetries <= 0) {
break;
}
console.log('tool calls', toolCalls);
}
console.log(messages);
};
funcCall(ollama as any);

View File

@@ -1,26 +0,0 @@
import { ModelScope } from '../../provider/chat-adapter/model-scope.ts';
import { logger } from '../../modules/logger.ts';
import util from 'util';
import { config } from 'dotenv';
config();
const chat = new ModelScope({
apiKey: process.env.MODEL_SCOPE_API_KEY,
model: 'Qwen/Qwen2.5-Coder-32B-Instruct',
});
// chat.chat([{ role: 'user', content: 'Hello, world! 1 + 1 equals ?' }]);
const chatMessage = [{ role: 'user', content: 'Hello, world! 1 + 1 equals ?' }];
const main = async () => {
const res = await chat.test();
logger.info('test', res);
};
main();
const mainChat = async () => {
const res = await chat.chat(chatMessage as any);
logger.info('chat', res);
};
// mainChat();

View File

@@ -1,37 +0,0 @@
import { Knowledge } from '../../../../src/provider/knowledge/knowledge.ts';
import fs from 'fs';
import dotenv from 'dotenv';
dotenv.config();
const knowledge = new Knowledge({
embeddingModel: 'bge-m3:latest',
baseURL: 'https://ollama.xiongxiao.me/v1',
model: 'qwq:latest',
apiKey: process.env.OLLAMA_API_KEY,
});
const main = async () => {
const res = await knowledge.generateEmbeddingCore('Hello world this is a test 你好沙盒 very big');
fs.writeFileSync('docs/embedding.json', JSON.stringify(res, null, 2));
console.log(res);
};
main();
const main2 = async () => {
const text1 = 'Hello, world! this is a test';
const text2 = 'Hello, world! this is a test 2';
const text3 = 'Hello, world! this is a test 3';
const text4 = 'Hello, world! this is a test 4';
const text5 = 'Hello, world! this is a test 5';
const text6 = 'Hello, world! this is a test 6';
const text7 = 'Hello, world! this is a test 7';
const text8 = 'Hello, world! this is a test 8';
const text9 = 'Hello, world! this is a test 9';
const text10 = 'Hello, world! this is a test 10';
const res = await knowledge.generateEmbeddingCore([text1, text2, text3, text4, text5, text6, text7, text8, text9, text10]);
fs.writeFileSync('docs/embedding2.json', JSON.stringify(res, null, 2));
console.log(res);
};
// main2();

View File

@@ -1,86 +0,0 @@
import { Ollama } from '../../../../src/provider/chat-adapter/ollama.ts';
import util from 'util';
const chat = new Ollama({
baseURL: 'https://ollama.xiongxiao.me/v1',
apiKey: 'xiongxiao2233',
model: 'qwq:latest',
});
// chat.chat([{ role: 'user', content: 'Hello, world!' }]);
const main = async () => {
const res = await chat.test();
console.log(util.inspect(res, { depth: null, colors: true }));
};
// main();
const getJson = async () => {
const res = await chat.chat(
[
{ role: 'system', content: '把发送的数据返回给我对应的json只处理完发送的数据。如果发送了多个给我一个数组' },
// { role: 'user', content: '{"name":"John","age":30}' },
{ role: 'user', content: 'name: 张三' },
{ role: 'user', content: 'name: 李四, age: 18' },
],
{
response_format: {
type: 'json_schema',
json_schema: {
name: 'user',
description: '用户信息',
schema: {
type: 'object',
// properties: {
// name: { type: 'string' },
// // age: { type: 'number' },
// },
// // required: ['name', 'age'],
// required: ['name'],
properties: {
name: { type: 'string' },
age: { type: 'number' },
},
required: ['name', 'age'],
},
},
},
n: 10,
},
);
console.log(util.inspect(res, { depth: null, colors: true }));
};
// getJson();
const createChat1 = async () => {
const res = await chat.chat(
[
{ role: 'user', content: 'a=1, b=2, c=3' },
{ role: 'user', content: 'a+b+c=?' },
{ role: 'assistant', content: '给定的值为 \\( a = 1 \\), \\( b = 2 \\), \\( c = 3 \\)。\n' + '\n' + '因此,\\( a + b + c = 1 + 2 + 3 = 6 \\)。' },
{ role: 'user', content: 'a+b+c+4=?' },
],
{
model: 'qwen2.5:7b',
},
);
console.log(util.inspect(res, { depth: null, colors: true }));
};
// createChat1();
const getTags = async () => {
const res = await chat.listModels();
console.log(util.inspect(res, { depth: null, colors: true }));
};
// getTags();
const getRunModels = async () => {
const res = await chat.listRunModels();
console.log('current', new Date().toISOString());
console.log(util.inspect(res, { depth: null, colors: true }));
};
// getRunModels();

View File

@@ -1,7 +0,0 @@
import { ProviderManager } from '../..//provider/index.ts';
import { config } from 'dotenv';
config();
const providerConfig = { provider: 'ModelScope', model: 'Qwen/Qwen2.5-Coder-32B-Instruct', apiKey: process.env.MODEL_SCOPE_API_KEY };
const provider = await ProviderManager.createProvider(providerConfig);
const result = await provider.chat([{ role: 'user', content: '你好' }]);
console.log(result);

View File

@@ -1,12 +0,0 @@
/**
* 尝试从字符串中提取JSON对象
*/
export const getJsonFromString = (str: string) => {
try {
const jsonMatch = str.match(/```json\s*([\s\S]*?)\s*```/);
if (jsonMatch && jsonMatch[1]) {
return JSON.parse(jsonMatch[1]);
}
} catch (error) {}
return null;
};