Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7977057be5 | |||
| 44cca59ec6 | |||
| 0dfa76dad6 |
107
www/app.js
107
www/app.js
@@ -5,7 +5,6 @@ const CONFIG = {
|
||||
apiUrl: 'https://open.bigmodel.cn/api/paas/v4/chat/completions',
|
||||
apiKey: '2259e33a1357460abe17919aaf81e73d.K44a8LPQTmFM5PKm',
|
||||
model: 'glm-4.5-air',
|
||||
thinkingModel: 'glm-z1-flash', // 智谱思考模型
|
||||
maxTokens: 2048
|
||||
};
|
||||
|
||||
@@ -395,10 +394,12 @@ async function streamGenerate(userMsgIndex) {
|
||||
sendBtn.disabled = true;
|
||||
|
||||
const aiMessageIndex = currentConversation.messages.length;
|
||||
|
||||
// 只有开启深度思考时才添加 thinking 字段
|
||||
currentConversation.messages.push({
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
thinking: '' // 思考内容
|
||||
...(enableThinking ? { thinking: '' } : {})
|
||||
});
|
||||
renderMessages();
|
||||
|
||||
@@ -406,12 +407,29 @@ async function streamGenerate(userMsgIndex) {
|
||||
const contentEl = lastMessageEl.querySelector('.message-content');
|
||||
const thinkingEl = lastMessageEl.querySelector('.thinking-content');
|
||||
|
||||
// 深度思考模式:思考块默认展开
|
||||
if (enableThinking && thinkingEl) {
|
||||
const thinkingBlock = lastMessageEl.querySelector('.thinking-block');
|
||||
if (thinkingBlock) thinkingBlock.classList.add('expanded');
|
||||
thinkingEl.innerHTML = '<span class="streaming-cursor">思考中...</span>';
|
||||
}
|
||||
|
||||
contentEl.innerHTML = '<span class="streaming-cursor">▌</span>';
|
||||
if (thinkingEl) thinkingEl.innerHTML = '<span class="streaming-cursor">▌</span>';
|
||||
|
||||
try {
|
||||
// 根据开关选择模型
|
||||
const model = enableThinking ? CONFIG.thinkingModel : CONFIG.model;
|
||||
// 构建请求体 - 统一使用 glm-4.5-air,通过 thinking 参数控制
|
||||
const requestBody = {
|
||||
model: CONFIG.model,
|
||||
messages: currentConversation.messages.slice(0, aiMessageIndex).map(m => ({
|
||||
role: m.role,
|
||||
content: m.content
|
||||
})),
|
||||
max_tokens: CONFIG.maxTokens,
|
||||
stream: true,
|
||||
thinking: {
|
||||
type: enableThinking ? 'enabled' : 'disabled'
|
||||
}
|
||||
};
|
||||
|
||||
const response = await fetch(CONFIG.apiUrl, {
|
||||
method: 'POST',
|
||||
@@ -419,15 +437,7 @@ async function streamGenerate(userMsgIndex) {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${CONFIG.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
messages: currentConversation.messages.slice(0, aiMessageIndex).map(m => ({
|
||||
role: m.role,
|
||||
content: m.content
|
||||
})),
|
||||
max_tokens: CONFIG.maxTokens,
|
||||
stream: true
|
||||
})
|
||||
body: JSON.stringify(requestBody)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -437,7 +447,7 @@ async function streamGenerate(userMsgIndex) {
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
let thinkingComplete = false; // 思考是否完成
|
||||
let thinkingOutputStarted = false; // 正式内容是否开始输出
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
@@ -457,24 +467,21 @@ async function streamGenerate(userMsgIndex) {
|
||||
const delta = data.choices?.[0]?.delta;
|
||||
|
||||
if (delta) {
|
||||
// 处理思考内容
|
||||
if (delta.reasoning_content || delta.thinking) {
|
||||
// 只有开启深度思考时才处理思考内容
|
||||
if (enableThinking && (delta.reasoning_content || delta.thinking)) {
|
||||
const thinkingChunk = delta.reasoning_content || delta.thinking;
|
||||
currentConversation.messages[aiMessageIndex].thinking += thinkingChunk;
|
||||
if (thinkingEl) {
|
||||
thinkingEl.innerHTML = renderMarkdown(currentConversation.messages[aiMessageIndex].thinking) + '<span class="streaming-cursor">▌</span>';
|
||||
// 思考时展开
|
||||
const thinkingBlock = lastMessageEl.querySelector('.thinking-block');
|
||||
if (thinkingBlock) thinkingBlock.classList.add('expanded');
|
||||
}
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// 处理正式回复内容
|
||||
if (delta.content) {
|
||||
// 如果开始输出正式内容,说明思考完成
|
||||
if (currentConversation.messages[aiMessageIndex].thinking && !thinkingComplete) {
|
||||
thinkingComplete = true;
|
||||
// 如果开启深度思考且开始输出正式内容,折叠思考块
|
||||
if (enableThinking && !thinkingOutputStarted && currentConversation.messages[aiMessageIndex].thinking) {
|
||||
thinkingOutputStarted = true;
|
||||
// 折叠思考内容
|
||||
const thinkingBlock = lastMessageEl.querySelector('.thinking-block');
|
||||
if (thinkingBlock) thinkingBlock.classList.remove('expanded');
|
||||
@@ -492,7 +499,7 @@ async function streamGenerate(userMsgIndex) {
|
||||
}
|
||||
|
||||
// 最终渲染
|
||||
if (thinkingEl && currentConversation.messages[aiMessageIndex].thinking) {
|
||||
if (thinkingEl && enableThinking && currentConversation.messages[aiMessageIndex].thinking) {
|
||||
thinkingEl.innerHTML = renderMarkdown(currentConversation.messages[aiMessageIndex].thinking);
|
||||
}
|
||||
contentEl.innerHTML = renderMarkdown(currentConversation.messages[aiMessageIndex].content);
|
||||
@@ -507,6 +514,58 @@ async function streamGenerate(userMsgIndex) {
|
||||
currentConversation.updatedAt = Date.now();
|
||||
saveConversations();
|
||||
renderMessages();
|
||||
|
||||
// 自动总结标题:第一次对话和每隔5次对话
|
||||
const totalMessages = currentConversation.messages.length;
|
||||
if (totalMessages === 1 || totalMessages % 5 === 0) {
|
||||
await generateConversationTitle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成对话标题
|
||||
async function generateConversationTitle() {
|
||||
if (!currentConversation) return;
|
||||
|
||||
// 构建对话摘要
|
||||
const conversationText = currentConversation.messages.map(m =>
|
||||
`${m.role === 'user' ? '用户' : 'AI'}: ${m.content.slice(0, 200)}`
|
||||
).join('\n');
|
||||
|
||||
const titlePrompt = `请用不超过10个字总结以下对话的主题,只输出标题,不要其他内容:
|
||||
${conversationText}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(CONFIG.apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${CONFIG.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: CONFIG.model,
|
||||
messages: [{ role: 'user', content: titlePrompt }],
|
||||
max_tokens: 50
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const newTitle = data.choices?.[0]?.message?.content?.trim();
|
||||
if (newTitle && newTitle.length > 0 && newTitle.length <= 15) {
|
||||
currentConversation.title = newTitle;
|
||||
currentConversation.updatedAt = Date.now();
|
||||
saveConversations();
|
||||
|
||||
// 更新页面标题显示
|
||||
const titleEl = document.querySelector('.header h1');
|
||||
if (titleEl) {
|
||||
titleEl.textContent = newTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成标题失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>AI助手</title>
|
||||
<link rel="stylesheet" href="style.css?v=2.2.1">
|
||||
<link rel="stylesheet" href="style.css?v=2.3.0">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="marked.min.js?v=2.2.1"></script>
|
||||
<script src="app.js?v=2.2.1"></script>
|
||||
<script src="marked.min.js?v=2.3.0"></script>
|
||||
<script src="app.js?v=2.3.0"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user