|
| 1 | +# Developing Bots for ChatALL |
| 2 | + |
| 3 | +## Quick Start |
| 4 | +- [Basic bot setup](https://github.com/ai-shifu/ChatALL/blob/main/CONTRIBUTION.md#adding-a-new-ai-bot) |
| 5 | + |
| 6 | +## Advanced Topics |
| 7 | + |
| 8 | +### UI order configuration |
| 9 | + |
| 10 | +In the `src/bots/index.js` file, you can find the `all` array like |
| 11 | + |
| 12 | +```javascript |
| 13 | +// Add to the main bot list |
| 14 | +const all = [ |
| 15 | + ChatGPT35Bot.getInstance(), |
| 16 | + ChatGPT4Bot.getInstance(), |
| 17 | + OpenAIAPI35Bot.getInstance(), |
| 18 | + OpenAIAPI4Bot.getInstance(), |
| 19 | + BingChatCreativeBot.getInstance(), |
| 20 | + BingChatBalancedBot.getInstance(), |
| 21 | + BingChatPreciseBot.getInstance(), |
| 22 | + WenxinQianfanBot.getInstance(), |
| 23 | + SparkBot.getInstance(), |
| 24 | + MOSSBot.getInstance(), |
| 25 | + BardBot.getInstance(), |
| 26 | + ...existing_bots... |
| 27 | +]; |
| 28 | +``` |
| 29 | + |
| 30 | +You can change the order of the bots in this array to control their display order in the UI. |
| 31 | +For example, if you want to move the `BardBot` between `MOSSBot` and `WenxinQianfanBot`, you can modify the array like this: |
| 32 | + |
| 33 | +```javascript |
| 34 | +const all = [ |
| 35 | + ChatGPT35Bot.getInstance(), |
| 36 | + ChatGPT4Bot.getInstance(), |
| 37 | + OpenAIAPI35Bot.getInstance(), |
| 38 | + OpenAIAPI4Bot.getInstance(), |
| 39 | + BingChatCreativeBot.getInstance(), |
| 40 | + BingChatBalancedBot.getInstance(), |
| 41 | + BingChatPreciseBot.getInstance(), |
| 42 | + MOSSBot.getInstance(), |
| 43 | + BardBot.getInstance(), // Moved BardBot here |
| 44 | + WenxinQianfanBot.getInstance(), |
| 45 | + SparkBot.getInstance(), |
| 46 | + ...existing_bots... |
| 47 | +]; |
| 48 | +``` |
| 49 | + |
| 50 | +### Setting component implementation |
| 51 | + |
| 52 | +To fulfill other core functions, you need login functionality, API key configuration, etc. If your bot does not require login or you don't mind putting the key directly in the code (strongly not recommended), you can skip this section. |
| 53 | + |
| 54 | +#### Create a settings component |
| 55 | + |
| 56 | + |
| 57 | +In the `src/components/BotSettings/` directory, create a new file named `KnowNothingBotSettings.vue`. |
| 58 | +\ |
| 59 | +You can use existing settings components as templates: |
| 60 | + |
| 61 | +1. If you only need login functionality, just copy `BardBotSettings.vue` and change import Bot from `@/bots/BardBot;` to import Bot from `@/bots/KnowNothingBot;`. |
| 62 | + - (Note: Some websites have implemented security measures to prevent ChatALL and similar clients from accessing them. If you encounter such situations, you will need to do a lot of hack work.) |
| 63 | +2. If you only need to configure the API key, copy `WenxinQianfanBotSettings.vue` and modify it, but this will require more work. |
| 64 | +3. For complex settings with multiple configurations, you will need to do even more work. |
| 65 | + |
| 66 | +#### Add settings field |
| 67 | + |
| 68 | +ChatALL's settings UI is built using [Vuetify 3](https://vuetifyjs.com/). |
| 69 | +Refer to the [Vuetify 3 official documentation](https://vuetifyjs.com/en/introduction/why-vuetify/) to see and test the rich components it supports. |
| 70 | +\ |
| 71 | +By referring to the existing code, you can basically get everything workin. No need for further explanation here. |
| 72 | + |
| 73 | +ChatALL's settings are stored in local storage using the [`vuex-persist`](https://github.com/championswimmer/vuex-persist). It's very handy, though the documentation is not readable enough. |
| 74 | +Here is a brief introduction on how to use it: |
| 75 | + |
| 76 | +First, in `src/store/index.js`, add the following code: |
| 77 | + |
| 78 | +```JavaScript |
| 79 | +export default createStore({ |
| 80 | + state: { |
| 81 | + ... |
| 82 | + knowNothing: { |
| 83 | + setting1: "", |
| 84 | + setting2: "", |
| 85 | + }, |
| 86 | + ... |
| 87 | + }, |
| 88 | + mutations: { |
| 89 | + ... |
| 90 | + setKnowNothing(state, { setting1, setting2 }) { |
| 91 | + state.knowNothing = { setting1, setting2 }; |
| 92 | + }, |
| 93 | + ... |
| 94 | + }, |
| 95 | + ... |
| 96 | +}); |
| 97 | +``` |
| 98 | + |
| 99 | +`setting1`, `setting2`, and sub-objects can be added, deleted, or modified as you like. Just make sure the top-level object is `knowNothing`, even if it only has one configuration. |
| 100 | + |
| 101 | +Then, in `KnowNothingBotSettings.vue`, add the following code: |
| 102 | + |
| 103 | +```JavaScript |
| 104 | +export default { |
| 105 | + ... |
| 106 | + methods: { |
| 107 | + ...mapMutations(["setKnowNothing"]), |
| 108 | + setSetting1(value){ |
| 109 | + this.setKnowNothing({ |
| 110 | + ...this.knowNothing, |
| 111 | + setting1: value |
| 112 | + }) |
| 113 | + }, |
| 114 | + setSetting2(value){ |
| 115 | + this.setKnowNothing({ |
| 116 | + ...this.knowNothing, |
| 117 | + setting2: value |
| 118 | + }) |
| 119 | + }, |
| 120 | + }, |
| 121 | + computed: { |
| 122 | + ...mapState(["knowNothing"]) |
| 123 | + } |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +Finally, bind the `v-model` of the Vuetify component to the corresponding `knowNothing.xxx`, and point the action to the corresponding `setXxx()` function. For example: |
| 128 | + |
| 129 | +```HTML |
| 130 | +<v-text-field |
| 131 | + v-model="knowNothing.setting1" |
| 132 | + @change="setSetting1($event.target.value)" |
| 133 | +></v-text-field> |
| 134 | +``` |
| 135 | + |
| 136 | +Done! Run the program and open DevTools to check if the values are correctly stored in the "Application" tab. |
| 137 | + |
| 138 | +In `KnowNothingBot.js`, using the parameters you set is very simple: |
| 139 | + |
| 140 | +```JavaScript |
| 141 | +... |
| 142 | +import store from "@/store"; |
| 143 | +... |
| 144 | +store.state.knowNothing.setting1 |
| 145 | +store.state.knowNothing.setting2 |
| 146 | +const { setting1, setting2 } = store.state.knowNothing; |
| 147 | +... |
| 148 | +``` |
| 149 | + |
| 150 | +```JavaScript |
| 151 | +export default createStore({ |
| 152 | + state: { |
| 153 | + ... |
| 154 | + knowNothing: { |
| 155 | + setting1: "", |
| 156 | + setting2: "", |
| 157 | + }, |
| 158 | + ... |
| 159 | + }, |
| 160 | + mutations: { |
| 161 | + ... |
| 162 | + setKnowNothing(state, { setting1, setting2 }) { |
| 163 | + state.knowNothing = { setting1, setting2 }; |
| 164 | + }, |
| 165 | + ... |
| 166 | + }, |
| 167 | + ... |
| 168 | +}); |
| 169 | +``` |
| 170 | + |
| 171 | +`setting1`, `setting2`, and sub-objects can be added, deleted, or modified as you like. Just make sure the top-level object is `knowNothing`, even if it only has one configuration. |
| 172 | + |
| 173 | +Then, in `KnowNothingBotSettings.vue`, add the following code: |
| 174 | + |
| 175 | +```JavaScript |
| 176 | +export default { |
| 177 | + ... |
| 178 | + methods: { |
| 179 | + ...mapMutations(["setKnowNothing"]), |
| 180 | + setSetting1(value){ |
| 181 | + this.setKnowNothing({ |
| 182 | + ...this.knowNothing, |
| 183 | + setting1: value |
| 184 | + }) |
| 185 | + }, |
| 186 | + setSetting2(value){ |
| 187 | + this.setKnowNothing({ |
| 188 | + ...this.knowNothing, |
| 189 | + setting2: value |
| 190 | + }) |
| 191 | + }, |
| 192 | + }, |
| 193 | + computed: { |
| 194 | + ...mapState(["knowNothing"]) |
| 195 | + } |
| 196 | +} |
| 197 | +``` |
| 198 | + |
| 199 | +Finally, bind the `v-model` of the Vuetify component to the corresponding `knowNothing.xxx`, and point the action to the corresponding `setXxx()` function. For example: |
| 200 | + |
| 201 | +```HTML |
| 202 | +<v-text-field |
| 203 | + v-model="knowNothing.setting1" |
| 204 | + @change="setSetting1($event.target.value)" |
| 205 | +></v-text-field> |
| 206 | +``` |
| 207 | + |
| 208 | +Done! Run the program and open DevTools to check if the values are correctly stored in the "Application" tab. |
| 209 | + |
| 210 | +In `KnowNothingBot.js`, using the parameters you set is very simple: |
| 211 | + |
| 212 | +```JavaScript |
| 213 | +... |
| 214 | +import store from "@/store"; |
| 215 | +... |
| 216 | +store.state.knowNothing.setting1 |
| 217 | +store.state.knowNothing.setting2 |
| 218 | +const { setting1, setting2 } = store.state.knowNothing; |
| 219 | +... |
| 220 | +``` |
| 221 | + |
| 222 | +### Detailed _sendPrompt() patterns |
| 223 | + |
| 224 | +This is the core function, which sends and receives messages. |
| 225 | + |
| 226 | +Reference existing bots depending on your interface type: |
| 227 | + |
| 228 | +1. For standard HTTP APIs: see `BardBot.js` |
| 229 | +2. For SSE-based APIs: see `ChatGPTBot.js` |
| 230 | +3. For WebSocket APIs: see `BingChatBot.js` |
| 231 | + |
| 232 | +How you send and parse messages depends on the specific chatbot. Once you receive a response or hit an error, do the following: |
| 233 | + |
| 234 | +1. When receiving partial text, call `onUpdateResponse(callbackParam, {});`. |
| 235 | +2. If the response only contains new incremental text, and you need to assemble all the text yourself, then call `onUpdateResponse(callbackParam, {content: text, done: false)};`. |
| 236 | +3. After receiving all the text, call `onUpdateResponse(callbackParam, {content: text, done: true)};` to update all the data. If the text has already been `onUpdateResponse` before, you can just call `onUpdateResponse(callbackParam, {done: true)};`. |
| 237 | +4. When ending normally, call `resolve()`. |
| 238 | +5. If an error occurs, call `reject(error)`. The `error` can be an exception or an error message string. ChatALL will automatically handle it and display it to the user. |
| 239 | + |
| 240 | +### checkAvailability() implementation |
| 241 | + |
| 242 | +ChatALL calls the `checkAvailability()` function to check if the bot is available when it starts for the first time, refreshes the page (Command+R or Ctrl+R), and completes the settings. |
| 243 | + |
| 244 | +In general, it performs these checks: |
| 245 | + |
| 246 | +1. Is the login valid? |
| 247 | +2. Is the API key configured properly? |
| 248 | +3. Are all the other necessary conditions met? |
| 249 | + |
| 250 | +If everything is good, it should execute: |
| 251 | + |
| 252 | +```JavaScript |
| 253 | +this.constructor._isAvailable = true; |
| 254 | +``` |
| 255 | + |
| 256 | +Otherwise, it should execute: |
| 257 | + |
| 258 | +```JavaScript |
| 259 | +this.constructor._isAvailable = false; |
| 260 | +``` |
| 261 | + |
| 262 | +Finally, always do: |
| 263 | + |
| 264 | +```JavaScript |
| 265 | +return this.isAvailable(); |
| 266 | +``` |
| 267 | + |
| 268 | +### Custom bot icons |
| 269 | + |
| 270 | +Place the icon file in `src/assets/bots/knownothing-logo.png`, and modify `KnowNothingBot.js`: |
| 271 | + |
| 272 | +```JavaScript |
| 273 | +static _logoFilename = "knownothing-logo.png"; |
| 274 | +``` |
| 275 | + |
| 276 | +### Multi-language support in JS/HTML |
| 277 | + |
| 278 | +Language files are located in `src/i18n/locales/`, named with language codes and in .json format. |
| 279 | +You need to add at least the following for your bot: |
| 280 | + |
| 281 | +`en.json` |
| 282 | +```json |
| 283 | + "knowNothing": { |
| 284 | + "name": "Know Nothing" |
| 285 | + }, |
| 286 | +``` |
| 287 | + |
| 288 | +`zh.json` |
| 289 | +```json |
| 290 | + "knowNothing": { |
| 291 | + "name": "啥都不懂" |
| 292 | + }, |
| 293 | +``` |
| 294 | + |
| 295 | +Plus any other strings your bot need. |
| 296 | + |
| 297 | +In JavaScript, you can use the following code to call the multi-language support: |
| 298 | + |
| 299 | +```JavaScript |
| 300 | +import i18n from "@/i18n"; |
| 301 | +... |
| 302 | +i18n.global.t("knowNothing.stringName") |
| 303 | +... |
| 304 | +``` |
| 305 | + |
| 306 | +In HTML, you can use the following code: |
| 307 | + |
| 308 | +```HTML |
| 309 | +{{ $t("knowNothing.stringName") }} |
| 310 | +``` |
0 commit comments