feat: AI自动添加功能,智能识别文本类型并整理数据
This commit is contained in:
@@ -164,6 +164,82 @@ def get_stats():
|
||||
return jsonify({'success': True, 'data': stats})
|
||||
|
||||
|
||||
@app.route('/api/ai-process', methods=['POST'])
|
||||
def ai_process():
|
||||
"""AI处理文本"""
|
||||
import requests
|
||||
|
||||
data = request.get_json()
|
||||
text = data.get('text', '').strip()
|
||||
|
||||
if not text:
|
||||
return jsonify({'success': False, 'error': '请输入文本内容'}), 400
|
||||
|
||||
# 大模型配置
|
||||
llm_url = "http://192.168.2.17:19007/v1/chat/completions"
|
||||
llm_key = "xxxx"
|
||||
|
||||
prompt = f"""请分析以下文本内容,识别其类型并提取关键信息。
|
||||
|
||||
文本内容:
|
||||
{text}
|
||||
|
||||
请按以下JSON格式返回结果(只返回JSON,不要其他内容):
|
||||
{
|
||||
"type": "text/link/column/todo",
|
||||
"title": "提取的标题(简短概括)",
|
||||
"content": "主要内容(如果是文本类型)",
|
||||
"url": "如果是链接或专栏,提取URL",
|
||||
"source": "如果是专栏,提取来源",
|
||||
"tags": ["相关标签1", "标签2"],
|
||||
"note": "补充说明或备注",
|
||||
"status": "如果是待办,默认pending",
|
||||
"priority": "如果是待办,默认medium"
|
||||
}
|
||||
|
||||
类型判断规则:
|
||||
- link: 包含http/https链接,且不是专栏订阅地址
|
||||
- column: 专栏订阅地址或RSS链接
|
||||
- todo: 包含任务、待办、提醒等关键词
|
||||
- text: 其他文本内容"""
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
llm_url,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {llm_key}"
|
||||
},
|
||||
json={
|
||||
"model": "auto",
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
"temperature": 0.3
|
||||
},
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
return jsonify({'success': False, 'error': f'模型调用失败: {response.status_code}'}), 500
|
||||
|
||||
result = response.json()
|
||||
content = result['choices'][0]['message']['content']
|
||||
|
||||
# 解析JSON
|
||||
import json
|
||||
import re
|
||||
|
||||
# 提取JSON部分
|
||||
json_match = re.search(r'\{.*\}', content, re.DOTALL)
|
||||
if json_match:
|
||||
parsed = json.loads(json_match.group())
|
||||
return jsonify({'success': True, 'data': parsed})
|
||||
else:
|
||||
return jsonify({'success': False, 'error': '无法解析模型返回'}), 500
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/search', methods=['GET'])
|
||||
def search_items():
|
||||
"""搜索条目"""
|
||||
@@ -263,6 +339,9 @@ INDEX_TEMPLATE = '''
|
||||
<option value="todo">待办</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-outline-info me-2" onclick="showAIAddModal()" title="AI自动添加">
|
||||
<i class="bi bi-robot"></i> AI添加
|
||||
</button>
|
||||
<button class="btn btn-outline-success me-2" onclick="exportData()" title="导出JSON">
|
||||
<i class="bi bi-download"></i> 导出
|
||||
</button>
|
||||
@@ -533,6 +612,44 @@ INDEX_TEMPLATE = '''
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI自动添加模态框 -->
|
||||
<div class="modal fade" id="aiAddModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="bi bi-robot"></i> AI自动添加</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">输入文本内容</label>
|
||||
<textarea id="aiInputText" class="form-control" rows="6" placeholder="粘贴文本、链接、笔记等,AI会自动识别并整理..."></textarea>
|
||||
</div>
|
||||
<div id="aiResult" style="display:none;">
|
||||
<hr>
|
||||
<h6>识别结果:</h6>
|
||||
<div id="aiResultContent" class="border rounded p-3 bg-light"></div>
|
||||
</div>
|
||||
<div id="aiLoading" style="display:none;">
|
||||
<div class="text-center py-3">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
<div class="mt-2">AI正在分析...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="aiProcessBtn" onclick="processAIInput()">
|
||||
<i class="bi bi-magic"></i> 分析并添加
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" id="aiConfirmBtn" style="display:none;" onclick="confirmAIAdd()">
|
||||
<i class="bi bi-check"></i> 确认添加
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const API_BASE = '/api';
|
||||
@@ -1132,6 +1249,95 @@ async function exportData() {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// AI自动添加
|
||||
let aiParsedData = null;
|
||||
|
||||
function showAIAddModal() {
|
||||
document.getElementById('aiInputText').value = '';
|
||||
document.getElementById('aiResult').style.display = 'none';
|
||||
document.getElementById('aiLoading').style.display = 'none';
|
||||
document.getElementById('aiProcessBtn').style.display = 'inline-block';
|
||||
document.getElementById('aiConfirmBtn').style.display = 'none';
|
||||
aiParsedData = null;
|
||||
new bootstrap.Modal(document.getElementById('aiAddModal')).show();
|
||||
}
|
||||
|
||||
async function processAIInput() {
|
||||
const text = document.getElementById('aiInputText').value.trim();
|
||||
if (!text) {
|
||||
alert('请输入文本内容');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载
|
||||
document.getElementById('aiLoading').style.display = 'block';
|
||||
document.getElementById('aiProcessBtn').disabled = true;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/ai-process`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
document.getElementById('aiLoading').style.display = 'none';
|
||||
document.getElementById('aiProcessBtn').disabled = false;
|
||||
|
||||
if (!data.success) {
|
||||
alert('AI分析失败: ' + data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
aiParsedData = data.data;
|
||||
|
||||
// 显示结果
|
||||
const typeLabels = { text: '📝 文本', link: '🔗 链接', column: '📰 专栏', todo: '✅ 待办' };
|
||||
let html = `<div><strong>类型:</strong> ${typeLabels[aiParsedData.type] || aiParsedData.type}</div>`;
|
||||
if (aiParsedData.title) html += `<div><strong>标题:</strong> ${aiParsedData.title}</div>`;
|
||||
if (aiParsedData.url) html += `<div><strong>URL:</strong> ${aiParsedData.url}</div>`;
|
||||
if (aiParsedData.content) html += `<div><strong>内容:</strong> ${aiParsedData.content.substring(0, 200)}${aiParsedData.content.length > 200 ? '...' : ''}</div>`;
|
||||
if (aiParsedData.tags && aiParsedData.tags.length) html += `<div><strong>标签:</strong> ${aiParsedData.tags.join(', ')}</div>`;
|
||||
if (aiParsedData.note) html += `<div><strong>备注:</strong> ${aiParsedData.note.substring(0, 100)}${aiParsedData.note.length > 100 ? '...' : ''}</div>`;
|
||||
if (aiParsedData.type === 'todo') {
|
||||
const statusLabels = { pending: '⏳ 待处理', in_progress: '🔄 进行中', completed: '✅ 已完成' };
|
||||
const priorityLabels = { low: '🟢 低', medium: '🟡 中', high: '🟠 高', urgent: '🔴 紧急' };
|
||||
html += `<div><strong>状态:</strong> ${statusLabels[aiParsedData.status] || 'pending'}</div>`;
|
||||
html += `<div><strong>优先级:</strong> ${priorityLabels[aiParsedData.priority] || 'medium'}</div>`;
|
||||
}
|
||||
|
||||
document.getElementById('aiResultContent').innerHTML = html;
|
||||
document.getElementById('aiResult').style.display = 'block';
|
||||
document.getElementById('aiProcessBtn').style.display = 'none';
|
||||
document.getElementById('aiConfirmBtn').style.display = 'inline-block';
|
||||
|
||||
} catch (e) {
|
||||
document.getElementById('aiLoading').style.display = 'none';
|
||||
document.getElementById('aiProcessBtn').disabled = false;
|
||||
alert('请求失败: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmAIAdd() {
|
||||
if (!aiParsedData) return;
|
||||
|
||||
const res = await fetch(`${API_BASE}/items`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(aiParsedData)
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
bootstrap.Modal.getInstance(document.getElementById('aiAddModal')).hide();
|
||||
refreshData();
|
||||
alert('添加成功!');
|
||||
} else {
|
||||
const data = await res.json();
|
||||
alert('添加失败: ' + data.error);
|
||||
}
|
||||
}
|
||||
|
||||
function debounce(fn, delay) {
|
||||
let timer;
|
||||
return function(...args) {
|
||||
|
||||
Reference in New Issue
Block a user