You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DeepSeek AI助手 - API客户端模块
该模块实现了与DeepSeek服务器通信的API客户端负责处理API请求、响应解析
以及日程和待办事项的智能提取功能。
"""
# 导入标准库模块
import requests # HTTP请求库
import json # JSON数据处理
from typing import Dict, List, Any # 类型注解
import logging # 日志模块
# 导入项目内部模块
from config.config import config_manager # 配置管理器
# 设置日志
logger = logging.getLogger(__name__)
class DeepSeekAPI:
"""DeepSeek API客户端类
该类负责与DeepSeek AI服务器进行通信处理所有API请求和响应。
支持模型切换、API密钥管理、消息发送以及日程和待办事项的智能提取。
"""
def __init__(self, logger=None):
"""初始化DeepSeek API客户端
从配置管理器加载初始配置包括API密钥、API地址和默认模型。
参数:
logger: 日志记录器对象用于记录API调用错误
"""
# 从配置管理器获取API密钥
self.api_key = config_manager.get_api_key()
# 从配置管理器获取API URL
self.api_url = config_manager.get_api_url()
# 从配置管理器获取默认模型
self.model = config_manager.get_default_model()
# 日志记录器
self.logger = logger
def set_model(self, new_model):
"""切换AI模型
参数:
new_model: 新的模型名称(例如:"deepseek-chat""deepseek-coder"
该方法用于切换不同的AI模型并将新模型保存到配置中以便下次启动时使用。
"""
self.model = new_model # 更新当前实例的模型设置
config_manager.set_default_model(new_model) # 保存到配置管理器
def set_api_key(self, new_key):
"""设置API密钥
参数:
new_key: 新的API密钥字符串
该方法用于更新API密钥并将新密钥保存到配置中以便下次启动时使用。
API密钥是与DeepSeek服务器通信的必要凭证。
"""
self.api_key = new_key # 更新当前实例的API密钥
config_manager.set_api_key(new_key) # 保存到配置管理器
def set_api_url(self, new_url):
"""设置API URL
参数:
new_url: 新的API端点URL字符串
该方法用于更新API服务器的URL地址并将新URL保存到配置中。
通常不需要修改此设置除非DeepSeek更改了API端点。
"""
self.api_url = new_url # 更新当前实例的API URL
config_manager.set_api_url(new_url) # 保存到配置管理器
def send_message(self, message):
"""发送单条消息获取AI回复
参数:
message: 用户消息内容字符串
返回:
成功时返回AI助手的回复内容字符串
失败时返回错误信息字符串
该方法是一个简化的API调用接口用于发送单条用户消息并获取AI回复。
它会自动构造包含单个用户消息的对话历史然后调用call_api方法。
"""
# 构造包含单条用户消息的对话历史
chat_history = [{"role": "user", "content": message}]
# 调用底层API方法获取回复
return self.call_api(chat_history)
def call_api(self, chat_history):
"""调用DeepSeek API获取AI回复
参数:
chat_history: 对话历史列表,格式为:
[{"role": "user/assistant", "content": "消息内容"}, ...]
返回:
成功时返回AI助手的回复内容字符串
失败时返回错误信息字符串
该方法是API客户端的核心方法负责
1. 验证API密钥的有效性
2. 构造符合API要求的请求头和参数
3. 发送HTTP POST请求到DeepSeek API服务器
4. 处理响应并提取AI回复内容
5. 捕获并处理各种可能的错误
"""
# 校验API Key是否已正确设置
if not self.api_key or self.api_key == "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx":
return "错误请先在config.py中填写有效的API Key"
# 构造API请求头
headers = {
"Content-Type": "application/json", # 指定请求内容类型为JSON
"Authorization": f"Bearer {self.api_key}" # 使用Bearer令牌认证
}
# 构造API请求参数
payload = {
"model": self.model, # 指定使用的AI模型
"messages": chat_history, # 对话历史
"temperature": config_manager.get_temperature(), # 温度参数(控制回复的创造性)
"max_tokens": config_manager.get_max_tokens(), # 最大令牌数(控制回复长度)
"stream": False # 是否使用流式响应(当前设置为非流式)
}
try:
# 发送HTTP POST请求到API服务器
response = requests.post(
self.api_url, # API端点URL
headers=headers, # 请求头
data=json.dumps(payload), # 将payload转换为JSON字符串
timeout=30 # 设置30秒超时
)
# 检查响应状态码如果不是2xx则抛出异常
response.raise_for_status()
# 解析JSON响应
result = response.json()
# 提取并返回AI助手的回复内容
return result["choices"][0]["message"]["content"].strip()
except requests.exceptions.Timeout:
# 处理请求超时错误
error_msg = "错误:请求超时,请检查网络!"
if self.logger:
self.logger.log("error", error_msg)
return error_msg
except requests.exceptions.RequestException as e:
# 处理其他HTTP请求错误
error_msg = f"API请求失败{str(e)}"
if self.logger:
self.logger.log("error", error_msg)
return error_msg
except KeyError:
# 处理API响应格式异常错误
error_msg = "错误API响应格式异常请检查API Key是否正确"
if self.logger:
self.logger.log("error", error_msg)
return error_msg
def parse_schedule(self, user_input):
"""解析用户输入中的日程信息
参数:
user_input: 用户输入的自然语言字符串,包含可能的日程信息
返回:
成功时返回日程信息字典,格式为:
{
"title": "日程标题",
"description": "日程描述",
"start_time": "ISO格式的开始时间",
"end_time": "ISO格式的结束时间",
"location": "地点",
"reminder": "提醒设置(可选)"
}
失败时返回None
该方法通过调用AI模型智能解析用户输入中的日程信息支持自然语言表达的各种日程格式
能够识别标题、时间、地点等关键信息并将其转换为结构化的JSON格式。
"""
from datetime import datetime
system_prompt = config_manager.get_schedule_assistant_prompt()
# 获取当前时间并添加到用户输入中帮助AI正确理解相对时间
current_time = datetime.now().isoformat()
user_input_with_time = f"当前时间:{current_time}\n\n{user_input}"
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_input_with_time}
]
response = self.call_api(messages)
# 检查API调用是否返回了错误信息
if response.startswith("错误:"):
return None
try:
# 尝试解析JSON响应
schedule_data = json.loads(response)
# 确保返回的数据至少包含title字段
if not isinstance(schedule_data, dict) or "title" not in schedule_data:
return None
return schedule_data
except json.JSONDecodeError:
# 如果解析失败返回None
return None
def parse_todo(self, user_message):
"""解析用户消息中的待办事项信息
参数:
user_message: 用户输入的自然语言字符串,包含可能的待办事项信息
返回:
成功时返回待办事项信息字典,格式为:
{
"title": "待办事项标题",
"description": "待办事项描述",
"due_date": "截止日期",
"priority": "优先级high/medium/low"
}
失败时返回None
该方法通过调用AI模型智能解析用户输入中的待办事项信息支持自然语言表达的各种待办事项格式
能够识别标题、截止日期、优先级等关键信息并将其转换为结构化的JSON格式。
"""
try:
from datetime import datetime
system_prompt = config_manager.get_todo_assistant_prompt()
# 获取当前时间并添加到用户输入中帮助AI正确理解相对时间
current_time = datetime.now().isoformat()
user_message_with_time = f"当前时间:{current_time}\n\n{user_message}"
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message_with_time}
]
response = self.call_api(messages)
# 检查API调用是否返回了错误信息
if response.startswith("错误:"):
return None
# 解析JSON响应
todo_data = json.loads(response)
# 确保返回的数据至少包含title字段
if not isinstance(todo_data, dict) or "title" not in todo_data:
return None
return todo_data
except json.JSONDecodeError as e:
print(f"解析JSON失败: {e}")
return None
except Exception as e:
print(f"解析待办事项失败: {e}")
return None