Antony-Zhang commited on
Commit
683ecba
·
0 Parent(s):

init commit(包含LLM交互部分)

Browse files
LLM/spark_desk.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # -*- coding: utf-8 -*-
2
+ # # @Time : 2023/6/28 16:08
3
+ # # @Author : Fishead_East
4
+ # # @Email : ytzd2696@foxmail.com
5
+ # # @File : spark_desk.py
6
+ # # @Project : PromptArt
7
+ """
8
+ 使用LangChain自定义LLM
9
+ """
10
+ import os
11
+ from typing import Optional, List, Mapping, Any
12
+ import ssl
13
+
14
+ from langchain.llms.base import LLM
15
+ from langchain.callbacks.manager import CallbackManagerForLLMRun
16
+ from langchain.prompts import PromptTemplate
17
+ from langchain.chains import LLMChain, SimpleSequentialChain
18
+
19
+ import websocket
20
+ from LLM.webInteract.web_param import WsParamGPT
21
+ from LLM.webInteract.web_interact_gpt import Singleton, WS
22
+ from LLM.webInteract.web_interact_gpt import (on_close,
23
+ on_open,
24
+ on_error,
25
+ on_message)
26
+
27
+
28
+ @Singleton
29
+ class SparkDesk(LLM):
30
+ """
31
+ 讯飞星火的语言模型
32
+ """
33
+
34
+ url = "wss://spark-api.xf-yun.com/v1.1/chat"
35
+ APPID = os.getenv("APPID") # 环境变量
36
+ APIKey = os.getenv("APIKEY")
37
+ APISecret = os.getenv("APISECRET")
38
+
39
+ @property
40
+ def _llm_type(self) -> str:
41
+ return "SparkDesk"
42
+
43
+ @property
44
+ def _identifying_params(self) -> Mapping[str, Any]:
45
+ _param_dict = {
46
+ "url": self.url,
47
+ "APPID": self.APPID,
48
+ "APIKey": self.APIKey,
49
+ "APISecret": self.APISecret
50
+ }
51
+ return _param_dict
52
+
53
+ def _call(
54
+ self,
55
+ prompt: str,
56
+ stop: Optional[List[str]] = None,
57
+ run_manager: Optional[CallbackManagerForLLMRun] = None,
58
+ ) -> str:
59
+ ws_param = WsParamGPT(self.url, self.APPID, self.APIKey, self.APISecret)
60
+ websocket.enableTrace(False)
61
+ wsUrl = ws_param.create_url()
62
+ ws = WS(appid=ws_param.APPID,
63
+ url=wsUrl,
64
+ on_message=on_message,
65
+ on_error=on_error,
66
+ on_close=on_close,
67
+ on_open=on_open)
68
+ ws.question = prompt
69
+ ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) # 建立长连接
70
+ return ws.received_message
71
+
72
+
73
+ if __name__ == "__main__":
74
+ llm = SparkDesk()
75
+
76
+ # test
77
+ prompt_1 = PromptTemplate(
78
+ input_variables=["lastname"],
79
+ template="我的邻居姓{lastname},他生了个儿子,给他儿子起个名字",
80
+ )
81
+ chain_1 = LLMChain(llm=llm,
82
+ prompt=prompt_1)
83
+ # 创建第二条链
84
+ prompt_2 = PromptTemplate(
85
+ input_variables=["child_name"],
86
+ template="邻居的儿子名字叫{child_name},给他起一个小名",
87
+ )
88
+ chain_2 = LLMChain(llm=llm, prompt=prompt_2)
89
+
90
+ # 链接两条链
91
+ overall_chain = SimpleSequentialChain(chains=[chain_1, chain_2], verbose=True)
92
+
93
+ # 执行链,只需要传入第一个参数
94
+ catchphrase = overall_chain.run("王")
95
+ print(catchphrase)
96
+
97
+ # # 使用代理来确定如何使用LLM来采取行动
98
+ # # 这里省略代理的定义
99
+ #
100
+ # # 使用内存来在链或调用之间存储状态
101
+ # # 这里省略内存的定义
102
+ #
103
+ # # 测试应用程序
104
+ # response = chain.run("the meaning of life")
105
+ # print(response)
LLM/spark_desk_embedding.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # @Time : 2023/7/3 15:41
3
+ # @Author : Fishead_East
4
+ # @Email : ytzd2696@foxmail.com
5
+ # @File : spark_desk_embedding.py
6
+ # @Project : PromptArt
7
+ import os
8
+ import json
9
+ import requests
10
+ from typing import Optional, List, Mapping, Any
11
+
12
+ from langchain.llms.base import LLM
13
+ from langchain.callbacks.manager import CallbackManagerForLLMRun
14
+ from langchain.embeddings.base import Embeddings
15
+ from langchain.embeddings.huggingface import HuggingFaceEmbeddings
16
+
17
+ from LLM.webInteract.web_param import WsParamEmb
18
+
19
+
20
+ class SparkDeskEmbedding(object):
21
+ """
22
+ 讯飞星火的Embedding模型
23
+ """
24
+ url = r'https://knowledge-retrieval.cn-huabei-1.xf-yun.com/v1/aiui/embedding/query'
25
+ APPID: str = os.getenv("APPID")
26
+ APIKey: str = os.getenv("APIKEY")
27
+ APISecret: str = os.getenv("APISECRET")
28
+
29
+ def _get_param(self, text) -> Mapping[str, Any]:
30
+ """
31
+ 组织请求消息
32
+ :param text: 待向量化的文本
33
+ :return:
34
+ """
35
+ param_dict = {
36
+ 'header': {
37
+ 'app_id': self.APPID
38
+ },
39
+ 'payload': {
40
+ 'text': text
41
+ }
42
+ }
43
+ return param_dict
44
+
45
+ def embed_query(self, text: str,) -> List[float]:
46
+ """Compute query embeddings using the Spark Desk Model.
47
+
48
+ Args:
49
+ text: The text to embed.
50
+
51
+ Returns:
52
+ Embeddings for the text.
53
+ """
54
+ ws_param = WsParamEmb(self.url, self.APPID, self.APIKey, self.APISecret)
55
+ wsUrl = ws_param.create_url()
56
+ param_dict = self._get_param(text)
57
+ response = requests.post(url=wsUrl, json=param_dict) # 得到响应串
58
+ result_dict = json.loads(response.content.decode('utf-8'))
59
+ embed = json.loads(result_dict['payload']['text']['vector'])
60
+ return embed
61
+
62
+
63
+ if __name__ == '__main__':
64
+ llm_embed = SparkDeskEmbedding()
65
+ embed1 = llm_embed.embed_query("你好吗?")
66
+ print(embed1)
67
+ print(len(embed1))
68
+ print(embed1[:5])
69
+
70
+
71
+ # class SparkDeskEmbeddings(HuggingFaceEmbeddings):
72
+ # """重写HuggingFaceEmbeddings加载类"""
73
+ #
74
+ # client: Any #: :meta private:
75
+ # model_name: str = "SparkDeskEmbeddings"
76
+ #
77
+ # def __init__(self, **kwargs: Any):
78
+ # super().__init__(**kwargs)
79
+ # # self.client即向量化工具,为sentence_transformers包中的类
80
+ #
81
+ # def embed_query(self, text: str) -> List[float]:
82
+ # """Compute query embeddings using a HuggingFace transformer model.
83
+ #
84
+ # Args:
85
+ # text: The text to embed.
86
+ #
87
+ # Returns:
88
+ # Embeddings for the text.
89
+ # """
90
+ # text = text.replace("\n", " ")
91
+ # embedding = self.client.encode(text, normalize_embeddings=True)
92
+ # return embedding.tolist()
LLM/webInteract/web_interact_gpt.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ GPT交互的功能模块
3
+ """
4
+ import _thread as thread
5
+ import json
6
+ import websocket
7
+
8
+ from LLM.webInteract.web_param import gen_params_gpt
9
+
10
+
11
+ class Singleton(object):
12
+ """
13
+ 类装饰器,用于实现单例模式
14
+ """
15
+
16
+ def __init__(self, cls):
17
+ self._cls = cls
18
+ self._instance = {}
19
+
20
+ def __call__(self):
21
+ if self._cls not in self._instance:
22
+ self._instance[self._cls] = self._cls()
23
+ return self._instance[self._cls]
24
+
25
+
26
+ class WS(websocket.WebSocketApp):
27
+ """
28
+ WebSocketApp子类,添加消息变量
29
+ """
30
+
31
+ def __init__(self, appid, url, on_message, on_error, on_close, on_open):
32
+ self.appid = appid
33
+ self.received_message = ""
34
+ super(WS, self).__init__(url=url,
35
+ on_message=on_message,
36
+ on_error=on_error,
37
+ on_close=on_close,
38
+ on_open=on_open)
39
+
40
+
41
+ def on_error(ws, error):
42
+ """
43
+ 收到websocket错误的处理
44
+ """
45
+ print("### error:", error)
46
+
47
+
48
+ def on_close(ws):
49
+ """
50
+ 收到websocket关闭的处理
51
+ """
52
+ print("### closed ###")
53
+
54
+
55
+ def on_open(ws):
56
+ """
57
+ 收到websocket连接建立的处理
58
+ """
59
+ # print("### open ###")
60
+ thread.start_new_thread(run, (ws,))
61
+
62
+
63
+ def run(ws, *args):
64
+ # print("### run ###")
65
+ data = json.dumps(gen_params_gpt(appid=ws.appid, question=ws.question))
66
+ ws.send(data)
67
+
68
+
69
+ def on_message(ws, message):
70
+ """
71
+ 到websocket消息的处理
72
+ :param ws:
73
+ :param message:
74
+ :return:
75
+ """
76
+ # print("### message ###")
77
+ data = json.loads(message) # 将JSON字符串转化为Python对象
78
+ code = data['header']['code']
79
+ if code != 0:
80
+ print(f'请求错误: {code}, {data}')
81
+ ws.close()
82
+ # print("# closed 1 #")
83
+ else:
84
+ choices = data["payload"]["choices"] # 有效载荷数据
85
+ status = choices["status"] # 消息的状态
86
+ content = choices["text"][0]["content"] # 消息的内容
87
+ # 存储返回的消息
88
+ ws.received_message += content
89
+
90
+ # print(ws.received_message)
91
+ if status == 2: # 判断末尾消息
92
+ ws.close()
93
+ # print("# closed 2 #")
94
+ # print(ws.received_message)
95
+
96
+
LLM/webInteract/web_param.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 交互的参数模块
3
+ """
4
+ # -*- coding: utf-8 -*-
5
+ # @Time : 2023/7/4 09:35
6
+ # @Author : Fishead_East
7
+ # @Email : ytzd2696@foxmail.com
8
+ # @File : web_param.py
9
+ # @Project : PromptArt
10
+ import _thread as thread
11
+ import base64
12
+ import datetime
13
+ import hashlib
14
+ import hmac
15
+ from urllib.parse import urlparse
16
+ from datetime import datetime
17
+ from time import mktime
18
+ from urllib.parse import urlencode
19
+ from wsgiref.handlers import format_date_time
20
+
21
+
22
+ class WsParamGPT(object):
23
+ """
24
+ 交互参数类(GPT)
25
+ """
26
+ def __init__(self, url, app_id, api_key, api_secret):
27
+ self.url = url
28
+ self.host = urlparse(self.url).netloc
29
+ self.path = urlparse(self.url).path
30
+ self.APPID = app_id
31
+ self.APIKey = api_key
32
+ self.APISecret = api_secret
33
+
34
+ def create_signature_origin(self, date) -> str:
35
+ """拼接字符串,得到初始签名signature_origin"""
36
+ signature_origin = "host: " + self.host + "\n"
37
+ signature_origin += "date: " + date + "\n"
38
+ signature_origin += "GET " + self.path + " HTTP/1.1" # GPT交互使用Get
39
+ return signature_origin
40
+
41
+ def create_url(self):
42
+ """
43
+ 生成url [host主机 + date时间戳(RFC1123格式) + authorization认证信息]
44
+ """
45
+ # print("create_url启动")
46
+ # (可对参数进行逐步打印确认)
47
+ # 生成参数:时间戳date(RFC1123格式)
48
+ now = datetime.now()
49
+ date = format_date_time(mktime(now.timetuple()))
50
+
51
+ # 生成参数:认证信息authorization(base64编码)
52
+ signature_origin = self.create_signature_origin(date)
53
+ # 进行hmac-sha256算法进行加密; 得到签名的摘要signature_sha
54
+ signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
55
+ digestmod=hashlib.sha256).digest()
56
+
57
+ # 进行base64编码生成签名signature_sha_base64
58
+ signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8')
59
+
60
+ # 将字符串拼接成原始认证
61
+ authorization_origin = f'api_key="{self.APIKey}", algorithm="hmac-sha256", headers="host date request-line", ' \
62
+ f'signature="{signature_sha_base64}"'
63
+
64
+ # 进行base64编码生成最终认证信息authorization
65
+ authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
66
+ # 将请求的鉴权参数组合为字典
67
+ v = {
68
+ "authorization": authorization,
69
+ "date": date,
70
+ "host": self.host
71
+ }
72
+ # 拼接鉴权参数,生成url
73
+ url_final = self.url + '?' + urlencode(v)
74
+ # print("create_url完成")
75
+ return url_final
76
+
77
+
78
+ class WsParamEmb(WsParamGPT):
79
+ """
80
+ 交互参数类(Embedding)
81
+ """
82
+ def __init__(self, url, app_id, api_key, api_secret):
83
+ super(WsParamEmb, self).__init__(url, app_id, api_key, api_secret)
84
+
85
+ def create_signature_origin(self, date) -> str:
86
+ """拼接字符串,得到初始签名signature_origin"""
87
+ signature_origin = "host: " + self.host + "\n"
88
+ signature_origin += "date: " + date + "\n"
89
+ signature_origin += "POST " + self.path + " HTTP/1.1" # Embedding交互使用Post
90
+ return signature_origin
91
+
92
+
93
+ def gen_params_gpt(appid, question):
94
+ """
95
+ 通过appid和用户的提问, 生成请求参数
96
+ """
97
+ # print("gen_params启动")
98
+ data = {
99
+ "header": {
100
+ "app_id": appid, # AppID
101
+ "uid": "1234" # 用于区分不同的用户
102
+ },
103
+ "parameter": {
104
+ "chat": {
105
+ "domain": "general", # (必)指定访问的领域
106
+ "random_threshold": 0, # 温度系数temperature
107
+ "max_tokens": 2048, # 模型回答的tokens的最大长度,范围为[1,4096]
108
+ "auditing": "default"
109
+ # "top_k": 4 # 从k个候选中随机选择⼀个(⾮等概率),范围为[1,6]
110
+ # "chat_id": "1234" # 用于关联用户会话,需要保障用户对话的唯一性
111
+ }
112
+ },
113
+ "payload": {
114
+ "message": {
115
+ "text": [
116
+ {
117
+ "role": "user", # 对话角色,范围为[user,assistant]
118
+ "content": question # 用户和AI的对话内容,text下所有content累计tokens需要控制在8192内
119
+ }
120
+ ]
121
+ }
122
+ }
123
+ }
124
+ # print("gen_params完成")
125
+ return data
README.md ADDED
File without changes