├── .gitignore
├── BACKGROUND.md
├── LICENSE
├── README.en.md
├── README.md
├── WebChatGPTAPI
├── APIExample.ts
├── WolframLocalServer.py
├── WolframResponseExample.json
└── promptsExample.md
├── chatGPTChromeEnhance
├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── build.mjs
├── package-lock.json
├── package.json
├── src
│ ├── _locales
│ │ ├── de
│ │ │ └── messages.json
│ │ ├── en
│ │ │ └── messages.json
│ │ ├── es
│ │ │ └── messages.json
│ │ ├── fr
│ │ │ └── messages.json
│ │ ├── it
│ │ │ └── messages.json
│ │ ├── ja
│ │ │ └── messages.json
│ │ ├── ko
│ │ │ └── messages.json
│ │ ├── pt_BR
│ │ │ └── messages.json
│ │ ├── ru
│ │ │ └── messages.json
│ │ └── zh_CN
│ │ │ └── messages.json
│ ├── assets
│ │ └── icons
│ │ │ ├── icon128.png
│ │ │ ├── icon16.png
│ │ │ └── icon48.png
│ ├── background
│ │ └── bg.ts
│ ├── components
│ │ ├── apiEditor.tsx
│ │ ├── dropdown.tsx
│ │ ├── errorMessage.tsx
│ │ ├── footer.tsx
│ │ ├── navBar.tsx
│ │ ├── promptEditor.tsx
│ │ ├── socialIconButton.tsx
│ │ ├── toolbar.tsx
│ │ └── tooltipWrapper.tsx
│ ├── content-scripts
│ │ ├── api.ts
│ │ └── mainUI.tsx
│ ├── declaration.d.ts
│ ├── manifest.json
│ ├── options
│ │ ├── options.html
│ │ └── options.tsx
│ ├── style
│ │ └── base.css
│ └── util
│ │ ├── apiManager.ts
│ │ ├── createShadowRoot.ts
│ │ ├── elementFinder.ts
│ │ ├── icons.tsx
│ │ ├── localization.ts
│ │ ├── localizedStrings.json
│ │ ├── promptManager.ts
│ │ ├── regionOptions.json
│ │ ├── timePeriodOptions.json
│ │ └── userConfig.ts
├── tailwind.config.js
└── tsconfig.json
├── chatGPTEx
├── .dockerignore
├── .gitignore
├── Dockerfile
├── api_class.py
├── apikey.ini.example
├── cn_stopwords.txt
├── docker-compose.yaml
├── entrypoint.sh
├── main.py
├── main_GPT4.py
├── optimizeOpenAI.py
├── prompts
│ ├── APIExtraPrompt.txt
│ ├── APIPrompt.txt
│ ├── ReplySum.txt
│ ├── conversationSummary.txt
│ ├── prompts.json
│ └── summary.txt
├── promptsSearch.py
├── requirements.txt
├── search.py
├── static
│ ├── js
│ │ ├── newChat.js
│ │ ├── prism.js
│ │ └── tts.js
│ └── styles
│ │ ├── ChatGPT_logo.png
│ │ ├── FiraCode-Regular.ttf
│ │ ├── check.png
│ │ ├── person.jpg
│ │ ├── prism.css
│ │ └── style.css
└── templates
│ └── index.html
├── img
├── API.jpg
├── APIAnimation.png
├── WebPageBeautification.jpg
├── chatGPTChromeEnhance.png
├── date.jpg
├── math.jpg
├── mathjax.jpg
├── mode.jpg
├── newPage.jpg
├── promptCompletion.gif
├── stream.gif
├── web.jpg
├── webHistory.jpg
├── zhihuq0.jpg
├── zhihuq1.jpg
├── zhihuq2.jpg
└── zhihuq3.jpg
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vscode
3 | *.log
4 | *.temp
5 | chatGPTEx/backup/
6 |
--------------------------------------------------------------------------------
/BACKGROUND.md:
--------------------------------------------------------------------------------
1 | # Background
2 |
3 | [简体中文](./README.md) [English](./README.en.md) / Background
4 |
5 | [Toolformer Paper](https://arxiv.org/abs/2302.04761)
6 |
7 | "ChatGPT as Inherent Toolformer" means that ChatGPT has the ability to become a tool for various tasks without requiring additional adjustments. However, ChatGPT has some limitations such as being unable to connect to the internet and difficulty solving math problems. ToolFormer enables language models to use specific tools for different tasks. Can ChatGPT be equipped with ToolFormer's abilities? The challenge is how to adapt ToolFormer's API generation process to ChatGPT. Recent experiments demonstrate that given a specific prompt, ChatGPT has a natural ability to create APIs for text. Therefore, it can be concluded that ChatGPT has inherent ToolFormer capabilities!
8 |
9 | the subproject WebChatGPT enchanced is based on [WebChatGPT chrome extension](https://github.com/qunash/chatgpt-advanced)
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 qunash
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 | # Ex-ChatGPT - ChatGPT with ToolFormer
2 |
3 |    
4 |
5 | [简体中文](./README.md) English / [Background](./BACKGROUND.md)
6 |
7 | ChatGPT can act as a **tool former** without requiring adjustment, generating API requests for questions to assist in answering. Ex-ChatGPT enables ChatGPT to call external APIs, such as **WolframAlpha, Google, and WikiMedia**, to provide more accurate and timely answers.
8 |
9 | This project is divided into `Ex-ChatGPT` and `WebChatGPTEnhance`. The former is a service that uses the `GPT3.5 Turbo API` and **Google,WolframAlpha,WikiMedia APIs**, while the latter is a **browser extension** which update the origin WebChatGPT plugin to Enable adding external API, supportting ChatGPT webpage to call different APIs and prompts
10 |
11 | ## user interface display
12 |
13 | ### ExChatGPT
14 |
15 | 
16 |
17 | ### WebChatGPTEnhance
18 |
19 | 
20 |
21 | ## Highlights
22 |
23 | - **OAuth2.0 Multi User Management** (In webTest branch)
24 | - **Speech Dialogue Functionality**, utilizing Microsoft Azure API to optimize response time (around 1-2 seconds), featuring both speech recognition and text-to-speech capabilities, supporting multiple languages and voices, and allowing for customized voices.
25 | - **Docker and Proxy support**
26 | - **Redundant backup of chat history**
27 | - Support for OpenAI GPT-3.5 Turbo API
28 | - Ability for ChatGPT to call external API interfaces, such as **Google, WolframAlpha, and WikiMedia**
29 | - Clean up Google search results data to reduce token usage.
30 | - Automatic saving and loading of conversation history with **automatic compression**
31 | - Ability to display the number of tokens used
32 | - **API pool**
33 | - **Markdown and MathJax rendering**
34 | - API call process displayed with an animation, similar to Bing
35 | - Conversation management with **load and chat** modes similar to ChatGPT web page layout
36 | - **Shortcut keys** for quickly selecting modes (`Tab`), line breaks (`Shift+Enter`), sending messages (`Enter`), and selecting message history (`up` and `down`), similar to a terminal.
37 | - `stream` feature is similar to that of a typewriter, it responds faster with results instead of loading everything at once. Stream outputs the results in steps, as shown in the example:
38 | 
39 | - Automatic **prompt completion** in `Chat Mode` with support for fuzzy search, pinyin search, and custom prompt selection.The project comes with prompt from [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts).
40 | 
41 |
42 | ## To Do List
43 |
44 | - [ ] Mobile interface adaptation
45 | - [ ] OCR recognition of formula text in images sent by users
46 | - [ ] OAuth2.0 multi-user authentication
47 | - [ ] Call the diffusing model to generate images (achieving similar multimodal effects)
48 | - [ ] Further spider summary and data cleansing of web search results
49 | - [ ] Add code running API, and more APIs
50 | - [ ] Embedding alignment retrieval of chat records/local knowledge databases
51 |
52 |
53 | ## Installation
54 |
55 | ### Ex-chatGPT
56 |
57 | - `pip install`
58 | `pip install -r requirements.txt`
59 | - Copy `apikey.ini.example` and rename it as `apikey.ini`. Then, fill in your `API key` and `proxy` in `apikey.ini`. If you only have one OpenAI API key, you should delete `key1 = sk-xxxx;key2 = sk-xxxx`.
60 | - `Google api key and search engine id` [apply](https://developers.google.com/custom-search/v1/overview?hl=en)
61 | - `wolframAlpha app id key` [apply](https://products.wolframalpha.com/api/)
62 | - `openAI api key`(new feature) or `chatGPT access_token`(old version) [apply](https://platform.openai.com)
63 | - (optional, Text To Speech And Speech recognition) fill in `Azure API key` and `region` [apply](https://learn.microsoft.com/zh-cn/azure/cognitive-services/speech-service)
64 | - run the `main.py` and click the local url like `http://127.0.0.1:1234/`
65 | - change the mode in the selection box, now have `chat,detail,web,webDirect,WebKeyWord`
66 | - **Voice Conversation Chat**(optional feature), select language and voice in `chatGPTEx/static/styles/tts.js`, click the microphone on chat interface to `start/close` conversation mode.
67 |
68 | ### WebChatGPTEnhanceExtension
69 |
70 | - fill you `Googgle api key and client id` in `chatGPTChromeEhance/src/util/apiManager.ts/getDefaultAPI`
71 | - run `npm install`
72 | - run `npm run build-prod`
73 | - get the extension in `chatGPTChromeEhance/build`
74 | - add your `prompts` and `APIs` in option page.
75 | - `APIs` and `prompts` examples are in `/WebChatGPTAPI`
76 | - `wolframAlpha` needs to run local sever - `WebChatGPTAPI/WolframLocalServer.py`
77 |
78 | ## Mode Introduction
79 |
80 | ### Web Mode
81 |
82 | The Web Mode starts by asking ChatGPT a question directly. ChatGPT generates a series of API calls related to the query and uses the first returned result and question to verify and supplement with an API call. Finally, ChatGPT summarizes the information. Web Mode has a better chat ability than just summarizing the response.
83 |
84 | ### Chat Mode
85 |
86 | Chat Mode only calls the OpenAI API interface, similar to the web version of ChatGPT. You can search and choose different prompts by typing `/promtname`, and it supports fuzz search.
87 |
88 | ### WebDirect Mode
89 |
90 | WebDirect Mode first lets ChatGPT generate a series of API calls related to the query. Then, it directly calls a third-party API to search for the answer to each query, and finally, ChatGPT summarizes the information. WebDirect Mode is faster and relatively more accurate for single query information.
91 |
92 | ### Detail Mode
93 |
94 | Detail Mode is an extension of the WebDirect Mode, where an additional API call is made to supplement the current results with further inquiries (such as information not found in the previous search). Finally, ChatGPT summarizes the information.
95 |
96 | ### Keyword Mode
97 |
98 | Keyword Mode generates keywords directly from ChatGPT for querying, using DDG. It doesn't require other API keys. However, its accuracy is relatively poor.
99 |
100 | ## Update Log
101 |
102 | - Clean up Google search results data to reduce token usage.
103 | - Update all API proxy pools and add API restriction cooldown mechanism (Google 403 cooldown for 1 day).
104 | - Voice dialogue function, using Microsoft Azure API, optimized response speed, including voice recognition and text-to-speech, supporting multiple voices and languages, custom voice.
105 | - `stream` feature is similar to that of a typewriter, it responds faster with results instead of loading everything at once. Stream outputs the results in steps, as shown in the example:
106 | 
107 | - Redundant backup of chat history.
108 | - Auto-complete prompt selection in chat mode with **fuzzy search** and **Pinyin search** support.
109 | 
110 |
111 | - Update docker and proxy support.
112 |
113 | - Shortcut keys for quick mode selection (`Tab`) and line break (`Shift + Enter`), while `Enter` sends the message. Message history selection (`up`, `down`) is similar to a terminal.
114 |
115 | - Update chat history management sidebar.
116 | 
117 |
118 | - Update API calls processing animation.
119 | 
120 |
121 | - Web page beautification.
122 | 
123 |
124 | - Update Markdown and MathJax renderer.
125 | 
126 |
127 | - Update chat history token optimizer, and web mode can respond according to chat history. Add token cost counter.
128 | 
129 |
130 | - Update web chat mode selection in the webpage and optimize prompt and token cost. Restrict the token limit.
131 | 
132 |
133 | - Update better support for Chinese queries and add current date info.
134 | 
135 |
136 | - Update web chat mode and fix some bugs.
137 | - Update API configuration.
138 | - Update API pool.
139 | - Automatic saving and loading of conversation history, ChatGPT can retrieve previous conversations.
140 | - Update official support for OpenAI GPT3.5 Turbo API, which is super fast and cheap.
141 | - Update extra API calls and search summarizations to provide a more comprehensive and detailed answer.
142 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ex-ChatGPT - ChatGPT with ToolFormer
2 |
3 |    
4 |
5 | 简体中文 [English](./README.en.md) / [Background](./BACKGROUND.md)
6 |
7 | ChatGPT 是一个强大的工具平台,可以无需任何调整就生成 API 请求来协助回答问题。`Ex-ChatGPT` 使得 ChatGPT 能够调用外部 API,例如 **WolframAlpha、Google 和 WikiMedia**,以提供更准确和及时的答案。
8 |
9 | 这个项目分为 `Ex-ChatGPT` 和 `WebChatGPTEnhance` 两部分。前者是一个使用了 `GPT3.5 Turbo API`、**WolframAlpha、Google 和 WikiMedia** 等 API 的服务,能够提供更强大的功能和更准确的答案。后者是一个**浏览器扩展程序**,它更新了原有的 WebChatGPT 插件以支持添加外部 API,支持 ChatGPT 网页调用不同的 API 和提示。
10 |
11 | ## 交互界面
12 |
13 | ### ExChatGPT
14 |
15 | 
16 |
17 | ### WebChatGPTEnhance
18 |
19 | 
20 |
21 | ## Highlights
22 |
23 | - **OAuth2.0多用户鉴权管理** (见webTest分支)
24 | - **语音对话功能**,使用微软 Azure API,优化响应速度 ( 1-2 秒左右 ) ,包含语音识别和文字转语音,支持多种音色和语言,自定义声音。
25 | - **docker 和 proxy 支持**
26 | - **聊天记录冗余备份**
27 | - 支持 OpenAI GPT-3.5 Turbo API
28 | - 允许 ChatGPT 调用外部 API 接口 ( **Google,WolframAlpha,WikiMedia** )
29 | - 对 Google 搜索结果进行数据清洗, 减少token占用
30 | - 自动保存载入对话历史,**自动压缩对话**
31 | - **可显示使用的 Token 数量**
32 | - **API池**, **API** 冷却
33 | - **Markdown and MathJax** 渲染
34 | - 调用**API 过程显示动画**, 类似必应
35 | - **历史对话管理**载入,类 chatgpt 页面布局
36 | - **快捷键**快速选择模式 `Tab` 和换行 `Shift+Enter`,`Enter` 发送, `up`,`down` 选择历史发送消息,类似终端
37 | - `stream` 特性,它类似于打字机的效果,可以更快地响应结果。与一次性加载所有内容不同,stream会逐步输出结果。如示例中所示:
38 | 
39 | - `chat` 模式下**prompt 自动补全**选择,支持模糊搜索, 拼音搜索, 支持自定义 prompt, 项目中自带 [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) 中的 `prompt`
40 | 
41 |
42 | ## 计划更新
43 |
44 | - [ ] 移动端界面适配
45 | - [ ] 发送图片OCR识别公式文字
46 | - [x] OAuth2.0多用户鉴权 (见webTest分支)
47 | - [ ] 调用diffusing model生成图片(达到类似多模态效果)
48 | - [ ] 网页搜索结果进一步爬虫总结清洗数据
49 | - [ ] 增加代码运行API,以及更多API
50 | - [ ] 聊天记录/本地知识数据库embedding对齐检索
51 |
52 | ## 安装
53 |
54 | ### Ex-chatGPT Installation
55 |
56 | - `pip install`
57 | `pip install -r requirements.txt`
58 | - 将 `apikey.ini.example` 复制改名为 `apikey.ini`,然后在 `apikey.ini` 中填入你的 API 密钥, 以及代理 ( 如果只有一个 `openAI` 的 `API key`,将 `key1 = sk-xxxx; key2 = sk-xxxx` 删除即可 )
59 | - `Google api key and search engine id` [申请](https://developers.google.com/custom-search/v1/overview?hl=en)
60 | - `wolframAlpha app id key` [申请](https://products.wolframalpha.com/api/)
61 | - `openAI api key`( 新功能 ) 或 `chatGPT access_token` ( 旧版本 ) [申请](https://platform.openai.com)
62 | - (可选) 在 `apikey.ini` 中填写`Azure API key` 和 `region` [申请](https://learn.microsoft.com/zh-cn/azure/cognitive-services/speech-service)
63 | - 运行 `main.py` 并打开 `http://127.0.0.1:1234/`
64 | - 选择模式 ( 可以使用 `Tab` ) ,例如 `chat,detail,web,webDirect,WebKeyWord`
65 | - `chat` 模式下 使用 `\{promptname} {query}` 格式来模糊搜索选择 prompt
66 | - **快捷键**快速选择模式 `Tab` 和换行 `Shift+Enter`,`Enter` 发送, `up`,`down` 选择历史发送消息,类似终端
67 | - **语音对话聊天**(可选功能), 在 `chatGPTEx/static/styles/tts.js` 中选择语言和音色, 在聊天界面中点击麦克风`启动/关闭`对话模式
68 |
69 | #### Docker 快速部署
70 |
71 | ##### 方法一 使用构建好的镜像
72 |
73 | 1. 创建配置文件目录并拉取配置文件
74 |
75 | `mkdir config && wget https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/main/chatGPTEx/apikey.ini.example -O ./config/apikey.ini`
76 |
77 | 2. 编辑配置文件或者把编辑好的配置文件传到config文件夹下。
78 |
79 | `vim ./config/apikey.ini`
80 |
81 | 3. 拉取docker镜像
82 |
83 | `docker pull 0nlylty/exchatgpt:latest`
84 |
85 | 4. 创建容器
86 |
87 | ```bash
88 | docker run -dit \
89 | -v ~/config:/config \
90 | -p 5000:5000 \
91 | --name exchatgpt \
92 | --restart unless-stopped \
93 | 0nlylty/exchatgpt:latest
94 | ```
95 |
96 | ##### 方法二 自己构建镜像
97 |
98 | 1. 创建配置文件目录并拉取配置文件
99 |
100 | `mkdir config && wget https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/main/chatGPTEx/apikey.ini.example -O ./config/apikey.ini`
101 |
102 | 2. 编辑配置文件或者把编辑好的配置文件传到config文件夹下。
103 |
104 | `vim ./config/apikey.ini`
105 |
106 | 3. 构建并运行
107 |
108 | ```
109 | # 克隆代码
110 | git clone https://github.com/circlestarzero/EX-chatGPT.git --depth=1
111 | # 进入项目目录
112 | cd EX-chatGPT/chatGPTEx
113 | # 编辑docker-compose.yaml的挂载路径
114 | ~/config:/config # 冒号左边请修改为保存配置的路径
115 | # 配置补充完整后启动
116 | docker compose up -d
117 | ```
118 |
119 | ##### 使用
120 |
121 | ```bash
122 | # 访问
123 | http://your_ip:5000
124 |
125 | # 查看日志
126 | docker logs -f --tail 100 exchatgpt
127 | ```
128 |
129 | ### WebChatGPTEnhance Installation
130 |
131 | - 在 `chatGPTChromeEhance/src/util/apiManager.ts/getDefaultAPI` 中填入 Google API 信息
132 | - 运行 `npm install`
133 | - 运行 `npm run build-prod`
134 | - 在 `chatGPTChromeEhance/build` 中获取构建好的扩展
135 | - add your `prompts` and `APIs` in option page.
136 | - `APIs` and `prompts` examples are in `/WebChatGPTAPI`
137 | - `wolframAlpha` needs to run local sever - `WebChatGPTAPI/WolframLocalServer.py`
138 |
139 | ## 模式介绍
140 |
141 | ### Web Mode
142 |
143 | Web Mode 开始时会直接询问 ChatGPT 一个问题。ChatGPT 会生成一系列与查询相关的 API 调用,并使用第一个返回的结果和问题进行验证和补充。最后,ChatGPT 会对信息进行总结。Web Mode 具有比仅总结响应更好的聊天能力。
144 |
145 | ### Chat Mode
146 |
147 | Chat Mode 仅调用 OpenAI API 接口,类似于 ChatGPT 的 Web 版本。您可以通过输入 `/promtname` 来搜索和选择不同的提示,它还支持模糊搜索。
148 |
149 | ### WebDirect Mode
150 |
151 | WebDirect Mode 首先让 ChatGPT 生成一系列与查询相关的 API 调用。然后,它直接调用第三方 API 搜索每个查询的答案,最后 ChatGPT 对信息进行总结。WebDirect Mode 对于单个查询信息更快且相对更准确。
152 |
153 | ### Detail Mode
154 |
155 | Detail Mode 是 WebDirect Mode 的扩展,它会进行额外的 API 调用来补充当前结果中未找到的信息 ( 例如之前未搜索到的信息 ) 。最后,ChatGPT 对信息进行总结。
156 |
157 | ### Keyword Mode
158 |
159 | Keyword Mode 直接从 ChatGPT 中生成关键词进行查询,使用 DDG 进行查询,不需要其他 API 密钥。但是其准确性相对较差。
160 |
161 | ## 更新日志
162 |
163 | - **OAuth2.0多用户鉴权管理** (见webTest分支)
164 | - 对 Google 搜索结果进行数据清洗, 减少token占用
165 | - 更新所有API代理池, 增加API限制冷却机制(Google 403 冷却1天)
166 | - **语音对话功能**, 使用微软azureAPI, 优化响应速度, 包含识别语音和文字转语音, 支持多种音色和语言,自定义声音
167 | - `stream` 特性,它类似于打字机的效果,可以更快地响应结果。与一次性加载所有内容不同,stream会逐步输出结果。如示例中所示:
168 | 
169 | - 聊天记录冗余备份
170 | - chat 模式下 prompt 自动补全选择,支持模糊搜索和拼音搜索
171 |
172 | 
173 |
174 | - 更新 Docker 和 proxy 支持
175 | - 支持 OpenAI GPT-3.5 Turbo API,快速且价格低廉
176 | - 提供额外的 API 调用和搜索摘要,以提供更全面和详细的答案
177 | - 使用快捷键快速选择模式 `Tab` 和换行 `Shift+Enter`,同时使用 `Enter` 发送消息。使用 `up` 和 `down` 选择历史发送消息,类似终端操作
178 | - 更新历史对话管理,支持载入、删除和保存历史对话
179 |
180 | 
181 |
182 | - 更新 API 调用处理动画
183 |
184 | 
185 |
186 | - 页面美化
187 |
188 | 
189 |
190 | - Markdown 和 MathJax 渲染器
191 |
192 | 
193 |
194 | - 更新聊天记录 token 优化器,Web 模式可以根据聊天记录进行响应;添加 token 成本计数器
195 |
196 | 
197 |
198 | - 更新 Web 聊天模式选择,优化 prompt 和 token 成本,限制 token 上限
199 |
200 | 
201 |
202 | - 改进对中文查询的支持,并添加当前日期信息
203 |
204 | 
205 |
206 | - 更新 Web 聊天模式并修复一些错误
207 | - 更新 API 配置
208 | - 更新 API 池
209 | - 自动保存载入对话历史,ChatGPT 可联系之前对话
210 |
--------------------------------------------------------------------------------
/WebChatGPTAPI/APIExample.ts:
--------------------------------------------------------------------------------
1 | //Word Search API
2 | async function WordSearch(query, numResults, timePeriod, region) {
3 | const headers = new Headers({
4 | Origin: "https://chat.openai.com",
5 | "X-RapidAPI-Key": "YOUR_KEY",
6 | "X-RapidAPI-Host": "wordsapiv1.p.rapidapi.com", "Content-Type": "application/json",
7 | });
8 | const url = `https://wordsapiv1.p.rapidapi.com/words/${query}`;
9 | const response = await fetch(url, { method: "GET", headers, });
10 | const results = await response.json(); console.log(results); return results;
11 | }
12 |
13 | //Google Entity Search API
14 | async function GoogleEntitySearch(query, numResults, timePeriod, region) {
15 | const headers = new Headers({ Origin: "https://chat.openai.com", });
16 | const searchParams = new URLSearchParams();
17 | searchParams.set('query', query);
18 | searchParams.set('key', 'YOUR_KEY');
19 | searchParams.set('indent', 'true');
20 | searchParams.set('limit', numResults.toString());
21 | const url = `https://kgsearch.googleapis.com/v1/entities:search?${searchParams.toString()}`;
22 | console.log(url);
23 | const response = await fetch(url, { method: "GET", headers, });
24 | const results = await response.json(); console.log(results); return results;
25 | }
26 |
27 | //Google search API
28 | async function GoogleSearch(query, numResults, timePeriod, region) {
29 | const headers = new Headers({ Origin: "https://chat.openai.com", });
30 | const searchParams = new URLSearchParams();
31 | searchParams.set('q', query);
32 | searchParams.set('key', 'YOUR_KEY');
33 | searchParams.set('cx', 'YOUR_GOOGLE_CLIENT_ID');
34 | searchParams.set('c2coff', '0');
35 | searchParams.set('num', numResults.toString());
36 | const url = `https://customsearch.googleapis.com/customsearch/v1?${searchParams.toString()}`;
37 | console.log(url);
38 | const response = await fetch(url, { method: "GET", headers, });
39 | const results = await response.json();
40 | const items = results.items;
41 | const filteredData = items.map(({ title, link, snippet }) => ({
42 | title,
43 | link,
44 | snippet
45 | }));
46 | return filteredData;
47 | }
48 | //Google search summarazation API
49 | async function GoogleSearchSimply(query, numResults, timePeriod, region) {
50 | const headers = new Headers({ Origin: "https://chat.openai.com", });
51 | const searchParams = new URLSearchParams();
52 | searchParams.set('q', query);
53 | searchParams.set('key', 'YOUR_KEY');
54 | searchParams.set('cx', 'YOUR_GOOGLE_CLIENT_ID');
55 | searchParams.set('c2coff', '0');
56 | searchParams.set('num', numResults.toString());
57 | const url = `https://customsearch.googleapis.com/customsearch/v1?${searchParams.toString()}`;
58 | console.log(url);
59 | const response = await fetch(url, { method: "GET", headers, });
60 | const results = await response.json();
61 | const items = results.items;
62 | const filteredData = items.map(({ title, link, snippet }) => ({
63 | title,
64 | snippet
65 | }));
66 | return filteredData;
67 | }
68 |
69 | //summarization arcticle API
70 | async function ArcticleSummarazation(query, numResults, timePeriod, region) {
71 | const options = {
72 | method: 'POST',
73 | headers: {
74 | 'content-type': 'application/json',
75 | 'X-RapidAPI-Key': 'YOUR_KEY',
76 | 'X-RapidAPI-Host': 'tldrthis.p.rapidapi.com'
77 | },
78 | body: `{"url":"${query}","min_length":250,"max_length":300,"is_detailed":false}`
79 | };
80 | const response = await fetch('https://tldrthis.p.rapidapi.com/v1/model/abstractive/summarize-url/', options);
81 | const results = await response.json();
82 | const summarize = results.summarize;
83 | console.log(summarize);
84 | return summarize;
85 | }
86 |
87 |
88 | //article extract API
89 | async function arcticleExtraction(query, numResults, timePeriod, region) {
90 | const headers = new Headers({
91 | Origin: "https://chat.openai.com",
92 | "X-RapidAPI-Key": "YOUR_KEY",
93 | "X-RapidAPI-Host": "lexper.p.rapidapi.com"
94 | });
95 | const searchParams = new URLSearchParams();
96 | searchParams.set('url', query);
97 | searchParams.set('js_timeout', '30');
98 | searchParams.set('media', 'false')
99 | const url = `https://lexper.p.rapidapi.com/v1.1/extract?${searchParams.toString()}`;
100 | const response = await fetch(url, { method: "GET", headers, });
101 | const results = await response.json();
102 | let text = results.article.text;
103 | const sentences = text.split(/[,.?!。,?!]/);
104 | while (sentences.length > 0 && text.length > 2000) {
105 | const indexToDelete = Math.floor(Math.random() * sentences.length);
106 | sentences.splice(indexToDelete, 1);
107 | text = sentences.join('.');
108 | }
109 | return text;
110 | }
111 |
112 | //Wolfram alpha API
113 | async function WolframAlpha(query, numResults, timePeriod, region) {
114 | const response = await fetch(
115 | // YOUR LOCAL SEVER
116 | `http://localhost:1111?query=${query}`, {
117 | method: 'GET', mode: 'no-cors'
118 | });
119 | const results = response.json();
120 | console.log(results);
121 | return results;
122 | }
123 |
--------------------------------------------------------------------------------
/WebChatGPTAPI/WolframLocalServer.py:
--------------------------------------------------------------------------------
1 | from urllib.parse import urlparse, parse_qs
2 | import http.server
3 | import socketserver
4 | import socket
5 | import requests
6 | import json
7 | cookies = {
8 | # YOUR COOKIE
9 | }
10 |
11 | headers = {
12 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
13 | 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
14 | 'Cache-Control': 'no-cache',
15 | 'Connection': 'keep-alive',
16 | 'DNT': '1',
17 | 'Pragma': 'no-cache',
18 | 'Sec-Fetch-Dest': 'document',
19 | 'Sec-Fetch-Mode': 'navigate',
20 | 'Sec-Fetch-Site': 'none',
21 | 'Sec-Fetch-User': '?1',
22 | 'Upgrade-Insecure-Requests': '1',
23 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
24 | 'sec-ch-ua': '"Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"',
25 | 'sec-ch-ua-mobile': '?0',
26 | 'sec-ch-ua-platform': '"macOS"',
27 | }
28 |
29 |
30 | HOST = '127.0.0.1' # Localhost
31 | PORT = 1111 # Port number
32 | TIMEOUT = 5 # 5 second timeout
33 |
34 |
35 | class MyHandler(http.server.BaseHTTPRequestHandler):
36 | def do_OPTIONS(self):
37 | self.send_response(200, "ok")
38 | self.send_header('Access-Control-Allow-Origin', '*')
39 | self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS')
40 | self.send_header("Access-Control-Allow-Headers", "X-Requested-With")
41 | self.send_header("Access-Control-Allow-Headers", "Content-Type")
42 | self.end_headers()
43 |
44 | def do_GET(self):
45 | parsed_url = urlparse(self.path)
46 | query_params = parse_qs(parsed_url.query)
47 | query = query_params.get('query', [''])[0]
48 | params = {
49 | 'input': query,
50 | 'format': 'plaintext',
51 | 'output': 'JSON',
52 | 'appid': 'YOUR_APP_ID', #GET from WolframAlpha
53 | }
54 | responseFromWolfram = requests.get(
55 | 'https://api.wolframalpha.com/v2/query', params=params, cookies=cookies, headers=headers)
56 | pods = responseFromWolfram.json()['queryresult']['pods'][:5]
57 | print(pods)
58 | pods = json.dumps(pods)
59 | response = f"{pods}".encode()
60 | self.send_response(200)
61 | self.send_header('Access-Control-Allow-Origin', '*')
62 | self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS')
63 | self.send_header("Access-Control-Allow-Headers", "X-Requested-With")
64 | self.send_header('content-type', 'application/json; charset=utf-8')
65 | self.end_headers()
66 | self.wfile.write(bytes(response))
67 |
68 | while True:
69 | with socketserver.TCPServer((HOST, PORT), MyHandler) as httpd:
70 | httpd.socket.settimeout(TIMEOUT)
71 | print(f"Serving at port {PORT}")
72 | try:
73 | httpd.serve_forever()
74 | except socket.timeout:
75 | print(f"Timeout after {TIMEOUT} seconds")
76 |
--------------------------------------------------------------------------------
/WebChatGPTAPI/WolframResponseExample.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Input",
4 | "scanner": "Identity",
5 | "id": "Input",
6 | "position": 100,
7 | "error": false,
8 | "numsubpods": 1,
9 | "subpods": [
10 | {
11 | "title": "",
12 | "plaintext": "log(20)"
13 | }
14 | ],
15 | "expressiontypes": {
16 | "name": "Default"
17 | },
18 | "infos": {
19 | "text": "log(x) is the natural logarithm",
20 | "links": [
21 | {
22 | "url": "http://reference.wolfram.com/language/ref/Log.html",
23 | "text": "Documentation",
24 | "title": "Mathematica"
25 | },
26 | {
27 | "url": "http://functions.wolfram.com/ElementaryFunctions/Log",
28 | "text": "Properties",
29 | "title": "Wolfram Functions Site"
30 | },
31 | {
32 | "url": "http://mathworld.wolfram.com/NaturalLogarithm.html",
33 | "text": "Definition",
34 | "title": "MathWorld"
35 | }
36 | ]
37 | }
38 | },
39 | {
40 | "title": "Decimal approximation",
41 | "scanner": "Numeric",
42 | "id": "DecimalApproximation",
43 | "position": 200,
44 | "error": false,
45 | "numsubpods": 1,
46 | "primary": true,
47 | "subpods": [
48 | {
49 | "title": "",
50 | "plaintext": "2.9957322735539909934352235761425407756766016229890282301540079104..."
51 | }
52 | ],
53 | "expressiontypes": {
54 | "name": "Default"
55 | },
56 | "states": [
57 | {
58 | "name": "More digits",
59 | "input": "DecimalApproximation__More digits"
60 | }
61 | ]
62 | },
63 | {
64 | "title": "Property",
65 | "scanner": "Numeric",
66 | "id": "Property",
67 | "position": 300,
68 | "error": false,
69 | "numsubpods": 1,
70 | "subpods": [
71 | {
72 | "title": "",
73 | "plaintext": "log(20) is a transcendental number"
74 | }
75 | ],
76 | "expressiontypes": {
77 | "name": "Default"
78 | }
79 | }
80 | ]
--------------------------------------------------------------------------------
/WebChatGPTAPI/promptsExample.md:
--------------------------------------------------------------------------------
1 | # API prompt
2 |
3 | Here is my Input you need to Output:
4 | Input: {query}
5 | Instructions:
6 | Your task is to list calls to a Question Answering API to a piece of text. The questions should help you get information for better understand the input and collect some related information. You can call the API by writing "[(API)(question)]" where "question" is the full complete question you want to ask for better answer the input and the question should be as short as possible, and you can turn a large question into smaller ones by call the API more time.
7 | .The API available is ['WikiSearch','Calculator','Google'],you need to choose one to fill in each of the "[(API)(question)]"
8 | Here are some examples of API calls:
9 | {
10 | Input: Joe Biden was born in Scranton, Pennsylvania.
11 | Output: {"calls": [{"API": "Google", "query": "Where was Joe Biden born?"},]}
12 | Input: Coca-Cola, or Coke, is a carbonated soft drink manufactured by the Coca-Cola Company.
13 | Output: {"calls": [{"API": "Google", "query": "What other name is Coca-Cola known by?"},
14 | {"API": "Google", "query": "Who manufactures Coca-Cola?"}]}
15 | Input: The New England Journal of Medicine is a registered trademark of the MMS.
16 | Output: {"calls":[{"API": "Google", "query": "Who is the publisher of The New England Journal of Medicine?"}]}
17 | Input: Out of 1400 participants, 400 passed the test.
18 | Output: {"calls":[{"API": "Calculator", "query": "400 / 1400"}]}
19 | Input: The name derives from "la tortuga", the Spanish word for turtle.
20 | Output: {"calls": [{"API": "WikiSearch", "query": "tortuga"}]}
21 | Input: The Brown Act is California's law that requires legislative bodies, like city councils, to hold their meetings open to the public.
22 | Output: {"calls": [{"API": "WikiSearch", "query": "Brown Act"}]}
23 | Input: 孔子是怎么火烧赤壁的?
24 | Output: {"calls":[{"API": "WikiSearch","query": "孔子"},{"API": "WikiSearch","query": "火烧赤壁"}]}
25 | Input: 强化学习中的策略梯度是什么,用通俗语言解释下
26 | Output: {"calls": [{"API": "Google", "query": "强化学习中的策略梯度"}]}
27 | }
28 | Remember: dont't really call the api and output any other text, Just output the API call in JSON type
29 |
30 | # Wolfram Alpha prompt
31 |
32 | WolframAlpha JSON results:
33 |
34 | Desiderio Rodriguez
35 | WolframAlpha JSON results:
36 |
37 | {web_results}
38 |
39 | Instructions: Using the provided Wolfram Alpha JSON results, write a comprehensive and detailed corect reply to the given query. Using latex to express the whole results.
40 | Query: {query}
41 |
42 | # Web Extraction prompt
43 |
44 | a WebSite Extraction in JSON type:
45 | {web_results}
46 |
47 | Current date: 2/22/2023
48 | Instructions: Using the provided Extract Website results in JSON type, write a comprehensive reply to the given query in a understandable and relatively detailed language. Make sure to cite results using [the counting of sentence] notation after the reference.
49 | WebsitesURL: {query}
50 |
51 | # word flash card prompt
52 |
53 | Here is the word I give you : "what's the meaning of English word tout"
54 |
55 | Here is the web serach result of the word wraped by big brackets:
56 | {{web_results}}
57 |
58 | I need you to act as a anki helper for my English study for TOEFL exam, I've already give you the word and its search results. You need to help me memorize the words by offer some format of anki card which likes the 'front-back' form by using the codeblocks to enwrap the Front and Back part serparately, and combine some related information from the web result to the back of the anki card to better illustrate it.
59 |
60 | First, in the back of the card you need to illustrate this word in a easy-understanding and consice way whose word limitation is 64 words and maxium 4 different meanings, each meanings should less thant 16 words and give each meaning a setence example as its sublist.
61 | Second, list some synoms of it, which at least 5.
62 | Third, list some deriatives of it, which at least 5.
63 | And The back of the card content need to be the format of lists.
64 | Fourth, give an imagiable vivid scene of the word to help me memorize, which is less than 16 words.
65 | Here is an example wraped by big brackets to help you better understanding:
66 | {
67 | Front:
68 |
69 | ```markdown
70 | forge
71 | /fôrj/
72 | ```
73 |
74 | Back:
75 |
76 | ```markdown
77 | # Meanings:
78 | - verb
79 | - make or shape (a metal object) by heating it in a fire or furnace and beating or hammering it.
80 | - "he forged a great suit of black armor"
81 | - create (a relationship or new conditions).
82 | - "the two women forged a close bond"
83 | - noun
84 | - a blacksmith's workshop; a smithy.
85 | - "Tom is learning some skills in his Dad's forge"
86 |
87 | # Synonyms:
88 | create, shape, establish, strengthen, counterfeit, imitate.
89 |
90 | # Deriatives:
91 | unforgettable, reforge, unforgettably, unforgeable, unforgeability
92 |
93 | # Image:
94 | a blacksmith is forge in his workshop.
95 | ```
96 |
97 | }
98 | Remember, dont't output other unrelated information,just output the card,and output it more understandable and consice way
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "extends": [
4 | "preact",
5 | "plugin:@typescript-eslint/recommended",
6 | "plugin:tailwindcss/recommended"
7 | ],
8 | "rules": {
9 | "tailwindcss/no-custom-classname": "off"
10 | },
11 | "ignorePatterns": ["build/"]
12 | }
13 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 qunash
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/README.md:
--------------------------------------------------------------------------------
1 | > Below is WebChatGPT illustrations. I add the API manager function to this extension and fix some bugs.
2 |
3 | [link-chrome]: https://chrome.google.com/webstore/detail/chatgpt-advanced/lpfemeioodjbpieminkklglpmhlngfcn 'Chrome Web Store'
4 | [link-firefox]: https://addons.mozilla.org/en-US/firefox/addon/web-chatgpt/ 'Firefox Addons'
5 |
6 |
7 |
WebChatGPT
8 |
9 | [](https://discord.gg/nmCjvyVpnB) [](https://twitter.com/hahahahohohe)
10 |
11 |
12 | This browser extension `adds web access` capability to [ChatGPT](https://chat.openai.com/). Get much more relevant and up-to-date answers from the chatbot!
13 |
14 | 
15 |
16 |
17 |
18 |
19 | [
][link-chrome] [
][link-firefox]
20 |
21 | [
][link-chrome] [
][link-chrome]
22 |
23 | [
][link-firefox]
24 | [
][link-firefox]
25 |
26 |
27 |
28 | https://user-images.githubusercontent.com/3750161/214155508-5c1ad4d8-b565-4fe0-9ce7-e68aed11e73d.mp4
29 |
30 |
31 |
32 | Manual installation
33 |
34 | ### Chrome, Microsoft Edge, etc.
35 | 1. Download prebuilt chrome zip file from [here](https://github.com/qunash/chatgpt-advanced/tree/main/build).
36 | 2. Unzip the file.
37 | 3. Open `chrome://extensions` in Chrome / `edge://extensions` in Microsoft Edge.
38 | 4. Enable developer mode (top right corner).
39 | 5. Click on `Load unpacked` and select the unzipped folder.
40 | 6. Go to [ChatGPT](https://chat.openai.com/chat/) and enjoy!
41 |
42 | ### Firefox
43 | 1. Download prebuilt firefox zip file from [here](https://github.com/qunash/chatgpt-advanced/tree/main/build).
44 |
45 | #### Temporary installation, in official Release or Beta
46 | 1. Go to `about:debugging#/runtime/this-firefox`.
47 | 2. Click `Load Temporary Add-on` button, then select the zip file you re-zipped.
48 |
49 | #### Persistent installation, in Nightly or Developer Edition
50 | 1. Open Firefox, go to `about:config` and set `xpinstall.signatures.required` to `false`.
51 | 2. Go to `about:addons`
52 | 3. Click on the gear icon in the top right corner of the Add-ons page and select `Install Add-on From File`.
53 | 4. Select the zip file and click open.
54 | 5. Firefox will prompt you to confirm the installation of the addon. Click Install.
55 | 6. The addon will be installed and will appear in the list of installed addons on the Add-ons page.
56 | 7. Go to [ChatGPT](https://chat.openai.com/chat/) and enjoy!
57 |
58 |
59 |
60 | Build from source
61 |
62 | 1. `git clone https://github.com/qunash/chatgpt-advanced.git`
63 | 2. `npm install`
64 | 3. `npm run build-prod`
65 | 4. Grab your zip extension from `build/` folder
66 |
67 |
68 | ## Contributing
69 |
70 | Contributions are welcome! Please submit pull requests to the `dev` branch.
71 |
72 |
73 |
74 |
75 | Like this free project? Please consider [supporting me](https://www.buymeacoffee.com/anzorq) to keep it running.
76 |
77 | [ ](https://www.buymeacoffee.com/anzorq)
78 |
79 | [](https://visitor-badge.glitch.me) [](https://discord.gg/nmCjvyVpnB) [](https://twitter.com/hahahahohohe)
80 |
81 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/build.mjs:
--------------------------------------------------------------------------------
1 | import esbuild from "esbuild"
2 | import archiver from "archiver"
3 | import fs from "fs-extra"
4 | import tailwindcss from "tailwindcss"
5 | import autoprefixer from "autoprefixer"
6 | import postcssPlugin from "esbuild-style-plugin"
7 | import copyStaticFilesPlugin from "esbuild-copy-files-plugin"
8 | import path from 'path'
9 |
10 |
11 | const buildDir = "build"
12 | const minify = process.argv.includes("--minify")
13 |
14 | async function cleanBuildDir() {
15 | const entries = await fs.readdir(buildDir)
16 | for (const entry of entries) {
17 | if (path.extname(entry) === ".zip") continue
18 | await fs.remove(`${buildDir}/${entry}`)
19 | }
20 | }
21 |
22 | async function runEsbuild() {
23 | await esbuild.build({
24 | entryPoints: [
25 | "src/content-scripts/mainUI.tsx",
26 | "src/background/bg.ts",
27 | "src/options/options.tsx",
28 | ],
29 | outdir: buildDir,
30 | bundle: true,
31 | minify,
32 | treeShaking: true,
33 | define: {
34 | "process.env.NODE_ENV": '"production"',
35 | },
36 | jsxFactory: "h",
37 | jsxFragment: "Fragment",
38 | jsx: "automatic",
39 | loader: {
40 | ".png": "dataurl",
41 | },
42 | plugins: [
43 | postcssPlugin({
44 | postcss: {
45 | plugins: [tailwindcss, autoprefixer],
46 | },
47 | }),
48 | copyStaticFilesPlugin({
49 | source: ["src/manifest.json", "src/assets/"],
50 | target: buildDir,
51 | copyWithFolder: false,
52 | }),
53 | copyStaticFilesPlugin({
54 | source: ["src/options/options.html"],
55 | target: `${buildDir}/options`,
56 | copyWithFolder: false,
57 | }),
58 | copyStaticFilesPlugin({
59 | source: ["src/_locales/"],
60 | target: buildDir,
61 | copyWithFolder: true,
62 | }),
63 | ],
64 | })
65 | }
66 |
67 | async function createZipExtensionForBrowser(browser) {
68 | const manifest = await fs.readJson(`${buildDir}/manifest.json`)
69 | const version = manifest.version
70 | let archiveName = `build/webchatgpt-${version}-${browser}.zip`
71 |
72 | const archive = archiver("zip", { zlib: { level: 9 } })
73 | const stream = fs.createWriteStream(archiveName)
74 |
75 | archive.pipe(stream)
76 |
77 | await addFilesToZip(archive, browser)
78 |
79 | console.log(`Creating ${archiveName}…`)
80 | archive.finalize()
81 | }
82 |
83 | async function addFilesToZip(archive, browser) {
84 | const entries = await fs.readdir("build")
85 | for (const entry of entries) {
86 | const entryStat = await fs.stat(`build/${entry}`)
87 |
88 | if (entryStat.isDirectory()) {
89 | archive.directory(`build/${entry}`, entry)
90 | } else {
91 | if (path.extname(entry) === ".zip") continue
92 | if (entry === "manifest.json") continue
93 | archive.file(`build/${entry}`, { name: entry })
94 | }
95 | }
96 | if (browser === "firefox") {
97 | archive.file("src/manifest.v2.json", { name: "manifest.json" })
98 | } else if (browser === "chrome") {
99 | archive.file("build/manifest.json", { name: "manifest.json" })
100 | }
101 | }
102 |
103 | async function build() {
104 | await cleanBuildDir()
105 | await runEsbuild()
106 |
107 | const createZips = process.argv.includes("--create-zips")
108 | if (createZips) {
109 | try {
110 | await deleteZipsInBuildFolder()
111 |
112 | await createZipExtensionForBrowser("chrome")
113 | await createZipExtensionForBrowser("firefox")
114 | } catch (error) {
115 | console.error(error)
116 | }
117 | }
118 |
119 | console.log("Build complete")
120 |
121 | async function deleteZipsInBuildFolder() {
122 | const entries = await fs.readdir("build")
123 | for (const entry of entries) {
124 | if (path.extname(entry) === ".zip") {
125 | await fs.remove(`build/${entry}`)
126 | }
127 | }
128 | }
129 | }
130 |
131 | build()
132 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "webchatgpt",
4 | "version": "0.0.0",
5 | "license": "MIT",
6 | "scripts": {
7 | "build-dev": "node build.mjs",
8 | "build-prod": "node build.mjs --create-zips",
9 | "build-prod-min": "node build.mjs --create-zips --minify",
10 | "lint": "eslint ./src",
11 | "lint:fix": "eslint ./src --fix",
12 | "watch": "chokidar src -c \"npm run build-dev\""
13 | },
14 | "dependencies": {
15 | "fs": "^0.0.1-security",
16 | "lodash-es": "^4.17.21",
17 | "preact": "^10.10.0",
18 | "uuid": "^9.0.0"
19 | },
20 | "devDependencies": {
21 | "@types/lodash-es": "^4.17.6",
22 | "@types/uuid": "^9.0.0",
23 | "@types/webextension-polyfill": "^0.10.0",
24 | "@typescript-eslint/eslint-plugin": "^5.30.6",
25 | "@typescript-eslint/parser": "^5.52.0",
26 | "archiver": "^5.3.1",
27 | "autoprefixer": "^10.4.13",
28 | "chokidar-cli": "^3.0.0",
29 | "daisyui": "^2.47.0",
30 | "esbuild": "^0.16.17",
31 | "esbuild-copy-files-plugin": "^1.1.0",
32 | "esbuild-style-plugin": "^1.6.1",
33 | "eslint": "^8.20.0",
34 | "eslint-config-preact": "^1.3.0",
35 | "eslint-plugin-tailwindcss": "^3.8.3",
36 | "fs-extra": "^11.1.0",
37 | "jest": "^29.4.3",
38 | "preact-cli": "^2.2.1",
39 | "tailwindcss": "^3.2.4",
40 | "typescript": "^4.5.2",
41 | "webextension-polyfill": "^0.10.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/de/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: ChatGPT mit Internetzugang"
4 | },
5 | "appDesc": {
6 | "message": "Erweitern Sie Ihre ChatGPT-Prompts mit relevanten Ergebnissen aus dem Web."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/en/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: ChatGPT with internet access"
4 | },
5 | "appDesc": {
6 | "message": "Augment your ChatGPT prompts with relevant results from the web."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/es/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: ChatGPT con acceso a internet"
4 | },
5 | "appDesc": {
6 | "message": "Aumente sus prompts ChatGPT con resultados relevantes de la web."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/fr/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: ChatGPT avec accès à Internet"
4 | },
5 | "appDesc": {
6 | "message": "Augmentez vos prompts ChatGPT avec des résultats pertinents provenant du Web."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/it/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: ChatGPT con accesso a Internet"
4 | },
5 | "appDesc": {
6 | "message": "Migliora i tuoi prompt di ChatGPT con risultati pertinenti dal web."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/ja/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: インターネットにアクセスできる ChatGPT"
4 | },
5 | "appDesc": {
6 | "message": "Webから関連する結果を使用して、ChatGPTのプロンプトを拡張します。"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/ko/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: 인터넷 액세스가 가능한 ChatGPT"
4 | },
5 | "appDesc": {
6 | "message": "웹에서 관련 결과를 사용하여 ChatGPT 프롬프트를 향상시킵니다."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/pt_BR/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: ChatGPT com acesso à internet"
4 | },
5 | "appDesc": {
6 | "message": "Aumente seus prompts do ChatGPT com resultados relevantes da web."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/ru/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT: ChatGPT с доступом в интернет"
4 | },
5 | "appDesc": {
6 | "message": "Дополните запросы к ChatGPT подходящими результатами из сети Интернет."
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/_locales/zh_CN/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": {
3 | "message": "WebChatGPT:可访问互联网的 ChatGPT"
4 | },
5 | "appDesc": {
6 | "message": "使用来自网络的相关结果增强您的 ChatGPT 提示。"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/assets/icons/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/chatGPTChromeEnhance/src/assets/icons/icon128.png
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/assets/icons/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/chatGPTChromeEnhance/src/assets/icons/icon16.png
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/assets/icons/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/chatGPTChromeEnhance/src/assets/icons/icon48.png
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/background/bg.ts:
--------------------------------------------------------------------------------
1 | import Browser from 'webextension-polyfill'
2 |
3 |
4 | const manifest_version = Browser.runtime.getManifest().manifest_version
5 |
6 |
7 | Browser.runtime.onInstalled.addListener(async () => openChatGPTWebpage())
8 |
9 | function openChatGPTWebpage() {
10 | Browser.tabs.create({
11 | url: "https://chat.openai.com/chat",
12 | })
13 | }
14 |
15 | // open chatgpt webpage when extension icon is clicked
16 | if (manifest_version == 2) {
17 | Browser.browserAction.onClicked.addListener(openChatGPTWebpage)
18 | } else {
19 | Browser.action.onClicked.addListener(openChatGPTWebpage)
20 | }
21 |
22 |
23 | Browser.runtime.onMessage.addListener((request) => {
24 | if (request === "show_options") {
25 | Browser.runtime.openOptionsPage()
26 | }
27 | if (request === "show_API_options") {
28 | Browser.runtime.openOptionsPage()
29 | }
30 | })
31 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/apiEditor.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import { useState, useEffect, useRef, useLayoutEffect } from 'preact/hooks'
3 | import { getTranslation, localizationKeys } from 'src/util/localization'
4 | import { deleteAPI, getDefaultAPI, getsavedAPIs, API, saveAPI } from 'src/util/apiManager'
5 | const APIEditor = (
6 | ) => {
7 | const [savedAPIS, setSavedAPIS] = useState([])
8 | const [api, setAPI] = useState(getDefaultAPI())
9 | const [deleteBtnText, setDeleteBtnText] = useState("delete")
10 | const [showErrors, setShowErrors] = useState(false)
11 | const [nameError, setNameError] = useState(false)
12 | const [textError, setTextError] = useState(false)
13 |
14 | useLayoutEffect(() => {
15 | updateSavedAPIS()
16 | }, [])
17 |
18 | const updateSavedAPIS = async () => {
19 | const apis = await getsavedAPIs()
20 | setSavedAPIS(apis)
21 | if (api.uuid === 'default') {
22 | setAPI(apis[0])
23 | }
24 | }
25 |
26 | useEffect(() => {
27 | setNameError(api.name.trim() === '')
28 | setTextError(api.text.trim() === '')
29 | }, [api])
30 |
31 | async function updateList() {
32 | getsavedAPIs().then(sp => {
33 | setSavedAPIS(sp)
34 | })
35 | }
36 |
37 | const handleSelect = (api:API) => {
38 | setShowErrors(false)
39 | setAPI(api)
40 | setDeleteBtnText("delete")
41 | }
42 |
43 | const handleAdd = () => {
44 | setShowErrors(false)
45 | setAPI({ name: '', text: '' })
46 | setDeleteBtnText("delete")
47 | if (nameInputRef.current) {
48 | nameInputRef.current.focus()
49 | }
50 | }
51 |
52 | const handleSave = async () => {
53 | setShowErrors(true)
54 | if (nameError || textError) {
55 | return
56 | }
57 | await saveAPI(api)
58 | await updateList()
59 | }
60 |
61 | const handleDeleteBtnClick = () => {
62 | if (deleteBtnText === "delete") {
63 | setDeleteBtnText("check")
64 | } else {
65 | handleDelete()
66 | }
67 | }
68 |
69 | const handleDelete = async () => {
70 | await deleteAPI(api)
71 | updateList()
72 | handleAdd()
73 | }
74 |
75 | const nameInputRef = useRef(null)
76 | const textareaRef = useRef(null)
77 | const handleTextareaChange = (e: Event) => {
78 | const text = (e.target as HTMLTextAreaElement).value
79 | if (text !== api.text) {
80 | setTextError(false)
81 | setAPI({ ...api, text })
82 | }
83 | }
84 | const actionToolbar = (
85 |
88 |
92 | {getTranslation(localizationKeys.buttons.save)}
93 |
94 |
95 | )
96 |
97 | const APIList = (
98 |
99 |
102 |
103 | add_circle
104 |
105 | "New API"
106 |
107 |
121 |
122 | )
123 |
124 | const nameInput = (
125 | {
133 | setNameError(false)
134 | setAPI({ ...api, name: (e.target as HTMLInputElement).value })
135 | }}
136 | disabled={api.uuid === 'default'}
137 | />
138 | )
139 |
140 | const btnDelete = (
141 |
147 |
148 | {deleteBtnText}
149 |
150 |
151 | )
152 |
153 | const textArea = (
154 |
163 | )
164 |
165 | return (
166 |
167 |
168 | {APIList}
169 |
170 |
171 |
172 |
173 | {nameInput}
174 | {btnDelete}
175 |
176 | {textArea}
177 |
178 | {actionToolbar}
179 |
180 |
181 | )
182 | }
183 |
184 | export default APIEditor
185 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/dropdown.tsx:
--------------------------------------------------------------------------------
1 | import { h, JSX } from "preact"
2 |
3 | function Dropdown(props: {
4 | value: string | number
5 | onChange: any
6 | options: Array<{ value: string | number; label: string }>
7 | }): JSX.Element {
8 |
9 | return (
10 |
14 | {props.options.map(({ value, label }) => (
15 | {label}
16 | ))}
17 |
18 | )
19 | }
20 |
21 | export default Dropdown
22 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/errorMessage.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import { useEffect, useState } from 'preact/hooks'
3 |
4 |
5 | function ErrorMessage({ message }) {
6 | const [show, setShow] = useState(true)
7 |
8 | useEffect(() => {
9 | const timer = setTimeout(() => {
10 | setShow(false)
11 | }, 10000)
12 | return () => clearTimeout(timer)
13 | }, [])
14 |
15 | return show && (
16 | //
17 |
18 | An error occurred
19 | {message}
20 | Check the console for more details. (Ctrl+Shift+J)
21 |
22 | )
23 | }
24 |
25 | export default ErrorMessage
26 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/footer.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import Browser from 'webextension-polyfill'
3 |
4 | function Footer() {
5 | return (
6 |
7 | Powered by cc
8 |
9 | )
10 | }
11 |
12 | export default Footer
13 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/navBar.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import { icons } from 'src/util/icons'
3 | import { getTranslation, Languages, localizationKeys } from 'src/util/localization'
4 | import Browser from 'webextension-polyfill'
5 | import IconButton from './socialIconButton'
6 | import TooltipWrapper from './tooltipWrapper'
7 |
8 |
9 | const NavBar = (
10 | props: {
11 | language: string,
12 | onLanguageChange: (language: string) => void,
13 | }
14 | ) => {
15 |
16 | const version = Browser.runtime.getManifest().version
17 |
18 | return (
19 |
20 |
21 |
WebChatGPT
22 |
{version}
23 |
24 |
25 |
26 |
27 |
28 | {icons.language}
29 | {icons.expand}
30 |
31 |
32 | {
33 | Object.entries(Languages).map(([value, label]) => (
34 | props.onLanguageChange(value)}
37 | >
38 |
39 | {label}
40 |
41 |
42 |
43 | ))
44 | }
45 |
46 |
47 |
48 |
49 |
50 |
51 |
)
52 | }
53 |
54 | export default NavBar
55 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/promptEditor.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import { useState, useEffect, useRef, useLayoutEffect } from 'preact/hooks'
3 | import { getTranslation, localizationKeys } from 'src/util/localization'
4 | import { deletePrompt, getDefaultPrompt, getSavedPrompts, Prompt, savePrompt } from 'src/util/promptManager'
5 | import TooltipWrapper from './tooltipWrapper'
6 |
7 | const PromptEditor = (
8 | props: {
9 | language: string
10 | }
11 | ) => {
12 | const [savedPrompts, setSavedPrompts] = useState
([])
13 | const [prompt, setPrompt] = useState(getDefaultPrompt())
14 | const [hasWebResultsPlaceholder, setHasWebResultsPlaceholder] = useState(false)
15 | const [hasQueryPlaceholder, setHasQueryPlaceholder] = useState(false)
16 | const [deleteBtnText, setDeleteBtnText] = useState("delete")
17 |
18 | const [showErrors, setShowErrors] = useState(false)
19 | const [nameError, setNameError] = useState(false)
20 | const [textError, setTextError] = useState(false)
21 | const [webResultsError, setWebResultsError] = useState(false)
22 | const [queryError, setQueryError] = useState(false)
23 |
24 | useLayoutEffect(() => {
25 | updateSavedPrompts()
26 | }, [])
27 |
28 | const updateSavedPrompts = async () => {
29 | const prompts = await getSavedPrompts()
30 | setSavedPrompts(prompts)
31 | if (prompt.uuid === 'default') {
32 | setPrompt(prompts[0])
33 | }
34 | }
35 |
36 | useEffect(() => {
37 | updateSavedPrompts()
38 | }, [props.language])
39 |
40 | useEffect(() => {
41 | updatePlaceholderButtons(prompt.text)
42 | }, [prompt])
43 |
44 | useEffect(() => {
45 | setNameError(prompt.name.trim() === '')
46 | setTextError(prompt.text.trim() === '')
47 | // setWebResultsError(!prompt.text.includes('{web_results}'))
48 | setQueryError(!prompt.text.includes('{query}'))
49 | }, [prompt])
50 |
51 | async function updateList() {
52 | getSavedPrompts().then(sp => {
53 | setSavedPrompts(sp)
54 | })
55 | }
56 |
57 | const handleSelect = (prompt: Prompt) => {
58 | setShowErrors(false)
59 | setPrompt(prompt)
60 | setDeleteBtnText("delete")
61 | }
62 |
63 |
64 | const handleAdd = () => {
65 | setShowErrors(false)
66 | setPrompt({ name: '', text: '' })
67 | setDeleteBtnText("delete")
68 | if (nameInputRef.current) {
69 | nameInputRef.current.focus()
70 | }
71 | }
72 |
73 | const handleSave = async () => {
74 | setShowErrors(true)
75 | if (nameError || textError || webResultsError || queryError) {
76 | return
77 | }
78 |
79 | await savePrompt(prompt)
80 | await updateList()
81 | }
82 |
83 | const handleDeleteBtnClick = () => {
84 | if (deleteBtnText === "delete") {
85 | setDeleteBtnText("check")
86 | } else {
87 | handleDelete()
88 | }
89 | }
90 |
91 | const handleDelete = async () => {
92 | await deletePrompt(prompt)
93 | updateList()
94 | handleAdd()
95 | }
96 |
97 |
98 | const nameInputRef = useRef(null)
99 | const textareaRef = useRef(null)
100 |
101 | const handleInsertText = (text: string) => {
102 | if (textareaRef.current) {
103 | const start = textareaRef.current.selectionStart
104 | const end = textareaRef.current.selectionEnd
105 | const currentText = textareaRef.current.value
106 | const newText = currentText.substring(0, start) + text + currentText.substring(end, currentText.length)
107 | textareaRef.current.setSelectionRange(start + text.length, start + text.length)
108 | textareaRef.current.focus()
109 |
110 | setPrompt({ ...prompt, text: newText })
111 | }
112 | }
113 |
114 | const handleTextareaChange = (e: Event) => {
115 | const text = (e.target as HTMLTextAreaElement).value
116 | if (text !== prompt.text) {
117 | setTextError(false)
118 | setPrompt({ ...prompt, text })
119 | }
120 | }
121 |
122 | const updatePlaceholderButtons = (text: string) => {
123 | setHasWebResultsPlaceholder(text.includes("{web_results}"))
124 | setHasQueryPlaceholder(text.includes("{query}"))
125 | }
126 |
127 | const actionToolbar = (
128 |
131 |
132 |
133 | {
138 | setWebResultsError(false)
139 | handleInsertText('{web_results}')
140 | }}
141 | >
142 | {web_results}
143 |
144 |
145 |
146 | {
151 | setQueryError(false)
152 | handleInsertText('{query}')
153 | }}
154 | >
155 | {query}
156 |
157 |
158 |
159 | handleInsertText('{current_date}')}
162 | >
163 | {current_date}
164 |
165 |
166 |
167 |
168 |
172 | {getTranslation(localizationKeys.buttons.save)}
173 |
174 |
175 | )
176 |
177 | const PromptList = (
178 |
179 |
182 |
183 | add_circle
184 |
185 | {getTranslation(localizationKeys.buttons.newPrompt)}
186 |
187 |
190 | {savedPrompts.map((prmpt: Prompt) => (
191 | handleSelect(prmpt)}
194 | >
195 |
196 | 📝 {prmpt.name}
197 |
198 |
199 | ))}
200 |
201 |
202 | )
203 |
204 | const nameInput = (
205 | {
213 | setNameError(false)
214 | setPrompt({ ...prompt, name: (e.target as HTMLInputElement).value })
215 | }}
216 | disabled={prompt.uuid === 'default' || prompt.uuid === 'default_en'}
217 | />
218 | )
219 |
220 | const btnDelete = (
221 |
227 |
228 | {deleteBtnText}
229 |
230 |
231 | )
232 |
233 | const textArea = (
234 |
243 | )
244 |
245 | return (
246 |
247 |
248 | {PromptList}
249 |
250 |
251 |
252 |
253 | {nameInput}
254 | {btnDelete}
255 |
256 | {textArea}
257 |
258 | {actionToolbar}
259 |
260 |
261 | )
262 | }
263 |
264 | export default PromptEditor
265 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/socialIconButton.tsx:
--------------------------------------------------------------------------------
1 | import { h, JSX } from "preact"
2 | import TooltipWrapper from "./tooltipWrapper"
3 |
4 | function IconButton(props: { url: string, tip: string, icon: JSX.Element }) {
5 | return (
6 |
7 |
8 |
9 | {props.icon}
10 |
11 |
12 |
13 | )
14 | }
15 |
16 | export default IconButton
17 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/toolbar.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 | import { useCallback, useEffect, useState } from 'preact/hooks'
3 | import { icons } from 'src/util/icons'
4 | import { getSavedPrompts, Prompt } from 'src/util/promptManager'
5 | import { getsavedAPIs,API} from 'src/util/apiManager'
6 | import { getUserConfig, updateUserConfig } from 'src/util/userConfig'
7 | import timePeriodOptions from 'src/util/timePeriodOptions.json'
8 | import regionOptions from 'src/util/regionOptions.json'
9 | import Browser from 'webextension-polyfill'
10 | import Dropdown from './dropdown'
11 | import { getTranslation, localizationKeys, setLocaleLanguage } from 'src/util/localization'
12 | import Footer from './footer'
13 |
14 |
15 | const numResultsOptions = Array.from({ length: 10 }, (_, i) => i + 1).map((num) => ({
16 | value: num,
17 | label: `${num} result${num === 1 ? '' : 's'}`
18 | }))
19 |
20 | function Toolbar() {
21 | const [webAccess, setWebAccess] = useState(true)
22 | const [numResults, setNumResults] = useState(3)
23 | const [timePeriod, setTimePeriod] = useState('')
24 | const [region, setRegion] = useState('wt-wt')
25 | const [promptUUID, setPromptUUID] = useState('')
26 | const [prompts, setPrompts] = useState([])
27 | const [apiUUID, setAPIUUID] = useState('')
28 | const [apis, setAPIS] = useState([])
29 |
30 | useEffect(() => {
31 | getUserConfig().then((userConfig) => {
32 | setWebAccess(userConfig.webAccess)
33 | setNumResults(userConfig.numWebResults)
34 | setTimePeriod(userConfig.timePeriod)
35 | setRegion(userConfig.region)
36 | setPromptUUID(userConfig.promptUUID)
37 | setAPIUUID(userConfig.promptUUID)
38 | setLocaleLanguage(userConfig.language)
39 | })
40 | updatePrompts()
41 | updateAPIs()
42 | }, [])
43 |
44 | const handlePromptClick = () => {
45 | updatePrompts()
46 | }
47 | const handleAPIClick = () => {
48 | updateAPIs()
49 | }
50 |
51 | const updatePrompts = () => {
52 | getSavedPrompts().then((savedPrompts) => {
53 | setPrompts(savedPrompts)
54 | })
55 | }
56 | const updateAPIs = () => {
57 | getsavedAPIs().then((savedAPIs) => {
58 | setAPIS(savedAPIs)
59 | })
60 | }
61 |
62 | const handleWebAccessToggle = useCallback(() => {
63 | setWebAccess(!webAccess)
64 | updateUserConfig({ webAccess: !webAccess })
65 | }, [webAccess])
66 |
67 | const handleNumResultsChange = (e: { target: { value: string } }) => {
68 | const value = parseInt(e.target.value, 10)
69 | setNumResults(value)
70 | updateUserConfig({ numWebResults: value })
71 | }
72 |
73 | const handleTimePeriodChange = (e: { target: { value: string } }) => {
74 | setTimePeriod(e.target.value)
75 | updateUserConfig({ timePeriod: e.target.value })
76 | }
77 |
78 | const handleRegionChange = (e: { target: { value: string } }) => {
79 | setRegion(e.target.value)
80 | updateUserConfig({ region: e.target.value })
81 | }
82 |
83 | const handlePromptChange = (uuid: string) => {
84 | removeFocusFromCurrentElement()
85 |
86 | setPromptUUID(uuid)
87 | updateUserConfig({ promptUUID: uuid })
88 | }
89 | const handleAPIChange = (uuid: string) => {
90 | removeFocusFromCurrentElement()
91 | setAPIUUID(uuid)
92 | updateUserConfig({ APIUUID: uuid })
93 | }
94 |
95 | const removeFocusFromCurrentElement = () => (document.activeElement as HTMLElement)?.blur()
96 |
97 |
98 | const webAccessToggle =
99 |
100 |
101 |
102 |
103 |
104 | return (
105 |
106 |
107 |
Browser.runtime.sendMessage("show_options")}
109 | >
110 | {icons.tune}
111 |
112 | {webAccessToggle}
113 |
117 |
121 |
125 |
128 |
129 |
130 | {prompts?.find((prompt) => prompt.uuid === promptUUID)?.name || 'Default prompt'}
131 |
132 | {icons.expand}
133 |
134 |
154 |
155 |
158 |
159 |
160 | {apis?.find((api) => api.uuid === apiUUID)?.name || 'Default API'}
161 |
162 | {icons.expand}
163 |
164 |
168 | {apis.map((api) =>
169 | handleAPIChange(api.uuid)}
171 | key={api.uuid}
172 | >
173 | {api.name}
174 |
175 | )
176 | }
177 | Browser.runtime.sendMessage("show_API_options")
179 | }
180 | >
181 | + New API
182 |
183 |
184 |
185 |
186 |
187 |
188 | )
189 | }
190 |
191 | export default Toolbar
192 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/components/tooltipWrapper.tsx:
--------------------------------------------------------------------------------
1 | import { h, JSX } from "preact"
2 |
3 | export const tooltipPositions = {
4 | top: "wcg-tooltip-top",
5 | bottom: "wcg-tooltip-bottom"
6 | }
7 |
8 |
9 | function TooltipWrapper(props: { tip: string, children: JSX.Element, position?: string }) {
10 | if (!props.tip) return props.children
11 |
12 | return (
13 |
18 | {props.children}
19 |
20 | )
21 |
22 | }
23 |
24 | export default TooltipWrapper
25 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/content-scripts/api.ts:
--------------------------------------------------------------------------------
1 | import { result } from "lodash-es"
2 | import { API, getDefaultAPI, getsavedAPIs } from "src/util/apiManager"
3 |
4 | export interface SearchResult {
5 | body: string
6 | href: string
7 | title: string
8 | }
9 | type FunctionArgs = { [key: string]: any };
10 | type FunctionType = (...args: any[]) => any;
11 |
12 | function stringToFunction(str: string): FunctionType | undefined {
13 | try {
14 | const fn = new Function(`return ${str}`)();
15 | if (typeof fn === 'function') {
16 | return fn as FunctionType;
17 | }
18 | } catch (e) {
19 | console.error('Error while converting string to function:', e);
20 | }
21 | return undefined;
22 | }
23 |
24 | function safeStringToFunction(
25 | str: string,
26 | argTypes: T
27 | ): ((args: T) => ReturnType) | undefined {
28 | const fn = stringToFunction(str);
29 | if (typeof fn === 'function') {
30 | return ((args: T) => fn(...Object.values(args))) as (
31 | args: T
32 | ) => ReturnType;
33 | }
34 | return undefined;
35 | }
36 |
37 | export async function apiSearch(query: string, numResults: number, timePeriod: string, region: string, apiUUID: string): Promise {
38 | const savedAPIS = await getsavedAPIs()
39 | const api_result = savedAPIS.find((i: API) => i.uuid === apiUUID) || getDefaultAPI()
40 | const api = api_result.text
41 | const FunctionString = api;
42 | const api_function = safeStringToFunction(FunctionString, { query: '', numResults: 0, timePeriod: '',region: '' });
43 | if (api_function) {
44 | const q = query,n = numResults, t = timePeriod, r = region;
45 | const result = api_function({query: q, numResults: n, timePeriod: t, region: r});
46 | console.log(result); // Output: 3
47 | return result;
48 | }
49 | return JSON.parse('{}');
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/content-scripts/mainUI.tsx:
--------------------------------------------------------------------------------
1 | import '../style/base.css'
2 | import { h, render } from 'preact'
3 | import { getTextArea, getFooter, getRootElement, getSubmitButton, getWebChatGPTToolbar } from '../util/elementFinder'
4 | import Toolbar from 'src/components/toolbar'
5 | import ErrorMessage from 'src/components/errorMessage'
6 | import { getUserConfig } from 'src/util/userConfig'
7 | import { apiSearch, SearchResult } from './api'
8 | import createShadowRoot from 'src/util/createShadowRoot'
9 | import { compilePrompt } from 'src/util/promptManager'
10 |
11 | let isProcessing = false
12 |
13 | let btnSubmit: HTMLButtonElement
14 | let textarea: HTMLTextAreaElement
15 | let footer: HTMLDivElement
16 |
17 | async function onSubmit(event: MouseEvent | KeyboardEvent) {
18 |
19 | if (event instanceof KeyboardEvent && event.shiftKey && event.key === 'Enter')
20 | return
21 |
22 | if ((event.type === "click" || (event instanceof KeyboardEvent && event.key === 'Enter')) && !isProcessing) {
23 |
24 | const query = textarea.value.trim()
25 |
26 | if (query === "") return
27 |
28 | textarea.value = ""
29 |
30 | const userConfig = await getUserConfig()
31 |
32 | isProcessing = true
33 |
34 | if (!userConfig.webAccess) {
35 | textarea.value = query
36 | pressEnter()
37 | isProcessing = false
38 | return
39 | }
40 |
41 | textarea.value = ""
42 |
43 | try {
44 | const results = await apiSearch(query, userConfig.numWebResults, userConfig.timePeriod, userConfig.region, userConfig.APIUUID)
45 | await pasteWebResultsToTextArea(results, query)
46 | pressEnter()
47 | isProcessing = false
48 |
49 | } catch (error) {
50 | isProcessing = false
51 | showErrorMessage(error)
52 | }
53 | }
54 | }
55 |
56 | async function pasteWebResultsToTextArea(results: SearchResult[], query: string) {
57 |
58 | textarea.value = await compilePrompt(results, query)
59 | }
60 |
61 | function pressEnter() {
62 | textarea.focus()
63 | const enterEvent = new KeyboardEvent('keydown', {
64 | bubbles: true,
65 | cancelable: true,
66 | key: 'Enter',
67 | code: 'Enter'
68 | })
69 | textarea.dispatchEvent(enterEvent)
70 | }
71 |
72 | function showErrorMessage(error: Error) {
73 | console.log("WebChatGPT error --> API error: ", error)
74 | const div = document.createElement('div')
75 | document.body.appendChild(div)
76 | render( , div)
77 | }
78 |
79 |
80 | async function updateUI() {
81 |
82 | if (getWebChatGPTToolbar()) return
83 |
84 | btnSubmit = getSubmitButton()
85 | textarea = getTextArea()
86 | footer = getFooter()
87 |
88 | if (textarea && btnSubmit) {
89 |
90 | textarea.addEventListener("keydown", onSubmit)
91 | btnSubmit.addEventListener("click", onSubmit)
92 |
93 | const textareaParent = textarea.parentElement.parentElement
94 | textareaParent.style.flexDirection = 'column'
95 | textareaParent.parentElement.style.flexDirection = 'column'
96 | textareaParent.parentElement.style.gap = '0px'
97 | textareaParent.parentElement.style.marginBottom = '0.5em'
98 |
99 | const { shadowRootDiv, shadowRoot } = await createShadowRoot('content-scripts/mainUI.css')
100 | textareaParent.appendChild(shadowRootDiv)
101 | render( , shadowRoot)
102 | }
103 |
104 | if (footer) {
105 | const lastChild = footer.lastElementChild as HTMLElement
106 | if (lastChild)
107 | lastChild.style.padding = '0 0 0.5em 0'
108 | }
109 | }
110 |
111 | const rootEl = getRootElement()
112 | window.onload = function () {
113 | updateUI()
114 |
115 | try {
116 | new MutationObserver(() => {
117 | updateUI()
118 | }).observe(rootEl, { childList: true })
119 | } catch (e) {
120 | console.info("WebChatGPT error --> Could not update UI:\n", e.stack)
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/declaration.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css' {
2 | const mapping: Record
3 | export default mapping
4 | }
5 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "__MSG_appName__",
4 | "description": "__MSG_appDesc__",
5 | "default_locale": "en",
6 | "version": "2.0.5",
7 | "icons": {
8 | "16": "icons/icon16.png",
9 | "48": "icons/icon48.png",
10 | "128": "icons/icon128.png"
11 | },
12 | "permissions": [
13 | "storage",
14 | "webRequest",
15 | "https://ddg-webapp-aagd.vercel.app/*",
16 | "https://api.wolframalpha.com/*"
17 | ],
18 | "background": {
19 | "scripts": ["background/bg.js"]
20 | },
21 | "browser_action": {},
22 | "browser_specific_settings": {
23 | "gecko": {
24 | "id": "{b13d04e3-41db-48b3-842c-8079df93c7ad}"
25 | }
26 | },
27 | "content_scripts": [
28 | {
29 | "matches": ["https://chat.openai.com/*"],
30 | "js": ["content-scripts/mainUI.js"]
31 | }
32 | ],
33 | "options_ui": {
34 | "page": "options/options.html",
35 | "open_in_tab": true,
36 | "chrome_style": true
37 | },
38 | "web_accessible_resources": ["content-scripts/mainUI.css", "icons/icon48.png"],
39 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
40 | }
41 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/options/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WebChatGPT Options
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/options/options.tsx:
--------------------------------------------------------------------------------
1 | import "../style/base.css"
2 | import { h, render } from "preact"
3 | import { getUserConfig, updateUserConfig } from "src/util/userConfig"
4 | import { useLayoutEffect, useState } from "preact/hooks"
5 | import PromptEditor from "src/components/promptEditor"
6 | import { getTranslation, localizationKeys, setLocaleLanguage } from "src/util/localization"
7 | import NavBar from "src/components/navBar"
8 | import APIEditor from "src/components/apiEditor"
9 | // const SocialCard = ({ icon, text }: { icon: JSX.Element, text: string }) => (
10 | //
11 | // {icon}`
12 | //
{text}
13 | //
14 | // )
15 |
16 |
17 | export default function OptionsPage() {
18 |
19 | const [language, setLanguage] = useState(null)
20 |
21 |
22 | useLayoutEffect(() => {
23 | getUserConfig().then(config => {
24 | setLanguage(config.language)
25 | setLocaleLanguage(config.language)
26 | })
27 | }, [])
28 |
29 | const onLanguageChange = (language: string) => {
30 | setLanguage(language)
31 | updateUserConfig({ language })
32 | setLocaleLanguage(language)
33 | }
34 |
35 | if (!language) {
36 | return
37 | }
38 |
39 | return (
40 |
51 | )
52 | }
53 |
54 | render( , document.getElementById("options"))
55 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/style/base.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/apiManager.ts:
--------------------------------------------------------------------------------
1 | import {SearchResult} from "src/content-scripts/api"
2 | import Browser from "webextension-polyfill"
3 | import { v4 as uuidv4 } from 'uuid'
4 | import { getUserConfig } from "./userConfig"
5 | export const SAVED_API_KEY = 'saved_apis'
6 |
7 |
8 | export interface API{
9 | uuid?: string,
10 | name: string,
11 | text: string
12 | }
13 |
14 | //fill you google API KEY and CLient ID
15 | export const getDefaultAPI = () => {
16 | return {
17 | name: 'Default API',
18 | text: 'async function GoogleSearch(query, numResults, timePeriod, region) { const headers = new Headers({ Origin: "https://chat.openai.com", }); const searchParams = new URLSearchParams(); searchParams.set("q", query); searchParams.set("key", "YOUR_GOOGLE_API_KEY"); searchParams.set("cx", "YOUR_GOOGLE_CLIENT_ID"); searchParams.set("c2coff", "0"); searchParams.set("num", numResults.toString()); const url = `https://customsearch.googleapis.com/customsearch/v1?${searchParams.toString()}`; console.log(url); const response = await fetch(url, { method: "GET", headers, }); const results = await response.json(); const items = results.items; const filteredData = items.map(({ title, link, snippet }) => ({ title, link, snippet })); return filteredData;}',
19 | uuid: 'default'
20 | }
21 | }
22 |
23 | export const getCurrentAPI = async () => {
24 | const userConfig = await getUserConfig()
25 | const currentAPIUuid = userConfig.APIUUID
26 | const savedAPIs = await getsavedAPIs()
27 | return savedAPIs.find((i: API) => i.uuid === currentAPIUuid) || getDefaultAPI()
28 | }
29 |
30 | export const getsavedAPIs = async (addDefaults = true) => {
31 | const data = await Browser.storage.sync.get([SAVED_API_KEY])
32 | const savedAPIs = data[SAVED_API_KEY] || []
33 | if (addDefaults)
34 | return addDefaultAPIs(savedAPIs)
35 | return savedAPIs
36 | }
37 | function addDefaultAPIs(apis: API[]) {
38 | addAPI(apis, getDefaultAPI())
39 | return apis
40 | function addAPI(apis: API[], api: API) {
41 | const index = apis.findIndex((i: API) => i.uuid === api.uuid)
42 | if (index >= 0) {
43 | apis[index] = api
44 | } else {
45 | apis.unshift(api)
46 | }
47 | }
48 | }
49 | export const saveAPI = async (api: API) => {
50 | const savedAPIs = await getsavedAPIs(false)
51 | const index = savedAPIs.findIndex((i: API) => i.uuid === api.uuid)
52 | if (index >= 0) {
53 | savedAPIs[index] = api
54 | } else {
55 | api.uuid = uuidv4()
56 | savedAPIs.push(api)
57 | }
58 | await Browser.storage.sync.set({ [SAVED_API_KEY]: savedAPIs })
59 | }
60 | export const deleteAPI = async (api: API) => {
61 | let savedAPIs = await getsavedAPIs()
62 | savedAPIs = savedAPIs.filter((i: API) => i.uuid !== api.uuid)
63 | await Browser.storage.sync.set({ [SAVED_API_KEY]: savedAPIs })
64 | }
65 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/createShadowRoot.ts:
--------------------------------------------------------------------------------
1 | import Browser from "webextension-polyfill"
2 |
3 | async function createShadowRoot(pathToCSS: string) {
4 | const shadowRootDiv = document.createElement('div')
5 | const shadowRoot = shadowRootDiv.attachShadow({ mode: 'open' })
6 | const style = document.createElement('style')
7 | style.textContent = await fetch(Browser.runtime.getURL(pathToCSS)).then(response => response.text())
8 | shadowRoot.append(style)
9 | return { shadowRootDiv, shadowRoot }
10 | }
11 |
12 | export default createShadowRoot
13 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/elementFinder.ts:
--------------------------------------------------------------------------------
1 | export function getTextArea(): HTMLTextAreaElement {
2 | return document.querySelector('textarea')
3 | }
4 |
5 | export function getFooter(): HTMLDivElement {
6 | return document.querySelector("div[class*='absolute bottom-0']")
7 | }
8 |
9 | export function getRootElement(): HTMLDivElement {
10 | return document.querySelector('div[id="__next"]')
11 | }
12 |
13 | export function getWebChatGPTToolbar(): HTMLElement {
14 | return document.querySelector("div[class*='wcg-toolbar']")
15 | }
16 |
17 | export function getSubmitButton(): HTMLButtonElement {
18 | const textarea = getTextArea()
19 | if (!textarea) {
20 | return null
21 | }
22 | return textarea.parentNode.querySelector("button")
23 | }
24 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/icons.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact'
2 |
3 | export const twitterIcon = (
4 |
5 |
6 |
7 | )
8 |
9 | export const discordIcon = (
10 |
11 |
12 |
13 | )
14 |
15 | export const githubIcon = (
16 |
17 |
18 |
19 | )
20 |
21 | export const tuneIcon = (
22 |
23 |
24 |
25 | )
26 |
27 | export const Language = (
28 |
29 |
30 |
31 | )
32 |
33 | export const Expand = (
34 |
35 |
36 |
37 | )
38 |
39 | export const icons = {
40 | twitter: twitterIcon,
41 | discord: discordIcon,
42 | github: githubIcon,
43 | tune: tuneIcon,
44 | language: Language,
45 | expand: Expand,
46 | }
47 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/localization.ts:
--------------------------------------------------------------------------------
1 | import Browser from "webextension-polyfill"
2 | import * as localizedStrings from './localizedStrings.json'
3 |
4 | export const getSystemLanguage = () => Browser.i18n.getUILanguage().split("-")[0]
5 |
6 | export const Languages = {
7 | auto: "Auto",
8 | en: "English",
9 | de: "Deutsch",
10 | es: "Español",
11 | fr: "Français",
12 | it: "Italiano",
13 | ja: "日本語",
14 | ko: "한국어",
15 | pt: "Português",
16 | zh: "中文"
17 | }
18 |
19 | const DEFAULT_LANGUAGE = 'en'
20 |
21 |
22 | let language = getSystemLanguage()
23 |
24 | export const getLocaleLanguage = () => language
25 |
26 | export const getCurrentLanguageName = () => language === Languages.auto ? Languages.en : Languages[language]
27 |
28 | export const setLocaleLanguage = (newLanguage: string) => {
29 | language = newLanguage === 'auto' ? getSystemLanguage() : newLanguage
30 | console.debug(`Language set to ${language}`)
31 | }
32 |
33 | export const getTranslation = (key: string, lang? : string) => {
34 | if(lang) {
35 | return localizedStrings[key][lang]
36 | }
37 | if (language in localizedStrings[key]) {
38 | return localizedStrings[key][language]
39 | }
40 | return localizedStrings[key][DEFAULT_LANGUAGE]
41 | }
42 |
43 |
44 | export const localizationKeys = {
45 | defaultPrompt: 'default_prompt',
46 | UI: {
47 | language: 'language',
48 | supportThisProject: 'support_this_project',
49 | supportMe: 'support_me',
50 | chooseLanguage: 'choose_language',
51 | },
52 | placeholders: {
53 | namePlaceholder: 'name_placeholder',
54 | },
55 | buttons: {
56 | save: 'save',
57 | newPrompt: 'new_prompt',
58 | },
59 | placeHolderTips: {
60 | currentDate: 'current_date_placeholder_tip',
61 | webResults: 'web_results_placeholder_tip',
62 | query: 'query_placeholder_tip',
63 | },
64 | socialButtonTips: {
65 | twitter: 'twitter_button_tip',
66 | github: 'github_button_tip',
67 | discord: 'discord_button_tip',
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/localizedStrings.json:
--------------------------------------------------------------------------------
1 | {
2 | "default_prompt": {
3 | "en": "Web search results:\n\n{web_results}\nCurrent date: {current_date}\n\nInstructions: Using the provided web search results, write a comprehensive reply to the given query. Make sure to cite results using [[number](URL)] notation after the reference. If the provided search results refer to multiple subjects with the same name, write separate answers for each subject.\nQuery: {query}"
4 | },
5 | "language": {
6 | "en": "Language",
7 | "pt": "Idioma",
8 | "es": "Idioma",
9 | "fr": "Langue",
10 | "de": "Sprache",
11 | "it": "Lingua",
12 | "zh": "语言",
13 | "ja": "言語",
14 | "ko": "언어",
15 | "ru": "язык"
16 | },
17 | "choose_language": {
18 | "en": "Choose language",
19 | "pt": "Escolha o idioma",
20 | "es": "Elegir idioma",
21 | "fr": "Choisir la langue",
22 | "de": "Sprache auswählen",
23 | "it": "Scegli la lingua",
24 | "zh": "选择语言",
25 | "ja": "言語を選択",
26 | "ko": "언어 선택",
27 | "ru": "Выбрать язык"
28 | },
29 | "support_this_project": {
30 | "en": "Support this project",
31 | "pt": "Apoie este projeto",
32 | "es": "Apoya este proyecto",
33 | "fr": "Soutenez ce projet",
34 | "de": "Unterstützen Sie dieses Projekt",
35 | "it": "Sostieni questo progetto",
36 | "zh": "支持此项目",
37 | "ja": "このプロジェクトを支援",
38 | "ko": "이 프로젝트 지원",
39 | "ru": "Поддержать проект"
40 | },
41 | "support_me": {
42 | "en": "This extension is free! 🥳 But the server it runs on is not. 😬\nPlease help me keep it alive! ⤵️",
43 | "pt": "Esta extensão é gratuita! 🥳 Mas o servidor em que ela é executada não é. 😬\nAjude-me a mantê-lo vivo! ⤵️",
44 | "es": "¡Esta extensión es gratuita! 🥳 Pero el servidor en el que se ejecuta no lo es. 😬\n¡Ayúdame a mantenerla viva! ⤵️",
45 | "fr": "Cette extension est gratuite ! 🥳 Mais le serveur sur lequel elle fonctionne ne l'est pas. 😬\nVeuillez m'aider à la maintenir en vie ! ⤵️",
46 | "de": "Diese Erweiterung ist kostenlos! 🥳 Aber der Server, auf dem sie läuft, ist es nicht. 😬\nBitte hilf mir, sie am Leben zu erhalten! ⤵️",
47 | "it": "Questa estensione è gratuita! 🥳 Ma il server su cui viene eseguita non lo è. 😬\nPer favore aiutami a tenerlo vivo! ⤵️",
48 | "zh": "此扩展程序是免费的!🥳 但是它运行的服务器不是。😬\n帮助这个项目继续下去!⤵️",
49 | "ja": "この拡張機能は無料です!🥳 しかし、それが実行されているサーバーは無料ではありません。😬\nこのプロジェクトを維持するのを手伝ってください!⤵️",
50 | "ko": "이 확장 프로그램은 무료입니다! 🥳 그러나 그것이 실행되는 서버는 아닙니다. 😬\n이 프로젝트를 계속 유지하도록 도와주세요! ⤵️",
51 | "ru": "Это расширение бесплатное! 🥳 Но сервер, на котором он работает, - нет. 😬\nПожалуйста, помогите поддержать его работу! ⤵️"
52 | },
53 | "save": {
54 | "en": "Save",
55 | "pt": "Salvar",
56 | "es": "Guardar",
57 | "fr": "Enregistrer",
58 | "de": "Speichern",
59 | "it": "Salva",
60 | "zh": "保存",
61 | "ja": "保存",
62 | "ko": "저장",
63 | "ru": "Сохранить"
64 | },
65 | "new_prompt": {
66 | "en": "New prompt",
67 | "pt": "Novo prompt",
68 | "es": "Nuevo prompt",
69 | "fr": "Nouveau prompt",
70 | "de": "Neues Prompt",
71 | "it": "Nuovo prompt",
72 | "zh": "新提示",
73 | "ja": "新しいプロンプト",
74 | "ko": "새로운 프롬프트",
75 | "ru": "Новый запрос"
76 | },
77 | "name_placeholder": {
78 | "en": "Name",
79 | "pt": "Nome",
80 | "es": "Nombre",
81 | "fr": "Nom",
82 | "de": "Name",
83 | "it": "Nome",
84 | "zh": "名称",
85 | "ja": "名前",
86 | "ko": "이름",
87 | "ru": "Название"
88 | },
89 | "current_date_placeholder_tip": {
90 | "en": "Insert placeholder for the current date (optional)",
91 | "pt": "Insira o espaço reservado para a data atual (opcional)",
92 | "es": "Ingrese un marcador de posición para la fecha actual (opcional)",
93 | "fr": "Insérer un marqueur de place pour la date actuelle (facultatif)",
94 | "de": "Platzhalter für das aktuelle Datum einfügen (optional)",
95 | "it": "Inserisci il segnaposto per la data attuale (opzionale)",
96 | "zh": "插入当前日期的占位符(可选)",
97 | "ja": "現在の日付のプレースホルダーを挿入(任意)",
98 | "ko": "현재 날짜의 자리 표시자를 삽입 (선택 사항)",
99 | "ru": "Вставить плейсхолдер текущей даты (необязательно)"
100 | },
101 | "web_results_placeholder_tip": {
102 | "en": "Insert placeholder for the web results (required)",
103 | "pt": "Insira o espaço reservado para os resultados da pesquisa na web (obrigatório)",
104 | "es": "Ingrese un marcador de posición para los resultados de búsqueda web (requerido)",
105 | "fr": "Insérer un marqueur de place pour les résultats de recherche web (requis)",
106 | "de": "Platzhalter für die Web-Ergebnisse einfügen (erforderlich)",
107 | "it": "Inserisci il segnaposto per i risultati web (richiesto)",
108 | "zh": "插入网络搜索结果的占位符(必需)",
109 | "ja": "Web結果のプレースホルダーを挿入(必須)",
110 | "ko": "웹 검색 결과의 자리 표시자를 삽입 (필수)",
111 | "ru": "Вставить плейсхолдер веб-результатов (обязательно)"
112 | },
113 | "query_placeholder_tip": {
114 | "en": "Insert placeholder for the initial query (required)",
115 | "pt": "Insira o espaço reservado para a pergunta inicial (obrigatório)",
116 | "es": "Ingrese un marcador de posición para la consulta inicial (requerido)",
117 | "fr": "Insérer un marqueur de place pour la requête initiale (requis)",
118 | "de": "Platzhalter für die ursprüngliche Anfrage einfügen (erforderlich)",
119 | "it": "Inserisci il segnaposto per la query iniziale (richiesto)",
120 | "zh": "插入初始查询的占位符(必需)",
121 | "ja": "初期クエリのプレースホルダーを挿入(必須)",
122 | "ko": "초기 쿼리의 자리 표시자를 삽입 (필수)",
123 | "ru": "Вставить плейсхолдер начального запроса (обязательно)"
124 | },
125 | "twitter_button_tip": {
126 | "en": "Follow me on Twitter",
127 | "pt": "Siga-me no Twitter",
128 | "es": "Sígueme en Twitter",
129 | "fr": "Me suivre sur Twitter",
130 | "de": "Folgen Sie mir auf Twitter",
131 | "it": "Seguimi su Twitter",
132 | "zh": "在 Twitter 上关注我",
133 | "ja": "Twitterで私をフォロー",
134 | "ko": "Twitter에서 나를 팔로우하세요",
135 | "ru": "Подписаться на меня в Twitter"
136 | },
137 | "github_button_tip": {
138 | "en": "View the source code on GitHub",
139 | "pt": "Veja o código fonte no GitHub",
140 | "es": "Ver el código fuente en GitHub",
141 | "fr": "Voir le code source sur GitHub",
142 | "de": "Quellcode auf GitHub anzeigen",
143 | "it": "Visualizza il codice sorgente su GitHub",
144 | "zh": "在 GitHub 上查看源代码",
145 | "ja": "GitHub上のソースコードを見る",
146 | "ko": "GitHub에서 소스 코드 보기",
147 | "ru": "Посмотреть исходный код на GitHub"
148 | },
149 | "discord_button_tip": {
150 | "en": "Join our Discord community",
151 | "pt": "Junte-se à nossa comunidade no Discord",
152 | "es": "Únete a nuestra comunidad de Discord",
153 | "fr": "Rejoignez notre communauté Discord",
154 | "de": "Treten Sie unserer Discord-Community bei",
155 | "it": "Unisciti alla nostra comunità su Discord",
156 | "zh": "加入我们的 Discord 社区",
157 | "ja": "私たちのDiscordコミュニティに参加してください",
158 | "ko": "우리의 Discord 커뮤니티에 가입하세요",
159 | "ru": "Присоединиться к нашему сообществу на Discord"
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/promptManager.ts:
--------------------------------------------------------------------------------
1 | import { SearchResult } from "src/content-scripts/api"
2 | import Browser from "webextension-polyfill"
3 | import { v4 as uuidv4 } from 'uuid'
4 | import { getCurrentLanguageName, getLocaleLanguage, getTranslation, localizationKeys } from "./localization"
5 | import { getUserConfig } from "./userConfig"
6 |
7 | export const SAVED_PROMPTS_KEY = 'saved_prompts'
8 |
9 | export interface Prompt {
10 | uuid?: string,
11 | name: string,
12 | text: string
13 | }
14 | export interface APIs{
15 | uuid?: string,
16 | name: string,
17 | text: string
18 | }
19 | function hasKey(obj: any, key: string): boolean {
20 | return key in obj;
21 | }
22 |
23 | function hasValue(obj: any, value: any): boolean {
24 | return Object.values(obj).includes(value);
25 | }
26 | export const compilePrompt = async (results: SearchResult[], query: string) => {
27 | const currentPrompt = await getCurrentPrompt()
28 | //const formattedResults = formatWebResults(results)
29 | let formattedResults = JSON.stringify(results)
30 | const regexList = [/\bnigger\w*/i, /\bfaggot\w*/i, /\bkike\w*/i, /\bdykes?\b/i, /\bwetbacks?\b/i, /\bchinks?\b/i, /\bgooks?\b/i, /\bpakis?\b/i, /\binjuns?\b/i, /\btrannys?\b/i, /\btrannies\b/i, /\bspicks?\b/i, /\bshemales?\b/i, ];
31 | for (const regex of regexList) {
32 | formattedResults = formattedResults.replace(regex, '***'); // substitute each regex with an empty string
33 | }
34 | const currentDate = new Date().toLocaleDateString()
35 | const prompt = replaceVariables(currentPrompt.text, {
36 | '{web_results}': formattedResults,
37 | '{query}': query,
38 | '{current_date}': currentDate
39 | })
40 | return prompt
41 | }
42 |
43 | const formatWebResults = (results: SearchResult[]) => {
44 | let counter = 1
45 | return results.reduce((acc, result): string => acc += `[${counter++}] "${result.body}"\nURL: ${result.href}\n\n`, "")
46 | }
47 |
48 | const replaceVariables = (prompt: string, variables: { [key: string]: string }) => {
49 | let newPrompt = prompt
50 | for (const key in variables) {
51 | try {
52 | newPrompt = newPrompt.replaceAll(key, variables[key])
53 | } catch (error) {
54 | console.log("WebChatGPT error --> API error: ", error)
55 | }
56 | }
57 | return newPrompt
58 | }
59 |
60 | export const getDefaultPrompt = () => {
61 | return {
62 | name: 'Default prompt',
63 | text: getTranslation(localizationKeys.defaultPrompt, 'en') + (getLocaleLanguage() !== 'en' ? `\nReply in ${getCurrentLanguageName()}` : ''),
64 | uuid: 'default'
65 | }
66 | }
67 |
68 | const getDefaultEnglishPrompt = () => {
69 | return { name: 'Default English', text: getTranslation(localizationKeys.defaultPrompt, 'en'), uuid: 'default_en' }
70 | }
71 |
72 | export const getCurrentPrompt = async () => {
73 | const userConfig = await getUserConfig()
74 | const currentPromptUuid = userConfig.promptUUID
75 | const savedPrompts = await getSavedPrompts()
76 | return savedPrompts.find((i: Prompt) => i.uuid === currentPromptUuid) || getDefaultPrompt()
77 | }
78 |
79 | export const getSavedPrompts = async (addDefaults = true) => {
80 | const data = await Browser.storage.sync.get([SAVED_PROMPTS_KEY])
81 | const savedPrompts = data[SAVED_PROMPTS_KEY] || []
82 | if (addDefaults)
83 | return addDefaultPrompts(savedPrompts)
84 |
85 | return savedPrompts
86 | }
87 | function addDefaultPrompts(prompts: Prompt[]) {
88 | if (getLocaleLanguage() !== 'en') {
89 | addPrompt(prompts, getDefaultEnglishPrompt())
90 | }
91 | addPrompt(prompts, getDefaultPrompt())
92 | return prompts
93 |
94 | function addPrompt(prompts: Prompt[], prompt: Prompt) {
95 | const index = prompts.findIndex((i: Prompt) => i.uuid === prompt.uuid)
96 | if (index >= 0) {
97 | prompts[index] = prompt
98 | } else {
99 | prompts.unshift(prompt)
100 | }
101 | }
102 | }
103 |
104 | export const savePrompt = async (prompt: Prompt) => {
105 | const savedPrompts = await getSavedPrompts(false)
106 | const index = savedPrompts.findIndex((i: Prompt) => i.uuid === prompt.uuid)
107 | if (index >= 0) {
108 | savedPrompts[index] = prompt
109 | } else {
110 | prompt.uuid = uuidv4()
111 | savedPrompts.push(prompt)
112 | }
113 | await Browser.storage.sync.set({ [SAVED_PROMPTS_KEY]: savedPrompts })
114 | }
115 |
116 | export const deletePrompt = async (prompt: Prompt) => {
117 | let savedPrompts = await getSavedPrompts()
118 | savedPrompts = savedPrompts.filter((i: Prompt) => i.uuid !== prompt.uuid)
119 | await Browser.storage.sync.set({ [SAVED_PROMPTS_KEY]: savedPrompts })
120 | }
121 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/regionOptions.json:
--------------------------------------------------------------------------------
1 | [
2 | { "value": "wt-wt", "label": "Any region" },
3 | { "value": "xa-ar", "label": "Saudi Arabia" },
4 | { "value": "xa-en", "label": "Saudi Arabia (en)" },
5 | { "value": "ar-es", "label": "Argentina" },
6 | { "value": "au-en", "label": "Australia" },
7 | { "value": "at-de", "label": "Austria" },
8 | { "value": "be-fr", "label": "Belgium (fr)" },
9 | { "value": "be-nl", "label": "Belgium (nl)" },
10 | { "value": "br-pt", "label": "Brazil" },
11 | { "value": "bg-bg", "label": "Bulgaria" },
12 | { "value": "ca-en", "label": "Canada" },
13 | { "value": "ca-fr", "label": "Canada (fr)" },
14 | { "value": "ct-ca", "label": "Catalan" },
15 | { "value": "cl-es", "label": "Chile" },
16 | { "value": "cn-zh", "label": "China" },
17 | { "value": "co-es", "label": "Colombia" },
18 | { "value": "hr-hr", "label": "Croatia" },
19 | { "value": "cz-cs", "label": "Czech Republic" },
20 | { "value": "dk-da", "label": "Denmark" },
21 | { "value": "ee-et", "label": "Estonia" },
22 | { "value": "fi-fi", "label": "Finland" },
23 | { "value": "fr-fr", "label": "France" },
24 | { "value": "de-de", "label": "Germany" },
25 | { "value": "gr-el", "label": "Greece" },
26 | { "value": "hk-tzh", "label": "Hong Kong" },
27 | { "value": "hu-hu", "label": "Hungary" },
28 | { "value": "in-en", "label": "India" },
29 | { "value": "id-id", "label": "Indonesia" },
30 | { "value": "id-en", "label": "Indonesia (en)" },
31 | { "value": "ie-en", "label": "Ireland" },
32 | { "value": "il-he", "label": "Israel" },
33 | { "value": "it-it", "label": "Italy" },
34 | { "value": "jp-jp", "label": "Japan" },
35 | { "value": "kr-kr", "label": "Korea" },
36 | { "value": "lv-lv", "label": "Latvia" },
37 | { "value": "lt-lt", "label": "Lithuania" },
38 | { "value": "xl-es", "label": "Latin America" },
39 | { "value": "my-ms", "label": "Malaysia" },
40 | { "value": "my-en", "label": "Malaysia (en)" },
41 | { "value": "mx-es", "label": "Mexico" },
42 | { "value": "nl-nl", "label": "Netherlands" },
43 | { "value": "nz-en", "label": "New Zealand" },
44 | { "value": "no-no", "label": "Norway" },
45 | { "value": "pe-es", "label": "Peru" },
46 | { "value": "ph-en", "label": "Philippines" },
47 | { "value": "ph-tl", "label": "Philippines (tl)" },
48 | { "value": "pl-pl", "label": "Poland" },
49 | { "value": "pt-pt", "label": "Portugal" },
50 | { "value": "ro-ro", "label": "Romania" },
51 | { "value": "ru-ru", "label": "Russia" },
52 | { "value": "sg-en", "label": "Singapore" },
53 | { "value": "sk-sk", "label": "Slovak Republic" },
54 | { "value": "sl-sl", "label": "Slovenia" },
55 | { "value": "za-en", "label": "South Africa" },
56 | { "value": "es-es", "label": "Spain" },
57 | { "value": "se-sv", "label": "Sweden" },
58 | { "value": "ch-de", "label": "Switzerland (de)" },
59 | { "value": "ch-fr", "label": "Switzerland (fr)" },
60 | { "value": "ch-it", "label": "Switzerland (it)" },
61 | { "value": "tw-tzh", "label": "Taiwan" },
62 | { "value": "th-th", "label": "Thailand" },
63 | { "value": "tr-tr", "label": "Turkey" },
64 | { "value": "ua-uk", "label": "Ukraine" },
65 | { "value": "uk-en", "label": "United Kingdom" },
66 | { "value": "us-en", "label": "United States" },
67 | { "value": "ue-es", "label": "United States (es)" },
68 | { "value": "ve-es", "label": "Venezuela" },
69 | { "value": "vn-vi", "label": "Vietnam" }
70 | ]
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/timePeriodOptions.json:
--------------------------------------------------------------------------------
1 | [
2 | { "value": "", "label": "Any time" },
3 | { "value": "d", "label": "Past day" },
4 | { "value": "w", "label": "Past week" },
5 | { "value": "m", "label": "Past month" },
6 | { "value": "y", "label": "Past year" }
7 | ]
8 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/src/util/userConfig.ts:
--------------------------------------------------------------------------------
1 | import { defaults } from 'lodash-es'
2 | import Browser from 'webextension-polyfill'
3 | import { getSystemLanguage } from './localization'
4 |
5 |
6 | const defaultConfig = {
7 | numWebResults: 3,
8 | webAccess: true,
9 | region: 'wt-wt',
10 | timePeriod: '',
11 | language: getSystemLanguage(),
12 | promptUUID: 'default',
13 | APIUUID: 'default',
14 | }
15 |
16 | export type UserConfig = typeof defaultConfig
17 |
18 | export async function getUserConfig(): Promise {
19 | const config = await Browser.storage.sync.get(defaultConfig)
20 | return defaults(config, defaultConfig)
21 | }
22 |
23 | export async function updateUserConfig(config: Partial): Promise {
24 | await Browser.storage.sync.set(config)
25 | }
26 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | corePlugins: {
4 | preflight: false,
5 | },
6 | content: ["./src/**/*.{html,tsx}"],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: [require("daisyui")],
11 | daisyui: {
12 | themes: ["valentine"],
13 | },
14 | prefix: "wcg-",
15 | }
16 |
--------------------------------------------------------------------------------
/chatGPTChromeEnhance/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "esModuleInterop": true,
7 | "jsx": "react",
8 | "jsxFactory": "h",
9 | "jsxFragmentFactory": "Fragment",
10 | "noEmit": true,
11 | "allowJs": true,
12 | "checkJs": true,
13 | "skipLibCheck": true,
14 | "baseUrl": "./",
15 | "paths": {
16 | "react": ["./node_modules/preact/compat"],
17 | "react-dom": ["./node_modules/preact/compat"]
18 | },
19 | "resolveJsonModule": true
20 | },
21 | "include": ["src/**/*"]
22 | }
23 |
--------------------------------------------------------------------------------
/chatGPTEx/.dockerignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Example user template template
3 | ### Example user template
4 |
5 | # IntelliJ project files
6 | .idea
7 | *.iml
8 | out
9 | gen
10 | ### Python template
11 | # Byte-compiled / optimized / DLL files
12 | __pycache__/
13 | *.py[cod]
14 | *$py.class
15 |
16 | # C extensions
17 | *.so
18 |
19 | # Distribution / packaging
20 | .Python
21 | build/
22 | develop-eggs/
23 | dist/
24 | downloads/
25 | eggs/
26 | .eggs/
27 | lib/
28 | lib64/
29 | parts/
30 | sdist/
31 | var/
32 | wheels/
33 | pip-wheel-metadata/
34 | share/python-wheels/
35 | *.egg-info/
36 | .installed.cfg
37 | *.egg
38 | MANIFEST
39 |
40 | # PyInstaller
41 | # Usually these files are written by a python script from a template
42 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
43 | *.manifest
44 | *.spec
45 |
46 | # Installer logs
47 | pip-log.txt
48 | pip-delete-this-directory.txt
49 |
50 | # Unit test / coverage reports
51 | htmlcov/
52 | .tox/
53 | .nox/
54 | .coverage
55 | .coverage.*
56 | .cache
57 | nosetests.xml
58 | coverage.xml
59 | *.cover
60 | *.py,cover
61 | .hypothesis/
62 | .pytest_cache/
63 |
64 | # Translations
65 | *.mo
66 | *.pot
67 |
68 | # Django stuff:
69 | *.log
70 | local_settings.py
71 | db.sqlite3
72 | db.sqlite3-journal
73 |
74 | # Flask stuff:
75 | instance/
76 | .webassets-cache
77 |
78 | # Scrapy stuff:
79 | .scrapy
80 |
81 | # Sphinx documentation
82 | docs/_build/
83 |
84 | # PyBuilder
85 | target/
86 |
87 | # Jupyter Notebook
88 | .ipynb_checkpoints
89 |
90 | # IPython
91 | profile_default/
92 | ipython_config.py
93 |
94 | # pyenv
95 | .python-version
96 |
97 | # pipenv
98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
101 | # install all needed dependencies.
102 | #Pipfile.lock
103 |
104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
105 | __pypackages__/
106 |
107 | # Celery stuff
108 | celerybeat-schedule
109 | celerybeat.pid
110 |
111 | # SageMath parsed files
112 | *.sage.py
113 |
114 | # Environments
115 | .venv
116 | env/
117 | venv/
118 | ENV/
119 | env.bak/
120 | venv.bak/
121 |
122 | # Spyder project settings
123 | .spyderproject
124 | .spyproject
125 |
126 | # Rope project settings
127 | .ropeproject
128 |
129 | # mkdocs documentation
130 | /site
131 |
132 | # mypy
133 | .mypy_cache/
134 | .dmypy.json
135 | dmypy.json
136 |
137 | # Pyre type checker
138 | .pyre/
139 |
140 | .git
141 | .gitignore
142 |
--------------------------------------------------------------------------------
/chatGPTEx/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 | # Distribution / packaging
9 | .Python
10 | build/
11 | develop-eggs/
12 | dist/
13 | downloads/
14 | eggs/
15 | .eggs/
16 | lib/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | wheels/
22 | share/python-wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | *.py,cover
49 | .hypothesis/
50 | .pytest_cache/
51 | cover/
52 |
53 | # Translations
54 | *.mo
55 | *.pot
56 |
57 | # Django stuff:
58 | *.log
59 | local_settings.py
60 | db.sqlite3
61 | db.sqlite3-journal
62 |
63 | # Flask stuff:
64 | instance/
65 | .webassets-cache
66 |
67 | # Scrapy stuff:
68 | .scrapy
69 |
70 | # Sphinx documentation
71 | docs/_build/
72 |
73 | # PyBuilder
74 | .pybuilder/
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | # For a library or package, you might want to ignore these files since the code is
86 | # intended to run in multiple environments; otherwise, check them in:
87 | # .python-version
88 |
89 | # pipenv
90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
93 | # install all needed dependencies.
94 | #Pipfile.lock
95 |
96 | # poetry
97 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
98 | # This is especially recommended for binary packages to ensure reproducibility, and is more
99 | # commonly ignored for libraries.
100 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
101 | #poetry.lock
102 |
103 | # pdm
104 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
105 | #pdm.lock
106 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
107 | # in version control.
108 | # https://pdm.fming.dev/#use-with-ide
109 | .pdm.toml
110 |
111 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
112 | __pypackages__/
113 |
114 | # Celery stuff
115 | celerybeat-schedule
116 | celerybeat.pid
117 |
118 | # SageMath parsed files
119 | *.sage.py
120 |
121 | # Environments
122 | .env
123 | .venv
124 | env/
125 | venv/
126 | ENV/
127 | env.bak/
128 | venv.bak/
129 |
130 | # Spyder project settings
131 | .spyderproject
132 | .spyproject
133 |
134 | # Rope project settings
135 | .ropeproject
136 |
137 | # mkdocs documentation
138 | /site
139 |
140 | # mypy
141 | .mypy_cache/
142 | .dmypy.json
143 | dmypy.json
144 |
145 | # Pyre type checker
146 | .pyre/
147 |
148 | # pytype static type analyzer
149 | .pytype/
150 |
151 | # Cython debug symbols
152 | cython_debug/
153 |
154 | # PyCharm
155 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
156 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
157 | # and can be added to the global gitignore or merged into this file. For a more nuclear
158 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
159 | #.idea/
160 |
161 | /apikey.ini
162 |
163 | # test file
164 | /test.json
165 | /main_test.py
166 | /testPrompts.txt
167 |
168 | #chat history
169 | /chatHistory.json
170 | /chatLists.json
171 | /backup/
172 | /promtsGrab.py
173 |
174 | #tts
175 | /YourAudioFile.wav
176 | /TTS.py
--------------------------------------------------------------------------------
/chatGPTEx/Dockerfile:
--------------------------------------------------------------------------------
1 | # FROM python:slim or python:alpine 以精简镜像,但需要额外下载依赖
2 | FROM python:3.11.2
3 |
4 | # 使用清华源以提高下载速度
5 | RUN pip install --no-cache-dir -r https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/main/chatGPTEx/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
6 |
7 | ENV LANG="C.UTF-8" \
8 | TZ="Asia/Shanghai" \
9 | REPO_URL="https://ghproxy.com/https://github.com/circlestarzero/EX-chatGPT.git" \
10 | WORKDIR="/app" \
11 | AUTO_UPDATE="true"
12 |
13 | WORKDIR ${WORKDIR}
14 |
15 | RUN git clone -b main ${REPO_URL} ${WORKDIR} --depth=1 --recurse-submodule \
16 | && chmod +x ${WORKDIR}/chatGPTEx/entrypoint.sh \
17 | && sed -i 's/app.run(host="127\.0\.0\.1",port=1234)/#app.run(host="127\.0\.0\.1",port=1234)/g; s/# app.run(host="0\.0\.0\.0", port = 5000)/app.run(host="0\.0\.0\.0", port = 5000)/g' ${WORKDIR}/chatGPTEx/main.py \
18 | && sed -i "s#program_dir+'/apikey.ini'#'/config/apikey.ini'#g" /app/chatGPTEx/main.py \
19 | && sed -i "s#program_dir+'/apikey.ini'#'/config/apikey.ini'#g" /app/chatGPTEx/search.py \
20 | && sed -i "s#os\.path\.join(program_dir, 'apikey.ini')#'/config/apikey.ini'#g" /app/chatGPTEx/search.py \
21 | && mkdir /config
22 |
23 | EXPOSE 5000
24 |
25 | VOLUME ["/config"]
26 |
27 | ENTRYPOINT ["/app/chatGPTEx/entrypoint.sh"]
28 |
--------------------------------------------------------------------------------
/chatGPTEx/api_class.py:
--------------------------------------------------------------------------------
1 | import json
2 | import requests
3 | import urllib.parse
4 | import re
5 | import os
6 | import string
7 | import time
8 | import threading
9 | from queue import PriorityQueue as PQ
10 | import jieba
11 | program_path = os.path.realpath(__file__)
12 | program_dir = os.path.dirname(program_path)
13 | with open(program_dir+'/cn_stopwords.txt', encoding='utf-8') as f:
14 | zh_stopwords = [line.strip() for line in f]
15 | def remove_stopwords(text):
16 | zh_words = jieba.cut(text, cut_all=False)
17 | zh_filtered_words = [word.strip() for word in zh_words if word not in zh_stopwords and word != ' ']
18 | filtered_words = zh_filtered_words
19 | return "".join(filtered_words)
20 | def clean_string(input_str):
21 | # Replace multiple spaces with a single space
22 | res = re.sub(r'\s+', ' ', input_str)
23 | # Remove spaces except between words
24 | res = re.sub(r'(?', '', res) # Remove HTML tags
66 | res = re.sub(r'\W{2,}', '', res)
67 | res = re.sub(r'(\d) +(\d)', r'\1,\2', res)
68 | res = res.strip() # Remove leading/trailing spaces
69 | res = remove_stopwords(res)
70 | return res
71 | class MetaAPI():
72 | def __init__(
73 | self,
74 | api_keys: list,
75 | api_name: str,
76 | base_url: str,
77 | apiTimeInterval:float = 0.1,
78 | lastAPICallTime:float = time.time()-100,
79 | proxy=None,
80 | )->None:
81 | self.apiTimeInterval = apiTimeInterval
82 | self.proxy = proxy
83 | self.session = requests.Session()
84 | if self.proxy:
85 | proxies = {
86 | "http": self.proxy,
87 | "https": self.proxy,
88 | }
89 | self.session.proxies = proxies
90 | self.api_name = api_name
91 | self.base_url = base_url
92 | self.api_keys = PQ()
93 | self.trash_api_keys = PQ()
94 | self.lock = threading.Lock()
95 | for key in api_keys:
96 | self.api_keys.put((lastAPICallTime,key))
97 | def get_api_key(self):
98 | with self.lock:
99 | while(self.trash_api_keys.qsize() and self.api_keys.qsize()):
100 | trash_key = self.trash_api_keys.get()
101 | apiKey = self.api_keys.get()
102 | if(trash_key[1] == apiKey[1]):
103 | self.api_keys.put((time.time()+24*3600, apiKey[1]))
104 | continue
105 | else:
106 | self.trash_api_keys.put(trash_key)
107 | self.api_keys.put(apiKey)
108 | break
109 | apiKey = self.api_keys.get()
110 | if apiKey[0] > time.time():
111 | print('API key exhausted')
112 | raise Exception('API key Exhausted')
113 | delay = self._calculate_delay(apiKey)
114 | time.sleep(delay)
115 | self.api_keys.put((time.time(), apiKey[1]))
116 | return apiKey[1]
117 | def _calculate_delay(self, apiKey):
118 | elapsed_time = time.time() - apiKey[0]
119 | if elapsed_time < self.apiTimeInterval:
120 | return self.apiTimeInterval - elapsed_time
121 | else:
122 | return 0
123 |
124 |
125 | class WikiSearchAPI(MetaAPI):
126 | def __init__(
127 | self,
128 | apikeys: list,
129 | proxy=None,
130 | )-> None:
131 | self.proxy = proxy
132 | self.session = requests.Session()
133 | if self.proxy:
134 | proxies = {
135 | "http": self.proxy,
136 | "https": self.proxy,
137 | }
138 | self.session.proxies = proxies
139 | api_name = 'Wiki Search'
140 | base_url = 'https://en.wikipedia.org/w/api.php'
141 | super(WikiSearchAPI, self).__init__(api_name=api_name, base_url=base_url,api_keys=[''])
142 | def call(self,query, num_results=2):
143 | def remove_html_tags(text):
144 | clean = re.compile('<.*?>')
145 | return re.sub(clean, '', text)
146 | base_url = 'https://en.wikipedia.org/w/api.php?'
147 | params = {
148 | "action": "query",
149 | "format": "json",
150 | "list": "search",
151 | "srsearch": query,
152 | }
153 | call_url = base_url + urllib.parse.urlencode(params)
154 | r = self.session.get(call_url)
155 | data = r.json()['query']['search']
156 | data = [d['title'] + ": " + remove_html_tags(d["snippet"]) for d in data][:num_results]
157 | return data
158 |
159 | class GoogleSearchAPI(MetaAPI):
160 | def __init__(
161 | self,
162 | apikeys: list,
163 | proxy=None,
164 | )-> None:
165 | self.proxy = proxy
166 | self.session = requests.Session()
167 | if self.proxy:
168 | proxies = {
169 | "http": self.proxy,
170 | "https": self.proxy,
171 | }
172 | self.session.proxies = proxies
173 | api_name = 'Google Search'
174 | base_url = 'https://customsearch.googleapis.com/customsearch/v1?'
175 | super(GoogleSearchAPI, self).__init__(api_name=api_name, base_url=base_url,api_keys=apikeys)
176 | def call(self, query, num_results=2):
177 | try:
178 | GOOGLE_API_KEY,GOOGLE_SEARCH_ENGINE_ID= self.get_api_key()
179 | except Exception as e:
180 | print("Error occurred while getting API key:", e)
181 | return []
182 | params = {
183 | 'q': query,
184 | 'key': GOOGLE_API_KEY,
185 | 'cx': GOOGLE_SEARCH_ENGINE_ID,
186 | 'c2coff': '0',
187 | 'num': num_results
188 | }
189 | call_url = self.base_url + urllib.parse.urlencode(params)
190 | try:
191 | res = self.session.get(call_url)
192 | if "items" in res.json():
193 | items = res.json()["items"]
194 | filter_data = [
195 | clean_string(item["title"] + ": " + item["snippet"]) for item in items
196 | ]
197 | return ('\n').join(filter_data)
198 | else:
199 | return []
200 | except requests.exceptions.HTTPError as err:
201 | if err.response.status_code == 403: # API key error
202 | self.trash_api_keys.put((time.time(), (GOOGLE_API_KEY,GOOGLE_SEARCH_ENGINE_ID)))
203 | return self.call(query, num_results)
204 | else:
205 | raise err
206 | class WolframAPI(MetaAPI):
207 | def __init__(
208 | self,
209 | apikeys: list,
210 | proxy=None,
211 | )-> None:
212 | self.proxy = proxy
213 | self.session = requests.Session()
214 | if self.proxy:
215 | proxies = {
216 | "http": self.proxy,
217 | "https": self.proxy,
218 | }
219 | self.session.proxies = proxies
220 | api_name = 'wolfram'
221 | base_url = 'https://api.wolframalpha.com/v2/query'
222 | super(WolframAPI, self).__init__(api_name=api_name, base_url=base_url,api_keys=apikeys)
223 | def call(self, query, num_results=3):
224 | query = query.replace('+', ' plus ')
225 | try:
226 | APPID = self.get_api_key()
227 | except Exception as e:
228 | print("Error occurred while getting API key:", e)
229 | return []
230 | params = {
231 | 'input': query,
232 | 'format': 'plaintext',
233 | 'output': 'JSON',
234 | 'appid': APPID, # get from wolfram Alpha document
235 | }
236 | headers = {
237 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
238 | 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
239 | 'Cache-Control': 'no-cache',
240 | 'Connection': 'keep-alive',
241 | 'DNT': '1',
242 | 'Pragma': 'no-cache',
243 | 'Sec-Fetch-Dest': 'document',
244 | 'Sec-Fetch-Mode': 'navigate',
245 | 'Sec-Fetch-Site': 'none',
246 | 'Sec-Fetch-User': '?1',
247 | 'Upgrade-Insecure-Requests': '1',
248 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
249 | 'sec-ch-ua': '"Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"',
250 | 'sec-ch-ua-mobile': '?0',
251 | 'sec-ch-ua-platform': '"macOS"',
252 | }
253 | try:
254 | responseFromWolfram = self.session.get(
255 | self.base_url, params=params, headers=headers)
256 | if 'queryresult' in responseFromWolfram.json() and 'pods' in responseFromWolfram.json()['queryresult']:
257 | pods = responseFromWolfram.json()['queryresult']['pods'][:num_results]
258 | pods_id = [pod["id"]for pod in pods]
259 | subplots = [(pod['subpods']) for pod in pods]
260 | pods_plaintext = []
261 | for subplot in subplots:
262 | text = '\n'.join([c['plaintext'] for c in subplot])
263 | pods_plaintext.append(text)
264 | # pods_plaintext = ['\n'.join(pod['subpods']['plaintext']) for pod in pods]
265 | res = [pods_id[i] + ": " + pods_plaintext[i] for i in range(len(pods_plaintext)) if pods_plaintext[i].strip() != '']
266 | return res
267 | else:
268 | return []
269 | except requests.exceptions.HTTPError as err:
270 | if err.response.status_code == 403: # API key error
271 | self.trash_api_keys.put((time.time(),(APPID)))
272 | return self.call(query, num_results)
273 | else:
274 | raise err
275 |
--------------------------------------------------------------------------------
/chatGPTEx/apikey.ini.example:
--------------------------------------------------------------------------------
1 | [Google]
2 | GOOGLE_API_KEY0 = x
3 | SEARCH_ENGINE_ID0 = x
4 | [OpenAI]
5 | key0 = sk-xxxx
6 | key1 = sk-xxxx
7 | key2 = sk-xxxx
8 | [WolframAlpha]
9 | WOLFRAMALPHA_APP_ID0 = xxxx
10 | [Proxy]
11 | api_proxy =
12 | [Azure]
13 | subscriptionKey = xxx
14 | region = xxx
--------------------------------------------------------------------------------
/chatGPTEx/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 |
4 | exchatgpt:
5 | build:
6 | context: .
7 | dockerfile: Dockerfile
8 | image: exchatgpt
9 | volumes:
10 | - ~/config:/config # 冒号左边请修改为你想保存配置的路径
11 | container_name: exchatgpt
12 | restart: unless-stopped
13 | logging:
14 | driver: "json-file"
15 | options:
16 | max-size: "1024m"
17 | max-file: "3"
18 | ports:
19 | - "5000:5000"
20 | # TODO: 添加环境变量以直接启动
21 | environment:
22 | - "GOOGLE_API_KEY0=XXX"
23 | - "SEARCH_ENGINE_ID0=XXX"
24 | - "key0=XXX"
25 | - "WOLFRAMALPHA_APP_ID0=XXX"
26 | - "API_PROXY=https://api.openai.com/v1/chat/completions"
27 | - "AZURE_SUBSCRIPTIONKEY="
28 | - "AZURE_REGION="
29 | - "AUTO_UPDATE=true"
30 |
--------------------------------------------------------------------------------
/chatGPTEx/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | cd ${WORKDIR}
3 |
4 | # 自动更新
5 | if [ "${AUTO_UPDATE}" = "true" ]; then
6 | if [ ! -s /tmp/requirements.txt.sha256sum ]; then
7 | sha256sum /app/chatGPTEx/requirements.txt > /tmp/requirements.txt.sha256sum
8 | fi
9 | echo "更新源码"
10 | branch=main
11 | git clean -dffx
12 | git fetch --depth 1 origin ${branch}
13 | git reset --hard origin/${branch}
14 | if [ $? -eq 0 ]; then
15 | echo "源码更新成功"
16 | chmod +x ${WORKDIR}/chatGPTEx/entrypoint.sh
17 | sed -i 's/app.run(host="127\.0\.0\.1",port=1234)/#app.run(host="127\.0\.0\.1",port=1234)/g; s/# app.run(host="0\.0\.0\.0", port = 5000)/app.run(host="0\.0\.0\.0", port = 5000)/g' ${WORKDIR}/chatGPTEx/main.py
18 | sed -i "s#program_dir+'/apikey.ini'#'/config/apikey.ini'#g" /app/chatGPTEx/main.py
19 | sed -i "s#program_dir+'/apikey.ini'#'/config/apikey.ini'#g" /app/chatGPTEx/search.py
20 | sed -i "s#os\.path\.join(program_dir, 'apikey.ini')#'/config/apikey.ini'#g" /app/chatGPTEx/search.py
21 | echo "Python更新依赖包"
22 | hash_old=$(cat /tmp/requirements.txt.sha256sum)
23 | hash_new=$(sha256sum requirements.txt)
24 | if [ "${hash_old}" != "${hash_new}" ]; then
25 | echo "检测到requirements.txt有变化,重新安装依赖..."
26 | pip install -r /app/chatGPTEx/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
27 | if [ $? -ne 0 ]; then
28 | echo "无法安装依赖,请更新镜像..."
29 | else
30 | echo "依赖安装成功..."
31 | sha256sum /app/chatGPTEx/requirements.txt > /tmp/requirements.txt.sha256sum
32 | fi
33 | fi
34 | else
35 | echo "更新失败"
36 | fi
37 | else
38 | echo "程序自动升级已关闭,如需自动升级请在创建容器时设置环境变量:AUTO_UPDATE=true"
39 | fi
40 | # 启动主程序
41 | exec python /app/chatGPTEx/main.py
42 |
--------------------------------------------------------------------------------
/chatGPTEx/main.py:
--------------------------------------------------------------------------------
1 | import json
2 | import datetime
3 | import os
4 | from promptsSearch import SearchPrompt,promptsDict
5 | from markdown_it import MarkdownIt
6 | from flask import Flask, render_template, request, Response
7 | from search import directQuery,web,detail,webDirect,WebKeyWord,load_history,APICallList,directQuery_stream,chatbot
8 | from graiax.text2img.playwright.plugins.code.highlighter import Highlighter
9 | from graiax.text2img.playwright import MarkdownConverter
10 | import configparser
11 | program_path = os.path.realpath(__file__)
12 | program_dir = os.path.dirname(program_path)
13 | config = configparser.ConfigParser()
14 | config.read(program_dir+'/apikey.ini')
15 | def parse_text(text):
16 | md = MarkdownIt("commonmark", {"highlight": Highlighter()}).enable("table")
17 | res = MarkdownConverter(md).convert(text)
18 | return res
19 | app = Flask(__name__)
20 | app.static_folder = 'static'
21 | @app.route("/")
22 | def home():
23 | return render_template("index.html")
24 |
25 | @app.route("/api/query")
26 | def get_bot_response():
27 | mode = str(request.args.get('mode'))
28 | userText = str(request.args.get('msg'))
29 | uuid = str(request.args.get('uuid'))
30 | now = datetime.datetime.now()
31 | now = now.strftime("%Y-%m-%d %H:%M")
32 | if mode=="chat":
33 | q = str(userText)
34 | promptName = str(request.args.get('prompt'))
35 | print(promptName)
36 | if promptName != "":
37 | if promptName in promptsDict:
38 | prompt = promptsDict[promptName]
39 | else:
40 | prompt = str(SearchPrompt(promptName)[0])
41 | prompt = promptsDict[prompt]
42 | return Response(directQuery_stream(q,conv_id=uuid,prompt=prompt), direct_passthrough=True, mimetype='application/octet-stream')
43 | else:
44 | return Response(directQuery_stream(q,conv_id=uuid), direct_passthrough=True, mimetype='application/octet-stream')
45 | elif mode == "web":
46 | q = 'current Time: '+ str(now) + '\n\nQuery:'+ str(userText)
47 | return Response(web(q,conv_id=uuid), direct_passthrough=True, mimetype='application/octet-stream')
48 | elif mode == "detail":
49 | q = 'current Time: '+ str(now) + '\n\nQuery:'+ str(userText)
50 | return Response(detail(q,conv_id=uuid), direct_passthrough=True, mimetype='application/octet-stream')
51 | elif mode =='webDirect':
52 | q = 'current Time: '+ str(now) + '\n\nQuery:'+ str(userText)
53 | return Response(webDirect(q,conv_id=uuid), direct_passthrough=True, mimetype='application/octet-stream')
54 | elif mode == 'WebKeyWord':
55 | q = str(userText)
56 | return Response(WebKeyWord(q,conv_id=uuid), direct_passthrough=True, mimetype='application/octet-stream')
57 | return "Error"
58 |
59 | @app.route("/api/addChat",methods=['POST'])
60 | def add_chat():
61 | uuid = str(request.form.get('uuid'))
62 | message = str(request.form.get('msg'))
63 | chatbot.add_to_conversation(message,role='assistant',convo_id=str(uuid))
64 | return parse_text(message+"\n\ntoken cost:"+str(chatbot.token_cost(convo_id=uuid)))
65 | @app.route("/api/chatLists")
66 | def get_chat_lists():
67 | if os.path.isfile(program_dir+'/chatLists.json'):
68 | with open(program_dir+'/chatLists.json', 'r', encoding='utf-8') as f:
69 | chatLists = json.load(f)
70 | chatLists["chatLists"] = list(reversed(chatLists["chatLists"]))
71 | return json.dumps(chatLists)
72 | else:
73 | with open(program_dir+'/chatLists.json', 'w', encoding='utf-8') as f:
74 | defaultChatLists = {
75 | "chatLists": [{
76 | "uuid": "default",
77 | "chatName": "Default"
78 | }]}
79 | json.dump(defaultChatLists,f,ensure_ascii=False)
80 | return json.dumps(defaultChatLists)
81 |
82 |
83 | @app.route("/api/history")
84 | def send_history():
85 | uuid = str(request.args.get('uuid'))
86 | msgs = []
87 | chats = load_history(conv_id=uuid)
88 | for chat in chats:
89 | queryTime = ''
90 | firstLine = chat['content'].split('\n')[0]
91 | # print(firstLine)
92 | if firstLine.find('current Time:')!=-1:
93 | queryTime = firstLine.split('current Time:')[-1]
94 | if chat['content'].find('Query:')!=-1:
95 | query = chat['content'].split('Query:')[1]
96 | chat['content'] = query
97 | if chat['role']=='user':
98 | msgs.append({'name': 'You', 'img': 'static/styles/person.jpg', 'side': 'right', 'text': parse_text(chat['content']), 'time': queryTime})
99 | elif chat['role']=='assistant':
100 | msgs.append({'name': 'ExChatGPT', 'img': 'static/styles/ChatGPT_logo.png', 'side': 'left', 'text': parse_text(chat['content']),'time': queryTime})
101 | return json.dumps(msgs,ensure_ascii=False)
102 | lastAPICallListLength = len(APICallList)
103 |
104 | @app.route("/api/APIProcess")
105 | def APIProcess():
106 | global lastAPICallListLength
107 | if len(APICallList) > lastAPICallListLength:
108 | lastAPICallListLength +=1
109 | return json.dumps(APICallList[lastAPICallListLength-1],ensure_ascii=False)
110 | else:
111 | return {}
112 |
113 | @app.route('/api/setChatLists',methods=['POST'])
114 | def set_chat_lists():
115 | with open(program_dir+'/chatLists.json', 'w', encoding='utf-8') as f:
116 | json.dump(request.json,f,ensure_ascii=False)
117 | return 'ok'
118 |
119 | @app.route('/api/promptsCompletion',methods=['get'])
120 | def promptsCompletion():
121 | prompt = str(request.args.get('prompt'))
122 | res = json.dumps(SearchPrompt(prompt),ensure_ascii=False)
123 | return res
124 |
125 | subscriptionKey = None
126 | region = None
127 | if 'Azure' in config:
128 | if 'subscriptionKey' in config['Azure']:
129 | subscriptionKey = config['Azure']['subscriptionKey']
130 | if 'region' in config['Azure']:
131 | region = config['Azure']['region']
132 | @app.route('/api/getAzureAPIKey',methods=['GET'])
133 | def AzureAPIKey():
134 | return json.dumps({'subscriptionKey':subscriptionKey, 'region':region},ensure_ascii=False)
135 |
136 | if __name__ == "__main__":
137 | app.config['JSON_AS_ASCII'] = False
138 | app.config['DEBUG'] = True
139 | #local config uncomment this line
140 | app.run(host="127.0.0.1",port=1234)
141 | #docker config uncomment this line
142 | # app.run(host="0.0.0.0", port = 5000)
143 |
--------------------------------------------------------------------------------
/chatGPTEx/main_GPT4.py:
--------------------------------------------------------------------------------
1 | #polished by GPT4
2 | import json
3 | import datetime
4 | import os
5 | import configparser
6 | from promptsSearch import SearchPrompt, promptsDict
7 | from markdown_it import MarkdownIt
8 | from flask import Flask, render_template, request, Response
9 | from search import (
10 | directQuery,
11 | web,
12 | detail,
13 | webDirect,
14 | WebKeyWord,
15 | load_history,
16 | APICallList,
17 | directQuery_stream,
18 | chatbot,
19 | )
20 | from graiax.text2img.playwright.plugins.code.highlighter import Highlighter
21 | from graiax.text2img.playwright import MarkdownConverter
22 |
23 | program_path = os.path.realpath(__file__)
24 | program_dir = os.path.dirname(program_path)
25 |
26 | config = configparser.ConfigParser()
27 | config.read(program_dir + "/apikey.ini")
28 |
29 |
30 | def parse_text(text):
31 | md = MarkdownIt("commonmark", {"highlight": Highlighter()}).enable("table")
32 | res = MarkdownConverter(md).convert(text)
33 | return res
34 |
35 |
36 | app = Flask(__name__)
37 | app.static_folder = "static"
38 |
39 |
40 | @app.route("/")
41 | def home():
42 | return render_template("index.html")
43 |
44 |
45 | @app.route("/api/query")
46 | def get_bot_response():
47 | mode = str(request.args.get("mode"))
48 | userText = str(request.args.get("msg"))
49 | uuid = str(request.args.get("uuid"))
50 | now = datetime.datetime.now()
51 | now = now.strftime("%Y-%m-%d %H:%M")
52 |
53 | if mode == "chat":
54 | q = str(userText)
55 | promptName = str(request.args.get("prompt"))
56 |
57 | if promptName != "":
58 | if promptName in promptsDict:
59 | prompt = promptsDict[promptName]
60 | else:
61 | prompt = str(SearchPrompt(promptName)[0])
62 | prompt = promptsDict[prompt]
63 | return Response(
64 | directQuery_stream(q, conv_id=uuid, prompt=prompt),
65 | direct_passthrough=True,
66 | mimetype="application/octet-stream",
67 | )
68 | else:
69 | return Response(
70 | directQuery_stream(q, conv_id=uuid),
71 | direct_passthrough=True,
72 | mimetype="application/octet-stream",
73 | )
74 | elif mode == "web":
75 | q = "current Time: " + str(now) + "\n\nQuery:" + str(userText)
76 | return Response(
77 | web(q, conv_id=uuid),
78 | direct_passthrough=True,
79 | mimetype="application/octet-stream",
80 | )
81 | elif mode == "detail":
82 | q = "current Time: " + str(now) + "\n\nQuery:" + str(userText)
83 | return Response(
84 | detail(q, conv_id=uuid),
85 | direct_passthrough=True,
86 | mimetype="application/octet-stream",
87 | )
88 | elif mode == "webDirect":
89 | q = "current Time: " + str(now) + "\n\nQuery:" + str(userText)
90 | return Response(
91 | webDirect(q, conv_id=uuid),
92 | direct_passthrough=True,
93 | mimetype="application/octet-stream",
94 | )
95 | elif mode == "WebKeyWord":
96 | q = str(userText)
97 | return Response(
98 | WebKeyWord(q, conv_id=uuid),
99 | direct_passthrough=True,
100 | mimetype="application/octet-stream",
101 | )
102 | return "Error"
103 |
104 |
105 | @app.route("/api/addChat", methods=["POST"])
106 | def add_chat():
107 | uuid = str(request.form.get("uuid"))
108 | message = str(request.form.get("msg"))
109 | chatbot.add_to_conversation(message, role="assistant", convo_id=str(uuid))
110 | return parse_text(
111 | message + "\n\ntoken cost:" + str(chatbot.token_cost(convo_id=uuid))
112 | )
113 |
114 |
115 | @app.route("/api/chatLists")
116 | def get_chat_lists():
117 | if os.path.isfile(program_dir + "/chatLists.json"):
118 | with open(program_dir +"/chatLists.json", "r", encoding="utf-8") as f:
119 | chatLists = json.load(f)
120 | chatLists["chatLists"] = list(reversed(chatLists["chatLists"]))
121 | return json.dumps(chatLists)
122 | else:
123 | with open(program_dir + "/chatLists.json", "w", encoding="utf-8") as f:
124 | defaultChatLists = {
125 | "chatLists": [
126 | {
127 | "uuid": "default",
128 | "chatName": "Default",
129 | }
130 | ]
131 | }
132 | json.dump(defaultChatLists, f, ensure_ascii=False)
133 | return json.dumps(defaultChatLists)
134 |
135 | @app.route("/api/history")
136 | def send_history():
137 | uuid = str(request.args.get("uuid"))
138 | msgs = []
139 | chats = load_history(conv_id=uuid)
140 | for chat in chats:
141 | queryTime = ""
142 | firstLine = chat["content"].split("\n")[0]
143 | if firstLine.find("current Time:") != -1:
144 | queryTime = firstLine.split("current Time:")[-1]
145 | if chat["content"].find("Query:") != -1:
146 | query = chat["content"].split("Query:")[1]
147 | chat["content"] = query
148 | if chat["role"] == "user":
149 | msgs.append(
150 | {
151 | "name": "You",
152 | "img": "static/styles/person.jpg",
153 | "side": "right",
154 | "text": parse_text(chat["content"]),
155 | "time": queryTime,
156 | }
157 | )
158 | elif chat["role"] == "assistant":
159 | msgs.append(
160 | {
161 | "name": "ExChatGPT",
162 | "img": "static/styles/ChatGPT_logo.png",
163 | "side": "left",
164 | "text": parse_text(chat["content"]),
165 | "time": queryTime,
166 | }
167 | )
168 | return json.dumps(msgs, ensure_ascii=False)
169 | lastAPICallListLength = len(APICallList)
170 |
171 | @app.route("/api/APIProcess")
172 | def APIProcess():
173 | global lastAPICallListLength
174 | if len(APICallList) > lastAPICallListLength:
175 | lastAPICallListLength += 1
176 | return json.dumps(
177 | APICallList[lastAPICallListLength - 1], ensure_ascii=False
178 | )
179 | else:
180 | return {}
181 |
182 | @app.route("/api/setChatLists", methods=["POST"])
183 | def set_chat_lists():
184 | with open(program_dir + "/chatLists.json", "w", encoding="utf-8") as f:
185 | json.dump(request.json, f, ensure_ascii=False)
186 | return "ok"
187 |
188 | @app.route("/api/promptsCompletion", methods=["get"])
189 | def promptsCompletion():
190 | prompt = str(request.args.get("prompt"))
191 | res = json.dumps(SearchPrompt(prompt), ensure_ascii=False)
192 | return res
193 |
194 | subscriptionKey = None
195 | region = None
196 | if "Azure" in config:
197 | if "subscriptionKey" in config["Azure"]:
198 | subscriptionKey = config["Azure"]["subscriptionKey"]
199 | if "region" in config["Azure"]:
200 | region = config["Azure"]["region"]
201 |
202 | @app.route("/api/getAzureAPIKey", methods=["GET"])
203 | def AzureAPIKey():
204 | return json.dumps(
205 | {"subscriptionKey": subscriptionKey, "region": region}, ensure_ascii=False
206 | )
207 |
208 | if __name__ == "__main__":
209 | app.config["JSON_AS_ASCII"] = False
210 | app.config["DEBUG"] = True
211 | # local config uncomment this line
212 | app.run(host="127.0.0.1", port=1234)
213 | # docker config uncomment this line
214 | # app.run(host="0.0.0.0", port=5000)
215 |
--------------------------------------------------------------------------------
/chatGPTEx/prompts/APIExtraPrompt.txt:
--------------------------------------------------------------------------------
1 | User Query: {query}
2 | ChatGPT API call response: {callResponse}
3 | Instructions:
4 | Your task is to generate a list of API calls to Question Answering APIs to extract relevant information and provide a better understanding of the input query. You have already received some API call responses, and your goal is to add more API calls to verify the previous responses and improve your reply.
5 | To make an API call, use the following format: "{"API": "{API}", "query": "{query}"}". Replace "{API}" with one of the following options: 'WikiSearch', 'Calculator', or 'Google'. Replace "{query}" with the specific question you want to ask to extract relevant information.
6 | Note that the WikiSearch API requires an English input consisting of a precise concept word related to the question, such as a person's name. The Google API requires a full, complete question in the same language as the input query, including enough information about the question, such as who, what, when, where, and why. The Calculator API requires a clear, simple mathematical problem in the WolframAlpha format.
7 | Here are some examples of API calls:
8 | Input: 电视剧狂飙怎么样, 和三体比应该看哪一部?
9 | Output: {"calls":[{"API": "Google", "query": "电视剧狂飙"},{"API": "Google", "query": "电视剧狂飙评分"},{"API": "Google", "query": "电视剧三体评分"},{"API": "Google", "query": "三体和狂飙谁更好?"}]}
10 | Input: Out of 1400 participants, 400 passed the test.
11 | Output: {"calls":[{"API": "Calculator", "query": "400 divided by 1400"}]}
12 | Your goal is to call at least 3 APIs to extract relevant information and provide a more detailed and comprehensive reply to the query. Note that you should only output the API calls in JSON format and not include any other text.
13 | Remember the following guidelines:
14 | Use the information extracted from the API calls to provide a detailed and precise reply to the query.
15 | Add new API calls to verify the previous responses and improve the accuracy of your reply.
16 | Call at least 3 APIs to ensure that the API call results are detailed and relevant.
17 | Use clear and concise language to ensure that your reply is easily understandable.
18 | Keep in mind the token cost limitation when writing your reply.
19 | Sort the JSON order based on relevance and importance as requested by the API query, with the most relevant item at the beginning of the list for easier understanding.
--------------------------------------------------------------------------------
/chatGPTEx/prompts/APIPrompt.txt:
--------------------------------------------------------------------------------
1 | Input: {query}
2 | ChatGpt response: {resp}
3 | Instructions:
4 | Your task is to generate a list of API calls to a Question Answering API based on the input query and chatgpt response. The API calls should help extract relevant information and provide a better understanding of the input query and verify the response of chatgpt.
5 | You can make an API call by using the following format: "{"API": "{API}", "query": "{query}"}". Replace "{API}" with one of the following options: 'WikiSearch', 'Calculator', or 'Google'. Replace "{query}" with the specific question you want to ask to extract relevant information.
6 | Note that the WikiSearch API requires an English input consisting of a precise concept word related to the question, such as a person's name. The Google API requires a full, complete question in the same langeuage as the query that includes enough information about the question, such as who, what, when, where, and why. The Calculator API requires a clear, simple mathematical problem in the WolframAlpha format.
7 | Here are some examples of API calls:
8 | Input: Coca-Cola, or Coke, is a carbonated soft drink manufactured by the Coca-Cola Company.
9 | Output: {"calls": [{"API": "Google", "query": "What other name is Coca-Cola known by?"},
10 | {"API": "Google", "query": "Who manufactures Coca-Cola?"}]}
11 | Input: Out of 1400 participants, 400 passed the test.
12 | Output: {"calls":[{"API": "Calculator", "query": "400 / 1400"}]}
13 | Input: 电视剧狂飙怎么样, 和三体比应该看哪一部?
14 | Output: {"calls":[{"API": "Google", "query": "电视剧狂飙"},{"API": "Google", "query": "电视剧狂飙评分"},{"API": "Google", "query": "电视剧三体评分"},{"API": "Google", "query": "三体和狂飙谁更好?"}]}
15 | To ensure better understanding, the Google API question must match the input query language. Additionally, the API calls should thoroughly validate every detail in ChatGPT's response.
16 | Sort the JSON order based on relevance and importance as requested by the API query, with the most relevant item at the beginning of the list for easier understanding.
--------------------------------------------------------------------------------
/chatGPTEx/prompts/ReplySum.txt:
--------------------------------------------------------------------------------
1 | Query: {query}
2 | API calls in JSON format generated by ChatGPT to extract related and helpful information for the query:
3 | {apicalls}
4 | Instructions:
5 | Using the provided API call results in JSON format, provide a detailed and precise reply to the given query. If the query has any errors, use the API call results to correct them in your reply.
6 | Here are some guidelines to help you write your reply:
7 | - Use the information extracted from the API calls to provide a comprehensive and relevant reply to the query.
8 | - If the API calls suggest that there is an error in the original query, point it out and provide a corrected version.
9 | - Ensure that your reply is detailed and precise, providing all relevant information related to the query.
10 | - Use clear and concise language to ensure that your reply is easily understandable.
11 | - When answering, do not make up information, but base it on the results of the API.
12 | Providing all relevant detailed information data relevant to the query, as comprehensive and detailed as possible, at least 300 words.
13 | Your reply should be written in the same language as the query(if the query is asked in Chinese, reply in Chinese) and be easy to understand.
--------------------------------------------------------------------------------
/chatGPTEx/prompts/conversationSummary.txt:
--------------------------------------------------------------------------------
1 | Your goal is to summarize the provided conversation in English. Your summary should be concise and focus on the key information to facilitate better dialogue for the large language model.
2 | Ensure that you include all necessary details and relevant information while still reducing the length of the conversation as much as possible. Your summary should be clear and easily understandable for the ChatGpt model providing a comprehensive and concise summary of the conversation.
3 | {conversation}
4 |
--------------------------------------------------------------------------------
/chatGPTEx/prompts/summary.txt:
--------------------------------------------------------------------------------
1 | Query: {query}
2 |
3 | API calls in JSON type generated by ChatGpt to provide related and helpful information for this query:
4 |
5 | {callResponse}
6 |
7 | Instructions:
8 |
9 | Your task is to extract useful and specific information for the given query using the provided API call results in JSON format. Your goal is to provide a detailed and concise summary of the information extracted and make it more related and helpful to the query to improve the understanding of the ChatGpt.
10 |
11 | If there is misinformation in the API call response, you should correct it in your extract. If you are unsure about something, do not include it in your extract, but instead, include a new query asking for clarification.
12 |
13 | Remember to keep the extract concise and relevant to the query, while still providing enough information to give a clear understanding of the topic.
14 |
15 | Optimize your response by using shorter and more specific sentences to reduce token cost while making it easy to understand for the ChatGpt model.
16 |
17 | Overall, your extract should provide valuable and accurate information to help the ChatGpt better understand the query and provide a comprehensive response.
--------------------------------------------------------------------------------
/chatGPTEx/promptsSearch.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | from fuzzywuzzy import process
4 | from xpinyin import Pinyin
5 | import csv
6 | import json
7 | name_list=[]
8 | program_path = os.path.realpath(__file__)
9 | program_dir = os.path.dirname(program_path)
10 | json_file_path = program_dir+'/prompts/prompts.json'
11 | name_list = []
12 | promptsJSON = {}
13 | promptsDict = {}
14 | with open(json_file_path, 'r', encoding='utf-8') as f:
15 | promptsJSON = json.load(f)
16 | for prompt in promptsJSON:
17 | name_list.append(prompt['act'])
18 | pinlist=[]
19 | for prompt in promptsJSON:
20 | promptsDict[prompt['act']] = prompt['prompt']
21 | pin =Pinyin()
22 | for i in name_list:
23 | pinlist.append([re.sub('-','',pin.get_pinyin(i)),i])
24 | def SearchPrompt(name,resultLimit=7):
25 | searchResults=process.extract(name, name_list, limit=resultLimit)
26 | searchResultsPin=process.extract(name, pinlist, limit=resultLimit)
27 | finalResult=[]
28 | for searchResult in searchResults:
29 | finalResult.append([searchResult[1],searchResult[0]])
30 | flag=0
31 | for searchResult in searchResultsPin:
32 | flag=0
33 | for i in finalResult:
34 | if searchResult[0][1]==i[1]:
35 | flag=1
36 | break
37 | if flag==0:
38 | finalResult.append([searchResult[1],searchResult[0][1]])
39 | finalResult.sort(reverse=True)
40 | finalResultList=[]
41 | cnt=0
42 | print(finalResult)
43 | for res in finalResult:
44 | if(res[0]<50): break
45 | finalResultList.append(res[1])
46 | cnt+=1
47 | if cnt>=resultLimit:break
48 | return finalResultList
49 | if __name__ == '__main__':
50 | print(SearchPrompt('zhengze'))
51 |
52 |
53 |
--------------------------------------------------------------------------------
/chatGPTEx/requirements.txt:
--------------------------------------------------------------------------------
1 | fastapi==0.93.0
2 | Flask==2.0.2
3 | Markdown==3.3.4
4 | mdit_py_plugins==0.3.5
5 | Pygments==2.14.0
6 | regex==2022.7.9
7 | requests==2.26.0
8 | uvicorn==0.20.0
9 | graiax-playwright==0.2.1
10 | graiax-text2img-playwright==0.3.0
11 | python-Levenshtein==0.20.9
12 | fuzzywuzzy==0.18.0
13 | tiktoken==0.3.1
14 | xpinyin==0.7.6
15 | jieba==0.42.1
16 |
--------------------------------------------------------------------------------
/chatGPTEx/search.py:
--------------------------------------------------------------------------------
1 | from api_class import GoogleSearchAPI, WikiSearchAPI, WolframAPI
2 | from optimizeOpenAI import ExChatGPT,APICallList
3 | import threading
4 | import json
5 | import re
6 | import configparser
7 | import os
8 | import requests
9 | program_path = os.path.realpath(__file__)
10 | program_dir = os.path.dirname(program_path)
11 | config_path = os.path.join(program_dir, 'apikey.ini')
12 | config = configparser.ConfigParser()
13 | config.read(program_dir+'/apikey.ini')
14 | if not os.path.exists(config_path):
15 | print("Config file doesn't exist!")
16 | exit()
17 | config.read(config_path)
18 | if not config.has_section('OpenAI'):
19 | print("OpenAI section doesn't exist in config file!")
20 | exit()
21 | API_PROXY = None
22 | if 'Proxy' in config and 'api_proxy' in config['Proxy']:
23 | API_PROXY = config['Proxy']['api_proxy']
24 | if(API_PROXY == ''):
25 | API_PROXY = None
26 | openAIAPIKeys = []
27 | try:
28 | items = config.items('OpenAI')
29 | for key, value in items:
30 | if key.startswith('key'):
31 | openAIAPIKeys.append(value)
32 | except configparser.Error as e:
33 | print(f"Error reading config file: {str(e)}")
34 | exit()
35 | chatbot = ExChatGPT(api_keys=openAIAPIKeys,apiTimeInterval=20,proxy=API_PROXY)
36 |
37 | googleAPIKeys = []
38 | SEARCH_ENGINE_IDs = []
39 | try:
40 | items = config.items('Google')
41 | for key, value in items:
42 | if key.startswith('google_api_key'):
43 | googleAPIKeys.append(value)
44 | if key.startswith('search_engine_id'):
45 | SEARCH_ENGINE_IDs.append(value)
46 | for i in range(min(len(googleAPIKeys),len(SEARCH_ENGINE_IDs))):
47 | googleAPIKeys[i] = (googleAPIKeys[i],SEARCH_ENGINE_IDs[i])
48 | Google = GoogleSearchAPI(googleAPIKeys,proxy=API_PROXY)
49 | except configparser.Error as e:
50 | print(f"Error reading config file: {str(e)}")
51 |
52 | WolframAPIKeys = []
53 | try:
54 | items = config.items('WolframAlpha')
55 | for key, value in items:
56 | if key.startswith('wolframalpha_app_id'):
57 | WolframAPIKeys.append(value)
58 | Wolfram = WolframAPI(WolframAPIKeys,proxy=API_PROXY)
59 | except configparser.Error as e:
60 | print(f"Error reading config file: {str(e)}")
61 | Wiki = WikiSearchAPI([],proxy=API_PROXY)
62 | max_token = 1000
63 | hint_recall_dialog = json.loads(json.dumps({"calls":[{"API":"ExChatGPT","query":"Recall our dialogs…"}]},ensure_ascii=False))
64 | hint_api_finished = json.loads(json.dumps({"calls":[{"API":"System","query":"API calls finished"}]},ensure_ascii=False))
65 | def load_history(conv_id = 'default'):
66 | if(conv_id not in chatbot.convo_history):
67 | chatbot.reset(conv_id)
68 | chatbot.backup_chat_history()
69 | return chatbot.convo_history[conv_id]
70 | def detail_old(query):
71 | call_res0 = search(APIQuery(query))
72 | Sum0 = Summary(query, call_res0)
73 | call_res1 = search(APIExtraQuery(query,Sum0))
74 | Sum1 = Summary(query, call_res1)
75 | print('\n\nChatGpt: \n' )
76 | result = SumReply(query, str(Sum0) + str(Sum1))
77 | return result
78 | def detail(query,conv_id = 'default'):
79 | global APICallList
80 | call_res0 = search(APIQuery(query),1000)
81 | print(f'API calls response:\n {call_res0}')
82 | call_res1 = search(APIExtraQuery(query,call_res0),1000)
83 | print(f'API calls response:\n {call_res1}')
84 | result = SumReply(query, str(call_res0) + str(call_res1),max_token=2000,conv_id=conv_id)
85 | for data in result:
86 | yield data.encode()
87 | chatbot.add_to_conversation('', "assistant", convo_id=conv_id)
88 | chatbot.delete_last2_conversation(conv_id)
89 | chatbot.add_to_conversation(str(query), "user", convo_id=conv_id)
90 | def web(query,conv_id = 'default'):
91 | global APICallList
92 | APICallList.append(hint_recall_dialog)
93 | resp = directQuery(f'Query: {query}', conv_id= conv_id)
94 | chatbot.delete_last2_conversation(conv_id)
95 | apir = APIQuery(query,resp=resp)
96 | call_res0 = search(apir,1600)
97 | APICallList.append(hint_api_finished)
98 | print(f'API calls response:\n {call_res0}')
99 | result = SumReply(f'Query: {query}' ,str(call_res0),max_token=2000, conv_id=conv_id)
100 | for data in result:
101 | yield data.encode()
102 | chatbot.add_to_conversation('', "assistant", convo_id=conv_id)
103 | chatbot.delete_last2_conversation(conv_id)
104 | chatbot.add_to_conversation(str(query), "user", convo_id=conv_id)
105 | chatbot.backup_chat_history()
106 | def webDirect(query,conv_id = 'default'):
107 | global APICallList
108 | apir = APIQuery(query)
109 | call_res0 = search(apir,1600)
110 | APICallList.append(hint_api_finished)
111 | print(f'API calls response:\n {call_res0}')
112 | result = SumReply(f'{query}', str(call_res0), conv_id=conv_id)
113 | for data in result:
114 | yield data.encode()
115 | chatbot.add_to_conversation('', "assistant", convo_id=conv_id)
116 | chatbot.delete_last2_conversation(conv_id)
117 | chatbot.add_to_conversation(str(query), "user", convo_id=conv_id)
118 | chatbot.backup_chat_history()
119 | def WebKeyWord(query,conv_id = 'default'):
120 | global APICallList
121 | q = chatbot.ask(
122 | f'Given a user prompt "{query}", respond with "none" if it is directed at the chatbot or cannot be answered by an internet search. Otherwise, provide a concise search query for a search engine. Avoid adding any additional text to the response to minimize token cost.',
123 | convo_id="search",
124 | temperature=0.0,
125 | ).strip()
126 | print("Searching for: ", q, "")
127 | if q == "none":
128 | search_results = '{"results": "No search results"}'
129 | else:
130 | APICallList.append(json.loads(json.dumps({"calls":[{"API":"ddg-api","query": "Searching for:" + q }]})))
131 | search_results = requests.post(
132 | url="https://ddg-api.herokuapp.com/search",
133 | json={"query": q, "limit": 4},
134 | timeout=10,
135 | ).text
136 | search_res = json.dumps(json.loads(search_results), indent=4,ensure_ascii=False)
137 | chatbot.add_to_conversation(
138 | "Search results:" + search_res,
139 | "system",
140 | convo_id=conv_id,
141 | )
142 | APICallList.append(hint_answer_generating)
143 | result = chatbot.ask_stream(query, "user", convo_id=conv_id)
144 | for data in result:
145 | yield data.encode()
146 | chatbot.add_to_conversation('', "assistant", convo_id=conv_id)
147 | chatbot.delete_last2_conversation(conv_id)
148 | chatbot.add_to_conversation(str(query), "user", convo_id=conv_id)
149 | chatbot.backup_chat_history()
150 | def directQuery(query,conv_id = 'default',prompt = ''):
151 | global APICallList
152 | APICallList.append(hint_answer_generating)
153 | response = chatbot.ask(prompt+'\n'+query,convo_id=conv_id)
154 | chatbot.delete_last2_conversation(conv_id)
155 | chatbot.add_to_conversation(str(query), "user", convo_id=conv_id)
156 | chatbot.add_to_conversation(str(response), "assistant", convo_id=conv_id)
157 | print(f'Direct Query: {query}\nChatGpt: {response}')
158 | return response+ '\n\n token_cost: '+ str(chatbot.token_cost())
159 | def directQuery_stream(query,conv_id = 'default',prompt = ''):
160 | global APICallList
161 | APICallList.append(hint_answer_generating)
162 | if(prompt!=''):
163 | chatbot.add_to_conversation(prompt, "system", convo_id=conv_id)
164 | response = chatbot.ask_stream(prompt+'\n'+query,convo_id=conv_id)
165 | for data in response:
166 | yield data.encode()
167 | chatbot.add_to_conversation('TEST!', "assistant", convo_id=conv_id)
168 | chatbot.delete_last2_conversation(conv_id)
169 | chatbot.add_to_conversation(str(query), "user", convo_id=conv_id)
170 | print(f'Direct Query: {query}\nChatGpt: {response}')
171 | def APIQuery(query,resp =''):
172 | with open(program_dir+"/prompts/APIPrompt.txt", "r", encoding='utf-8') as f:
173 | prompt = f.read()
174 | prompt = prompt.replace("{query}", query)
175 | prompt = prompt.replace("{resp}", resp)
176 | response = ""
177 | chatbot.reset(convo_id='api',system_prompt='Your are a API caller for a LLM, you need to call some APIs to get the information you need.')
178 | response = chatbot.ask(prompt,convo_id='api')
179 | pattern = r"(\{[\s\S\n]*\"calls\"[\s\S\n]*\})"
180 | match = re.search(pattern, response)
181 | global APICallList
182 | if match:
183 | json_data = match.group(1)
184 | result = json.loads(json_data)
185 | print(f'API calls: {result}\n')
186 | APICallList.append(result)
187 | return result
188 | return json.loads("{\"calls\":[]}")
189 | def APIExtraQuery(query,callResponse):
190 | with open(program_dir+"/prompts/APIExtraPrompt.txt", "r",encoding='utf-8') as f:
191 | prompt = f.read()
192 | prompt = prompt.replace("{query}", query)
193 | prompt = prompt.replace("{callResponse}", str(callResponse))
194 | chatbot.reset(convo_id='api',system_prompt='Your are a API caller for a LLM, you need to call some APIs to get the information you need.')
195 | response = chatbot.ask(prompt,convo_id='api')
196 | pattern = r"(\{[\s\S\n]*\"calls\"[\s\S\n]*\})"
197 | match = re.search(pattern, response)
198 | global APICallList
199 | if match:
200 | json_data = match.group(1)
201 | result = json.loads(json_data)
202 | APICallList.append(result)
203 | print(f'API calls: {result}\n')
204 | return result
205 | return json.loads("{\"calls\":[]}")
206 | hint_answer_generating = json.loads(json.dumps({"calls":[{"API":"ExChatGPT","query":"Generating answers for you…"}]}))
207 | def SumReply(query, apicalls, max_token=2000, conv_id = 'default'):
208 | global APICallList
209 | APICallList.append(hint_answer_generating)
210 | with open(program_dir+"/prompts/ReplySum.txt", "r",encoding='utf-8') as f:
211 | prompt = f.read()
212 | apicalls = str(apicalls)
213 | while(chatbot.token_str(apicalls) > max_token):
214 | apicalls = apicalls[:-100]
215 | prompt = prompt.replace("{query}", query)
216 | prompt = prompt.replace("{apicalls}", apicalls)
217 | response = chatbot.ask_stream(prompt,convo_id=conv_id)
218 | for data in response:
219 | yield data
220 | print(f'ChatGPT SumReply:\n {response}\n')
221 | def Summary(query, callResponse):
222 | with open(program_dir+"/prompts/summary.txt", "r",encoding='utf-8') as f:
223 | prompt = f.read()
224 | prompt = prompt.replace("{query}", query)
225 | prompt = prompt.replace("{callResponse}", callResponse)
226 | chatbot.reset(convo_id='sum',system_prompt='Your need to summarize the information you got from the APIs.')
227 | response = chatbot.ask_stream(prompt,convo_id='sum')
228 | print(f'Summary : {response}\n')
229 | return response
230 | def search(content,max_token=2000,max_query=5):
231 | call_list = content['calls']
232 | # global search_data
233 | global call_res
234 | call_res = {}
235 | def google_search(query, num_results=4,summarzie = False):
236 | search_data = Google.call(query, num_results=num_results)
237 | if summarzie:
238 | summary_data = search_data
239 | call_res['google/' + query] = summary_data
240 | else:
241 | call_res['google/' + query] = search_data
242 | def wiki_search(query, num_results=3,summarzie = False):
243 | search_data = Wiki.call(query, num_results=num_results)
244 | if summarzie:
245 | summary_data = search_data
246 | call_res['wiki/' + query] = summary_data
247 | else:
248 | call_res['wiki/' + query] = search_data
249 | call_res['wiki/' + query] = search_data
250 | def wolfram_search(query, num_results=3):
251 | search_data = Wolfram.call(query, num_results)
252 | call_res['wolfram/' + query] = search_data
253 | all_threads = []
254 | for call in call_list[:max_query]:
255 | q = call['query']
256 | api = call['API']
257 | if api.lower() == 'google':
258 | t = threading.Thread(target=google_search, args=[q, 4, False])
259 | elif api.lower() == 'wikisearch':
260 | t = threading.Thread(target=wiki_search, args=[q, 1, False])
261 | elif api.lower() == 'calculator':
262 | t = threading.Thread(target=wolfram_search, args=[q])
263 | else:
264 | continue
265 | all_threads.append(t)
266 | for t in all_threads:
267 | t.start()
268 | for t in all_threads:
269 | t.join()
270 | call_res = {key: value for key, value in call_res.items() if len(str(value)) >= 10}
271 | call_res = json.loads(json.dumps(call_res,ensure_ascii=False))
272 | res = str(call_res)
273 | while chatbot.token_str(res) > max_token:
274 | flag = 0
275 | for key,value in call_res.items():
276 | if chatbot.token_str(res) <= max_token: break
277 | if len(value) > 2 and key.find('wolfram') == -1:
278 | flag = 1
279 | value = value[:-1]
280 | call_res[key] = value
281 | res = str(call_res)
282 | if flag == 0: break
283 | while chatbot.token_str(res) > max_token:
284 | res = res[:-100]
285 | return res
286 | if __name__ == "__main__":
287 | print(Google.call('狂飙电视剧', num_results=10))
--------------------------------------------------------------------------------
/chatGPTEx/static/js/newChat.js:
--------------------------------------------------------------------------------
1 | function get(selector, root = document) {
2 | return root.querySelector(selector);
3 | }
4 | const newChatBtn = get(".new-chat");
5 | const sidebarList = get(".sidebar-list");
6 | const sidebar = get(".sidebar")
7 | function fetchChatLists() {
8 | fetch('/api/chatLists')
9 | .then(response => response.json())
10 | .then(data => {
11 | if (data.hasOwnProperty('chatLists')) {
12 | console.log(data)
13 | data.chatLists.forEach(message => {
14 | NewChat(message.chatName, message.uuid);
15 | sidebar.scrollTop = sidebar.scrollHeight;
16 | });
17 | }
18 | })
19 | .catch(error => console.error(error));
20 | }
21 | fetchChatLists();
22 | function formatChatDate(date) {
23 | const M = "0" + (date.getMonth() + 1);
24 | const D = "0" + date.getDate();
25 | const h = "0" + date.getHours();
26 | const m = "0" + date.getMinutes();
27 | return `${M.slice(-2)}/${D.slice(-2)} ${h.slice(-2)}:${m.slice(-2)}`;
28 | }
29 | function generateUUID() {
30 | // Generate a random UUID
31 | // Adapted from https://stackoverflow.com/a/2117523/7049894
32 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
33 | const r = Math.random() * 16 | 0;
34 | const v = c == 'x' ? r : (r & 0x3 | 0x8);
35 | return v.toString(16);
36 | });
37 | }
38 | function NewChat(ChatName, uuid) {
39 | if (ChatName == null) {
40 | ChatName = 'New Chat ' + formatChatDate(new Date())
41 | }
42 | const newChatItem = `
43 |
44 |
45 |
47 |
48 |
49 | ${ChatName}
50 |
51 |
52 |
54 |
55 |
56 |
57 |
58 |
59 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | `
71 | if (uuid == null) {
72 | uuid = generateUUID();
73 | }
74 | sidebarList.insertAdjacentHTML('afterbegin', newChatItem);
75 | sidebarList.firstElementChild.setAttribute('uuid', uuid);
76 | deleteButton = sidebarList.firstElementChild.querySelector(".delete-button");
77 | console.log(sidebarList.firstElementChild.getAttribute('uuid'));
78 | const chatList = deleteButton.closest('.chat-list');
79 | deleteButton.addEventListener('click', function () {
80 | setChatLists();
81 | const chatLists = document.querySelectorAll('.chat-list');
82 | if (chatLists.length > 1) {
83 | chatList.remove();
84 | }
85 | });
86 | const editButton = chatList.querySelector('.edit-button');
87 | editButton.addEventListener('click', function () {
88 | const chatListText = editButton.parentNode.parentNode.querySelector('.chat-list-text')
89 | console.log(chatListText)
90 | chatListText.contentEditable = true;
91 | chatListText.focus = true;
92 | chatListText.setAttribute('spellcheck') = false;
93 | });
94 | const chatListText = chatList.querySelector('.chat-list-text')
95 | chatListText.contentEditable = false;
96 | chatList.addEventListener('blur', function () {
97 | chatListText.contentEditable = false;
98 | });
99 | chatList.addEventListener('click', function () {
100 | chatListParent = chatList.parentNode;
101 | if (chatListParent) {
102 | if(chatListParent.firstElementChild == chatList) return;
103 | chatListParent.insertBefore(chatList, chatListParent.firstElementChild);
104 | loadHistory();
105 | setChatLists();
106 | }
107 | });
108 | }
109 | newChatBtn.addEventListener("click", function () {
110 | NewChat(null, null);
111 | loadHistory();
112 | setChatLists();
113 | });
114 | const deleteButtons = document.querySelectorAll('.delete-button');
115 | deleteButtons.forEach((deleteButton) => {
116 | deleteButton.addEventListener('click', function () {
117 | const chatList = deleteButton.closest('.chat-list');
118 | const chatLists = document.querySelectorAll('.chat-list');
119 | if (chatLists.length > 1) {
120 | setChatLists();
121 | chatList.remove();
122 | }
123 | });
124 | });
125 | const editButtons = document.querySelectorAll(".edit-button");
126 | editButtons.forEach((editButton) => {
127 | editButton.addEventListener('click', function () {
128 | const chatListText = editButton.parentNode.parentNode.querySelector('.chat-list-text')
129 | console.log(chatListText)
130 | chatListText.contentEditable = true;
131 | chatListText.focus = true;
132 | });
133 | });
134 | const chatListTexts = document.querySelectorAll('.chat-list-text');
135 | chatListTexts.forEach((chatListText) => {
136 | chatListText.contentEditable = false;
137 | chatListText.addEventListener('blur', function () {
138 | chatListText.contentEditable = false;
139 | });
140 | });
141 |
142 | function setChatLists() {
143 | lists = [];
144 |
145 | // Convert the JavaScript object to a JSON string
146 | chatLists = document.querySelectorAll('.chat-list');
147 | chatLists.forEach((chatList) => {
148 | const uuid = chatList.getAttribute('uuid');
149 | const name = chatListText = chatList.querySelector('.chat-list-text').textContent
150 | lists.push({ "uuid": uuid, "chatName": name })
151 | })
152 | const chatListJSON = {
153 | "chatLists": lists
154 | };
155 | console.log(chatListJSON)
156 | const options = {
157 | method: 'POST',
158 | headers: {
159 | 'Content-Type': 'application/json'
160 | },
161 | body: JSON.stringify(chatListJSON)
162 | };
163 | fetch('/api/setChatLists', options)
164 | .then(response => {
165 | if (response.ok) {
166 | return response.ok;
167 | } else {
168 | throw new Error('Failed to send data to server');
169 | }
170 | })
171 | .then(data => {
172 | console.log('Server response:', data);
173 | })
174 | .catch(error => {
175 | console.error('Error:', error);
176 | });
177 | }
178 |
--------------------------------------------------------------------------------
/chatGPTEx/static/js/tts.js:
--------------------------------------------------------------------------------
1 | const enableChinese = true;
2 | // https://clearn.microsoft.com/zh-cn/azure/cognitive-services/speech-service/language-support?tabs=tts
3 | const createSpeechConfig = (() => {
4 | let speechConfig;
5 | return async () => {
6 | if (!speechConfig) {
7 | try {
8 | const response = await fetch('/api/getAzureAPIKey');
9 | const data = await response.json();
10 | const subscriptionKey = data.subscriptionKey;
11 | const region = data.region;
12 | console.log('Subscription Key:', subscriptionKey);
13 | console.log('Region:', region);
14 | speechConfig = SpeechSDK.SpeechConfig.fromSubscription(subscriptionKey, region);
15 | speechConfig.speechRecognitionLanguage = "en-US";
16 | if (enableChinese) {
17 | speechConfig.speechRecognitionLanguage = "zh-CN";
18 | }
19 | } catch (error) {
20 | console.error('Error fetching Azure API Key:', error);
21 | }
22 | }
23 |
24 | return speechConfig;
25 | };
26 | })();
27 | let synthesizer;
28 | async function TTS(text) {
29 | if (!synthesizer) {
30 | const speechConfig = await createSpeechConfig();
31 | synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig);
32 | }
33 | const EnglishSsml = `
34 |
35 |
36 | ${text}
37 |
38 |
39 | `;
40 | const ChineseSsml = `
41 |
42 |
43 | ${text}
44 |
45 |
46 | `;
47 | var ssml;
48 | if (enableChinese) {
49 | ssml = ChineseSsml;
50 | } else {
51 | ssml = EnglishSsml;
52 | }
53 | return new Promise((resolve, reject) => {
54 | synthesizer.speakSsmlAsync(
55 | ssml,
56 | (result) => {
57 | if (result.reason === SpeechSDK.ResultReason.SynthesizingAudioCompleted) {
58 | console.log(`synthesis finished for [${text}].\n`);
59 | resolve();
60 | } else if (result.reason === SpeechSDK.ResultReason.Canceled) {
61 | console.log(`synthesis failed. Error detail: ${result.errorDetails}\n`);
62 | reject(result.errorDetails);
63 | }
64 | window.console.log(result);
65 | },
66 | (err) => {
67 | window.console.log(err);
68 | reject(err);
69 | }
70 | );
71 | });
72 | }
73 | var enableRecord = false
74 | var lastRecordmsg = ''
75 | let speechRecognizer
76 | async function fromMic() {
77 | if(!speechRecognizer){
78 | const speechConfig = await createSpeechConfig();
79 | const audioConfig = SpeechSDK.AudioConfig.fromDefaultMicrophoneInput();
80 | speechRecognizer = new SpeechSDK.SpeechRecognizer(speechConfig, audioConfig);
81 | }
82 | console.log(enableRecord)
83 | if (enableRecord) {
84 | speechRecognizer.stopContinuousRecognitionAsync();
85 | enableRecord = false;
86 | return;
87 | }
88 | lastRecordmsg = msgerInput.value;
89 | speechRecognizer.recognizing = (s, e) => {
90 | console.log(`RECOGNIZING: Text=${e.result.text}`);
91 | msgerInput.value = lastRecordmsg + e.result.text;
92 | textAreaHeightAdjut();
93 | };
94 | speechRecognizer.recognized = (s, e) => {
95 | enableRecord = true;
96 | if (e.result.reason == SpeechSDK.ResultReason.RecognizedSpeech) {
97 | textAreaHeightAdjut();
98 | msgerInput.value = lastRecordmsg + e.result.text;
99 | lastRecordmsg = msgerInput.value
100 | console.log(`RECOGNIZED: Text=${e.result.text}`);
101 | }
102 | else if (e.result.reason == SpeechSDK.ResultReason.NoMatch) {
103 | console.log("NOMATCH: Speech could not be recognized.");
104 | }
105 | };
106 | speechRecognizer.canceled = (s, e) => {
107 | enableRecord = false;
108 | console.log(`CANCELED: Reason=${e.reason}`);
109 | if (e.reason == SpeechSDK.CancellationReason.Error) {
110 | console.log(`"CANCELED: ErrorCode=${e.errorCode}`);
111 | console.log(`"CANCELED: ErrorDetails=${e.errorDetails}`);
112 | console.log("CANCELED: Did you set the speech resource key and region values?");
113 | }
114 | speechRecognizer.stopContinuousRecognitionAsync();
115 | };
116 | speechRecognizer.sessionStopped = (s, e) => {
117 | enableRecord = false;
118 | console.log("\n Session stopped event.");
119 | speechRecognizer.stopContinuousRecognitionAsync();
120 | };
121 | speechRecognizer.startContinuousRecognitionAsync();
122 | enableRecord = true;
123 | }
124 |
125 |
126 |
--------------------------------------------------------------------------------
/chatGPTEx/static/styles/ChatGPT_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/chatGPTEx/static/styles/ChatGPT_logo.png
--------------------------------------------------------------------------------
/chatGPTEx/static/styles/FiraCode-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/chatGPTEx/static/styles/FiraCode-Regular.ttf
--------------------------------------------------------------------------------
/chatGPTEx/static/styles/check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/chatGPTEx/static/styles/check.png
--------------------------------------------------------------------------------
/chatGPTEx/static/styles/person.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/chatGPTEx/static/styles/person.jpg
--------------------------------------------------------------------------------
/chatGPTEx/static/styles/prism.css:
--------------------------------------------------------------------------------
1 | /* PrismJS 1.29.0
2 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+csharp+cpp+css-extras+diff+git+http+java+json+latex+markdown+markup-templating+matlab+nginx+php+python+regex+rust+typescript+vim&plugins=line-highlight+line-numbers+show-invisibles+autolinker+file-highlight+show-language+jsonp-highlight+highlight-keywords+remove-initial-line-feed+inline-color+previewers+autoloader+command-line+unescaped-markup+normalize-whitespace+data-uri-highlight+toolbar+copy-to-clipboard+download-button+match-braces+diff-highlight+filter-highlight-all */
3 | code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
4 | pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)}
5 | pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
6 | .token.cr,.token.lf,.token.space,.token.tab:not(:empty){position:relative}.token.cr:before,.token.lf:before,.token.space:before,.token.tab:not(:empty):before{color:grey;opacity:.6;position:absolute}.token.tab:not(:empty):before{content:'\21E5'}.token.cr:before{content:'\240D'}.token.crlf:before{content:'\240D\240A'}.token.lf:before{content:'\240A'}.token.space:before{content:'\00B7'}
7 | .token a{color:inherit}
8 | div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none}
9 | span.inline-color-wrapper{background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyIDIiPjxwYXRoIGZpbGw9ImdyYXkiIGQ9Ik0wIDBoMnYySDB6Ii8+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMXYxSDB6TTEgMWgxdjFIMXoiLz48L3N2Zz4=);background-position:center;background-size:110%;display:inline-block;height:1.333ch;width:1.333ch;margin:0 .333ch;box-sizing:border-box;border:1px solid #fff;outline:1px solid rgba(0,0,0,.5);overflow:hidden}span.inline-color{display:block;height:120%;width:120%}
10 | .prism-previewer,.prism-previewer:after,.prism-previewer:before{position:absolute;pointer-events:none}.prism-previewer,.prism-previewer:after{left:50%}.prism-previewer{margin-top:-48px;width:32px;height:32px;margin-left:-16px;z-index:10;opacity:0;-webkit-transition:opacity .25s;-o-transition:opacity .25s;transition:opacity .25s}.prism-previewer.flipped{margin-top:0;margin-bottom:-48px}.prism-previewer:after,.prism-previewer:before{content:'';position:absolute;pointer-events:none}.prism-previewer:before{top:-5px;right:-5px;left:-5px;bottom:-5px;border-radius:10px;border:5px solid #fff;box-shadow:0 0 3px rgba(0,0,0,.5) inset,0 0 10px rgba(0,0,0,.75)}.prism-previewer:after{top:100%;width:0;height:0;margin:5px 0 0 -7px;border:7px solid transparent;border-color:rgba(255,0,0,0);border-top-color:#fff}.prism-previewer.flipped:after{top:auto;bottom:100%;margin-top:0;margin-bottom:5px;border-top-color:rgba(255,0,0,0);border-bottom-color:#fff}.prism-previewer.active{opacity:1}.prism-previewer-angle:before{border-radius:50%;background:#fff}.prism-previewer-angle:after{margin-top:4px}.prism-previewer-angle svg{width:32px;height:32px;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.prism-previewer-angle[data-negative] svg{-webkit-transform:scaleX(-1) rotate(-90deg);-moz-transform:scaleX(-1) rotate(-90deg);-ms-transform:scaleX(-1) rotate(-90deg);-o-transform:scaleX(-1) rotate(-90deg);transform:scaleX(-1) rotate(-90deg)}.prism-previewer-angle circle{fill:transparent;stroke:#2d3438;stroke-opacity:.9;stroke-width:32;stroke-dasharray:0,500}.prism-previewer-gradient{background-image:linear-gradient(45deg,#bbb 25%,transparent 25%,transparent 75%,#bbb 75%,#bbb),linear-gradient(45deg,#bbb 25%,#eee 25%,#eee 75%,#bbb 75%,#bbb);background-size:10px 10px;background-position:0 0,5px 5px;width:64px;margin-left:-32px}.prism-previewer-gradient:before{content:none}.prism-previewer-gradient div{position:absolute;top:-5px;left:-5px;right:-5px;bottom:-5px;border-radius:10px;border:5px solid #fff;box-shadow:0 0 3px rgba(0,0,0,.5) inset,0 0 10px rgba(0,0,0,.75)}.prism-previewer-color{background-image:linear-gradient(45deg,#bbb 25%,transparent 25%,transparent 75%,#bbb 75%,#bbb),linear-gradient(45deg,#bbb 25%,#eee 25%,#eee 75%,#bbb 75%,#bbb);background-size:10px 10px;background-position:0 0,5px 5px}.prism-previewer-color:before{background-color:inherit;background-clip:padding-box}.prism-previewer-easing{margin-top:-76px;margin-left:-30px;width:60px;height:60px;background:#333}.prism-previewer-easing.flipped{margin-bottom:-116px}.prism-previewer-easing svg{width:60px;height:60px}.prism-previewer-easing circle{fill:#2d3438;stroke:#fff}.prism-previewer-easing path{fill:none;stroke:#fff;stroke-linecap:round;stroke-width:4}.prism-previewer-easing line{stroke:#fff;stroke-opacity:.5;stroke-width:2}@-webkit-keyframes prism-previewer-time{0%{stroke-dasharray:0,500;stroke-dashoffset:0}50%{stroke-dasharray:100,500;stroke-dashoffset:0}100%{stroke-dasharray:0,500;stroke-dashoffset:-100}}@-o-keyframes prism-previewer-time{0%{stroke-dasharray:0,500;stroke-dashoffset:0}50%{stroke-dasharray:100,500;stroke-dashoffset:0}100%{stroke-dasharray:0,500;stroke-dashoffset:-100}}@-moz-keyframes prism-previewer-time{0%{stroke-dasharray:0,500;stroke-dashoffset:0}50%{stroke-dasharray:100,500;stroke-dashoffset:0}100%{stroke-dasharray:0,500;stroke-dashoffset:-100}}@keyframes prism-previewer-time{0%{stroke-dasharray:0,500;stroke-dashoffset:0}50%{stroke-dasharray:100,500;stroke-dashoffset:0}100%{stroke-dasharray:0,500;stroke-dashoffset:-100}}.prism-previewer-time:before{border-radius:50%;background:#fff}.prism-previewer-time:after{margin-top:4px}.prism-previewer-time svg{width:32px;height:32px;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.prism-previewer-time circle{fill:transparent;stroke:#2d3438;stroke-opacity:.9;stroke-width:32;stroke-dasharray:0,500;stroke-dashoffset:0;-webkit-animation:prism-previewer-time linear infinite 3s;-moz-animation:prism-previewer-time linear infinite 3s;-o-animation:prism-previewer-time linear infinite 3s;animation:prism-previewer-time linear infinite 3s}
11 | .command-line-prompt{border-right:1px solid #999;display:block;float:left;font-size:100%;letter-spacing:-1px;margin-right:1em;pointer-events:none;text-align:right;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.command-line-prompt>span:before{opacity:.7;content:' ';display:block;padding-right:.8em}.command-line-prompt>span[data-user]:before{content:"[" attr(data-user) "@" attr(data-host) "] $"}.command-line-prompt>span[data-user=root]:before{content:"[" attr(data-user) "@" attr(data-host) "] #"}.command-line-prompt>span[data-prompt]:before{content:attr(data-prompt)}.command-line-prompt>span[data-continuation-prompt]:before{content:attr(data-continuation-prompt)}.command-line span.token.output{opacity:.7}
12 | [class*=lang-] script[type='text/plain'],[class*=language-] script[type='text/plain'],script[type='text/plain'][class*=lang-],script[type='text/plain'][class*=language-]{display:block;font:100% Consolas,Monaco,monospace;white-space:pre;overflow:auto}
13 | .token.punctuation.brace-hover,.token.punctuation.brace-selected{outline:solid 1px}.rainbow-braces .token.punctuation.brace-level-1,.rainbow-braces .token.punctuation.brace-level-5,.rainbow-braces .token.punctuation.brace-level-9{color:#e50;opacity:1}.rainbow-braces .token.punctuation.brace-level-10,.rainbow-braces .token.punctuation.brace-level-2,.rainbow-braces .token.punctuation.brace-level-6{color:#0b3;opacity:1}.rainbow-braces .token.punctuation.brace-level-11,.rainbow-braces .token.punctuation.brace-level-3,.rainbow-braces .token.punctuation.brace-level-7{color:#26f;opacity:1}.rainbow-braces .token.punctuation.brace-level-12,.rainbow-braces .token.punctuation.brace-level-4,.rainbow-braces .token.punctuation.brace-level-8{color:#e0e;opacity:1}
14 | pre.diff-highlight>code .token.deleted:not(.prefix),pre>code.diff-highlight .token.deleted:not(.prefix){background-color:rgba(255,0,0,.1);color:inherit;display:block}pre.diff-highlight>code .token.inserted:not(.prefix),pre>code.diff-highlight .token.inserted:not(.prefix){background-color:rgba(0,255,128,.1);color:inherit;display:block}
15 |
--------------------------------------------------------------------------------
/chatGPTEx/static/styles/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --body-bg: linear-gradient(135deg,#ffe4e1 0%,#c9e4ff 100%);
3 | --msger-bg: #fff;
4 | --border: 2px solid #ddd;
5 | --right-msg-bg: #5e86c1;
6 | --left-msg-bg: #F5F5F5;
7 | --right-msg-text: #fff;
8 | --code-bg: #faebd7;
9 | }
10 |
11 | html {
12 | box-sizing: border-box;
13 | }
14 | @font-face {
15 | font-family: 'Fira Code';
16 | src: url('/static/styles/FiraCode-Regular.ttf') format('truetype');
17 | font-weight: normal;
18 | font-style: normal;
19 | font-display: swap;
20 | }
21 | *,
22 | *:before,
23 | *:after {
24 | margin: 0;
25 | padding: 0;
26 | box-sizing: inherit;
27 | }
28 |
29 | body {
30 | transform: scale(0.985);
31 | height: 100vh;
32 | background-image: var(--body-bg);
33 | font-family: monospace;
34 | }
35 |
36 | .container {
37 | display: flex;
38 | justify-content: center;
39 | align-items: center;
40 | flex-direction: row;
41 | height: 100vh;
42 | }
43 |
44 | .chat-page{
45 | position: relative;
46 | overflow-x: hidden;
47 | overflow-y: auto;
48 | flex: 1;
49 | flex-flow: column wrap;
50 | justify-content: space-between;
51 | width: 100%;
52 | max-width: 80%;
53 | height: calc(99%);
54 | background: transparent;
55 | border: 0px;
56 | margin: 0px;
57 | }
58 | .msger {
59 | position: relative;
60 | overflow-x: hidden;
61 | overflow-y: auto;
62 | flex: 1;
63 | flex-flow: column wrap;
64 | justify-content: space-between;
65 | width: 100%;
66 | height: 100%;
67 | border: var(--border);
68 | border-radius: 14px;
69 | background: var(--msger-bg);
70 | box-shadow: 0 15px 15px -5px rgba(0, 0, 0, 0.2);
71 | margin-bottom: 0px;
72 | margin-top: 0px;
73 | }
74 |
75 | .msger-header {
76 | /* display: flex; */
77 | font-size: medium;
78 | justify-content: space-·;
79 | text-align: center;
80 | border-bottom: var(--border);
81 | background: #eee;
82 | color: #000;
83 | }
84 |
85 | .msger-chat {
86 | flex: 1;
87 | overflow-y: auto;
88 | padding: 10px;
89 | margin-bottom: 70px;
90 | }
91 | .msger-chat::-webkit-scrollbar {
92 | width: 6px;
93 | }
94 | .msger-chat::-webkit-scrollbar-track {
95 | background: #ddd;
96 | }
97 | .msger-chat::-webkit-scrollbar-thumb {
98 | background: #bdbdbd;
99 | }
100 | .msg {
101 | display: flex;
102 | align-items: flex-end;
103 | margin-bottom: 15px;
104 | }
105 | .msg-text{
106 | font-size: 1.2em;
107 | white-space: pre-wrap;
108 | }
109 | .msg-img {
110 | width: 50px;
111 | height: 50px;
112 | margin-right: 10px;
113 | background: #ddd;
114 | background-repeat: no-repeat;
115 | background-position: center;
116 | background-size: cover;
117 | border-radius: 50%;
118 | }
119 | .msg-bubble {
120 | font-family: "Fira Code"!important;
121 | max-width: 75%;
122 | padding: 20px;
123 | border-radius: 15px;
124 | background: var(--left-msg-bg);
125 | animation: pulse 1s;
126 | animation: fade-in 0.5s ease-in-out forwards;
127 | }
128 | @keyframes fade-in {
129 | from {
130 | opacity: 0;
131 | }
132 | to {
133 | opacity: 1;
134 | }
135 | }
136 | .msg-api{
137 | padding-bottom: 10px;
138 | padding-left: 63px;
139 | max-width: 80%;
140 | background: transparent;
141 | animation-name: fade-in;
142 | animation-duration: 1s;
143 | }
144 | @keyframes fade-in {
145 | from {
146 | opacity: 0;
147 | }
148 | to {
149 | opacity: 1;
150 | }
151 | }
152 | .check-img{
153 | width: 20px;
154 | height: 20px;
155 | padding-top: 4px;
156 | margin-right: 10px;
157 | }
158 | .msg-info {
159 | display: flex;
160 | justify-content: space-between;
161 | align-items: center;
162 | margin-bottom: 10px;
163 | }
164 | .msg-info-name {
165 | margin-right: 10px;
166 | font-weight: bold;
167 | }
168 | .msg-info-time {
169 | font-size: 0.85em;
170 | }
171 | .msg-info-mode {
172 | font-size: 0.85em;
173 | font-style: italic;
174 | padding: 10px;
175 | }
176 |
177 | .left-msg .msg-bubble {
178 | border-bottom-left-radius: 0;
179 | }
180 |
181 | .right-msg {
182 | flex-direction: row-reverse;
183 | }
184 | .right-msg .msg-bubble {
185 | background: var(--right-msg-bg);
186 | color: var(--right-msg-text);
187 | border-bottom-right-radius: 0;
188 | }
189 | .left-msg .msg-bubble{
190 | padding-left: 50px;
191 | }
192 | .right-msg .msg-img {
193 | margin: 0 0 0 10px;
194 | }
195 |
196 | .msger-inputarea {
197 | display: flex;
198 | background-color: transparent;
199 | }
200 | .msger-input :focus:hover{
201 | border-color: pink;
202 | }
203 | .msger-inputarea * {
204 | background-color: transparent;
205 | border: none;
206 | font-size: 1.4em;
207 | color: var(--right-msg-text);
208 | font-family: monospace;
209 | }
210 | .msger-input {
211 | flex: 1;
212 | }
213 | .msger-send-btn{
214 | padding: 10px;
215 | width: auto;
216 | border-radius: 5px;
217 | margin-left: 10px;
218 | margin-right: 10px;
219 | background: transparent;
220 | color: #fff;
221 | cursor: pointer;
222 | transition: background 0.23s;
223 | }
224 | .msger-send-record{
225 | padding: 10px;
226 | width: 30px;
227 | border-radius: 5px;
228 | /* margin-left: 10px;
229 | margin-right: 10px; */
230 | background: transparent;
231 | outline: none!important;
232 | border: 0px!important;
233 | color: #fff;
234 | cursor: pointer;
235 | display: flex;
236 | }
237 | .msger-send-btn:hover ,.msger-send-record:hover {
238 | background: #f5cae6;
239 | }
240 |
241 | .msger-chat {
242 | background-color: #fcfcfe;
243 | }
244 | select#mode {
245 | font-family: monospace;
246 | color: white;
247 | background-color: transparent;
248 | align-content: center;
249 | border: none;
250 | padding: 5px;
251 | width: 2em;
252 | font-optical-sizing: auto;
253 | font-size: 1em;
254 | border-radius: 5px;
255 | -webkit-appearance: none;
256 | -moz-appearance: none;
257 | appearance: none;
258 | background-repeat: no-repeat;
259 | height: 2.5em;
260 | }
261 |
262 | select#prompt:hover, select#prompt:focus {
263 | background-color: #f5cae6;
264 | outline: none;
265 | }
266 | select#prompt {
267 | font-family: monospace;
268 | color: white;
269 | background-color: transparent;
270 | align-content: center;
271 | border: none;
272 | padding: 5px;
273 | width: 2em;
274 | font-optical-sizing: auto;
275 | font-size: 1em;
276 | border-radius: 5px;
277 | -webkit-appearance: none;
278 | -moz-appearance: none;
279 | appearance: none;
280 | background-repeat: no-repeat;
281 | height: 2.5em;
282 | }
283 |
284 | select#prompt:hover, select#prompt:focus {
285 | background-color: #f5cae6;
286 | outline: none;
287 | }
288 | .row {
289 | opacity: 95%;
290 | border: none;
291 | border-radius: 16px;
292 | padding: 5px;
293 | background: var(--right-msg-bg);
294 | box-shadow: 0 15px 15px -5px rgba(0, 0, 0, 0.2);
295 | position: absolute;
296 | bottom: 2%;
297 | left: 50%;
298 | transform: translateX(-50%);
299 | right: auto;
300 | display: flex;
301 | flex-wrap: wrap;
302 | align-items: center;
303 | width: 70%;
304 | height: auto;
305 | }
306 | .col {
307 | flex: 1;
308 | }
309 | .col:first-child {
310 | flex: 0 0 5%;
311 | /* align-self: flex-start; */
312 | }
313 | .save-hisotry{
314 | border: none;
315 | background-color: #f2f2f2;
316 | padding: 5px;
317 | font-size: 1em;
318 | border-radius: 5px;
319 | -webkit-appearance: none;
320 | -moz-appearance: none;
321 | appearance: none;
322 | background-repeat: no-repeat;
323 | height: 3.7em;
324 | }
325 | .save-hisotry:hover{
326 | background-color: #ddd;
327 | outline: none;
328 | }
329 | pre {
330 | font-family: monospace;
331 | font-size: 0.9em!important;
332 | line-height: 1em;
333 | margin: 0;
334 | overflow: auto;
335 | padding: 1em;
336 | border-radius: 10px;
337 | }
338 |
339 | /* Add line numbers to the highlighted code */
340 | .line-numbers-mode .line-numbers-rows {
341 | border-right: 1px solid #ccc;
342 | }
343 |
344 | /* Style the line numbers */
345 | .line-numbers-mode .line-numbers-rows span {
346 | display: inline-block;
347 | padding-right: 0.5em;
348 | text-align: right;
349 | user-select: none;
350 | opacity: 0.5;
351 | }
352 |
353 | /* Style for the left sidebar */
354 | .sidebar {
355 | background: #fff5ff !important;
356 | padding: 20px;
357 | width: 330px;
358 | height: 100%;
359 | overflow-x: hidden;
360 | overflow-y: auto;
361 | margin: 10px 5px;
362 | height: calc(99%);
363 | border: var(--border);
364 | border-radius: 12px;
365 | background: var(--msger-bg);
366 | box-shadow: 0 15px 15px -5px rgba(0, 0, 0, 0.2);
367 | }
368 |
369 | .sidebar h2 {
370 | margin-top: 0;
371 | font-size: 24px;
372 | color: #333;
373 | text-transform: uppercase;
374 | font-weight: bold;
375 | }
376 |
377 | .sidebar ul {
378 | list-style: none;
379 | padding: 0;
380 | margin: 0;
381 | }
382 |
383 | .sidebar li {
384 | font-size: 18px;
385 | color: #000;
386 | border-bottom: 1px solid #ccc;
387 | }
388 |
389 | .sidebar li:last-child {
390 | border-bottom: none;
391 | }
392 |
393 | .sidebar li:hover {
394 | background-color: #ddd;
395 | }
396 |
397 | .new-chat {
398 | font-family: monospace;
399 | text-align: center;
400 | width: 100%;
401 | display: flex;
402 | padding: 0.75rem;
403 | padding-left: 13%;
404 | align-items: center;
405 | border-radius: 0.375rem;
406 | background-color: #fff;
407 | color: #fff;
408 | cursor: pointer;
409 | font-size: 2rem;
410 | margin-bottom: 0.5rem;
411 | flex-shrink: 0;
412 | border-width: 1px;
413 | border-color: rgba(255, 255, 255, 0.2);
414 | transition-property: background-color, border-color, color, fill, stroke;
415 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
416 | transition-duration: 200ms;
417 | background: linear-gradient( to top right,pink 0%, #8bb9dd 50%);
418 | box-shadow: 1px 1px 10px #87ceeb;
419 | }
420 |
421 | .new-chat:hover {
422 | background: linear-gradient(to top right, pink 0%, #8bb9dd 80%);
423 | animation: mask 0.3s forwards;
424 | }
425 |
426 | @keyframes mask {
427 | 0% {
428 | background: linear-gradient(to top right, pink 0%, #8bb9dd 50%);
429 | }
430 | 100% {
431 | background: linear-gradient(to top right, pink 0%, #8bb9dd 70%);
432 | }
433 | }
434 | .chat-list a {
435 | display: flex;
436 | padding: 1rem;
437 | align-items: center;
438 | gap: 0.75rem;
439 | position: relative;
440 | border-radius: 0.375rem;
441 | cursor: pointer;
442 | word-break: break-all;
443 | padding-right: 3.5rem;
444 | background: linear-gradient(135deg,#ffe4e1 0%,#fff5ff 100%);
445 | }
446 |
447 | .chat-list a:hover {
448 | background-color: #d8bfd8;
449 | }
450 |
451 | .chat-list a svg {
452 | stroke: currentColor;
453 | fill: none;
454 | stroke-width: 2;
455 | height: 1em;
456 | width: 1em;
457 | }
458 |
459 | .chat-list a .flex-1 {
460 | max-height: 5rem;
461 | overflow: hidden;
462 | position: relative;
463 | }
464 |
465 | .chat-list a .absolute {
466 | position: absolute;
467 | display: flex;
468 | right: 1rem;
469 | z-index: 10;
470 | visibility: visible;
471 | }
472 |
473 | .chat-list a .absolute button {
474 | padding: 0.2rem;
475 | cursor: pointer;
476 | }
477 |
478 | .chat-list a .absolute button:hover {
479 | color: #F9FAFB;
480 | }
481 |
482 | .chat-list a .absolute button:hover svg polyline,
483 | .chat-list a .absolute button:hover svg path,
484 | .chat-list a .absolute button:hover svg line {
485 | stroke: #F9FAFB;
486 | }
487 | .chat-list{
488 | padding-top: 5px;
489 | padding-bottom: 8px;
490 | }
491 | .edit-button{
492 | background: transparent;
493 | border: none;
494 | }
495 | .delete-button{
496 | background: transparent;
497 | border: none;
498 | }
499 | body > div.container > div > ul > li:nth-child(1) > a{
500 | background-color: #f0dff2;
501 | }
502 | span.token.operator{
503 | background: transparent!important;
504 | }
505 | span.token.space{
506 | visibility: hidden!important;
507 | }
508 | span.token.lf{
509 | visibility: hidden!important;
510 | }
511 | textarea{
512 | padding-left: 2px;
513 | padding-top: 10px;
514 | padding-bottom: 10px;
515 | vertical-align:middle;
516 | outline: none;
517 | -webkit-text-size-adjust: 100%;
518 | tab-size: 4;
519 | color: inherit;
520 | font-weight: inherit;
521 | appearance: none;
522 | line-height: 1.5rem;
523 | margin: 0;
524 | width: 100%;
525 | resize: none;
526 | border-width: 0;
527 | background-color: transparent;
528 | overflow-y: scroll;
529 | }
530 | ::-webkit-scrollbar {
531 | height: 1rem;
532 | width: .5rem
533 | }
534 |
535 | ::-webkit-scrollbar:horizontal {
536 | height: .5rem;
537 | width: 1rem
538 | }
539 |
540 | ::-webkit-scrollbar-track {
541 | background-color: transparent;
542 | border-radius: 9999px
543 | }
544 |
545 | ::-webkit-scrollbar-thumb {
546 | --tw-border-opacity: 1;
547 | background-color: rgba(217,217,227,.8);
548 | border-color: rgba(255,255,255,var(--tw-border-opacity));
549 | border-radius: 9999px;
550 | border-width: 1px
551 | }
552 |
553 | ::-webkit-scrollbar-thumb:hover {
554 | --tw-bg-opacity: 1;
555 | background-color: rgba(236,236,241,var(--tw-bg-opacity))
556 | }
557 |
558 | .dark ::-webkit-scrollbar-thumb {
559 | --tw-bg-opacity: 1;
560 | background-color: rgba(86,88,105,var(--tw-bg-opacity))
561 | }
562 |
563 | .dark ::-webkit-scrollbar-thumb:hover {
564 | --tw-bg-opacity: 1;
565 | background-color: rgba(172,172,190,var(--tw-bg-opacity))
566 | }
567 | select#prompt{
568 | display: none;
569 | }
570 |
571 |
--------------------------------------------------------------------------------
/img/API.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/API.jpg
--------------------------------------------------------------------------------
/img/APIAnimation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/APIAnimation.png
--------------------------------------------------------------------------------
/img/WebPageBeautification.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/WebPageBeautification.jpg
--------------------------------------------------------------------------------
/img/chatGPTChromeEnhance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/chatGPTChromeEnhance.png
--------------------------------------------------------------------------------
/img/date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/date.jpg
--------------------------------------------------------------------------------
/img/math.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/math.jpg
--------------------------------------------------------------------------------
/img/mathjax.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/mathjax.jpg
--------------------------------------------------------------------------------
/img/mode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/mode.jpg
--------------------------------------------------------------------------------
/img/newPage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/newPage.jpg
--------------------------------------------------------------------------------
/img/promptCompletion.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/promptCompletion.gif
--------------------------------------------------------------------------------
/img/stream.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/stream.gif
--------------------------------------------------------------------------------
/img/web.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/web.jpg
--------------------------------------------------------------------------------
/img/webHistory.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/webHistory.jpg
--------------------------------------------------------------------------------
/img/zhihuq0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/zhihuq0.jpg
--------------------------------------------------------------------------------
/img/zhihuq1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/zhihuq1.jpg
--------------------------------------------------------------------------------
/img/zhihuq2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/zhihuq2.jpg
--------------------------------------------------------------------------------
/img/zhihuq3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/circlestarzero/EX-chatGPT/ddfe9c4f410d25d1880b61d149d0ffcbf53371d0/img/zhihuq3.jpg
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | azure_storage==0.37.0
2 | beautifulsoup4==4.11.2
3 | fastapi==0.94.0
4 | Flask==2.0.2
5 | fuzzywuzzy==0.18.0
6 | jieba==0.42.1
7 | requests==2.26.0
8 | tiktoken==0.3.1
9 | uvicorn==0.21.0
10 | xpinyin==0.7.6
11 |
--------------------------------------------------------------------------------