File size: 9,947 Bytes
bc2c61b
c009dd5
 
 
 
c517271
3ab87cd
bc2c61b
c009dd5
 
 
 
bc2c61b
 
c009dd5
 
 
 
 
 
8628ca8
 
 
c009dd5
 
c517271
 
 
 
 
 
 
 
 
 
8628ca8
c517271
 
 
 
 
 
 
970785f
c517271
 
 
 
 
c009dd5
 
 
3239c32
c009dd5
 
 
 
 
bc3895d
c009dd5
 
 
 
bc3895d
c009dd5
 
 
2e92470
c009dd5
 
 
 
 
 
 
 
 
11e9138
c517271
c009dd5
1af2928
2e92470
c009dd5
 
 
 
 
 
 
c517271
 
 
c009dd5
c517271
c009dd5
 
 
 
 
 
 
1af2928
2e92470
c009dd5
3ab87cd
c009dd5
2e92470
c009dd5
c3b58ec
2e92470
 
 
 
 
 
c009dd5
2e92470
 
c009dd5
 
2e92470
c009dd5
 
 
 
 
 
 
c517271
c009dd5
 
 
19d7fb4
c009dd5
3239c32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f7c1f6c
 
3239c32
c009dd5
bc2c61b
19d7fb4
bc2c61b
 
f7c1f6c
1af2928
 
 
bc2c61b
 
 
 
 
 
 
f7c1f6c
bc2c61b
f7c1f6c
3ab87cd
bc2c61b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3132327
bc2c61b
 
 
 
 
 
 
 
 
 
 
 
 
 
c3b58ec
bc2c61b
c3b58ec
bc2c61b
19d7fb4
3132327
bc2c61b
 
 
19d7fb4
 
bc2c61b
19d7fb4
bc2c61b
 
 
 
 
19d7fb4
bc2c61b
 
 
 
 
 
 
19d7fb4
ae31beb
c009dd5
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import { AvailableModelsRequester } from "../networks/llm_requester.js";

class EndpointStorage {
    constructor() {
        this.init_database();
        this.load_local_endpoints();
        this.create_endpoint_and_api_key_items();
        this.fill_available_models_select();
    }
    init_database() {
        this.db = new Dexie("endpoints");
        this.db.version(1).stores({
            endpoints:
                "index, endpoint, api_key, need_protect, available_models",
        });
        this.db.endpoints.count((count) => {
            console.log(`${count} endpoints loaded.`);
        });
    }
    clear_database() {
        this.db.endpoints.count((count) => {
            console.log(`${count} endpoints would be cleared.`);
        });
        this.db.endpoints.clear();
    }
    async load_local_endpoints() {
        fetch("/endpoints")
            .then((response) => response.json())
            .then((data) => {
                if (data.error) {
                    console.error(data.error);
                    return;
                }
                let count = Object.keys(data).length;
                console.log(`${count} local endpoints loaded.`);
                // data is array of endpoint items, each item has 4 keys:
                // - `endpoint`, `api_key`, `api_type`, `need_protect`
                // add these to db.endpoints
                data.forEach((endpoint) => {
                    this.db.endpoints.put({
                        index: endpoint.endpoint,
                        endpoint: endpoint.endpoint,
                        api_key: endpoint.api_key,
                        need_protect: endpoint.need_protect || false,
                    });
                });
            });
    }
    generate_endpoint_and_api_key_item_html() {
        let endpoint_and_api_key_item_html = `
            <div class="row mt-2 no-gutters">
                <div class="col-auto">
                    <button class="btn px-0 remove-endpoint-button">
                        <i class="fa fa-circle-minus"></i>
                    </button>
                </div>
                <div class="col pl-0">
                    <input class="form-control endpoint-input" rows="1"
                        placeholder="Input Endpoint URL, don't add /v1"
                    ></input>
                </div>
                <div class="col pl-0">
                    <input class="form-control api-key-input" rows="1"
                        placeholder="Input API Key, then click ✔️"
                    ></input>
                </div>
                <div class="col-auto px-0">
                    <button class="btn submit-endpoint-button">
                        <i class="fa fa-check"></i>
                    </button>
                </div>
            </div>
        `;
        return endpoint_and_api_key_item_html;
    }
    add_endpoint_and_api_key_item() {
        let endpoint_and_api_key_items = $("#endpoint-and-api-key-items");
        let endpoint_and_api_key_item = $(
            this.generate_endpoint_and_api_key_item_html()
        );
        endpoint_and_api_key_items.append(endpoint_and_api_key_item);
        this.bind_endpoint_and_api_key_buttons(endpoint_and_api_key_item);
    }
    create_endpoint_and_api_key_items() {
        let endpoint_and_api_key_items = $("#endpoint-and-api-key-items");
        let endpoints = this.db.endpoints;
        endpoint_and_api_key_items.empty();

        endpoints.each((row) => {
            if (row.need_protect) {
                return;
            }
            let endpoint_and_api_key_item_html =
                this.generate_endpoint_and_api_key_item_html();
            let endpoint_and_api_key_item = $(endpoint_and_api_key_item_html);
            let endpoint_input =
                endpoint_and_api_key_item.find(".endpoint-input");
            endpoint_input.val(row.endpoint);
            let api_key_input =
                endpoint_and_api_key_item.find(".api-key-input");
            api_key_input.val(row.api_key);
            endpoint_and_api_key_items.append(endpoint_and_api_key_item);
            this.bind_endpoint_and_api_key_buttons(endpoint_and_api_key_item);
        });
        endpoint_and_api_key_items.hide();
    }
    bind_endpoint_and_api_key_buttons(endpoint_and_api_key_item) {
        let self = this;
        // console.log("endpoint_and_api_key_item:", endpoint_and_api_key_item);
        let endpoint_submit_button = endpoint_and_api_key_item.find(
            ".submit-endpoint-button"
        );
        endpoint_submit_button.click(function () {
            let endpoint_input =
                endpoint_and_api_key_item.find(".endpoint-input");
            let endpoint_input_value = endpoint_input.val().trim();
            let api_key_input =
                endpoint_and_api_key_item.find(".api-key-input");
            let api_key_input_value = api_key_input.val().trim();

            if (endpoint_input_value.trim() === "") {
                console.log("Endpoint is empty.");
                return;
            } else {
                self.db.endpoints.put({
                    index: endpoint_input_value,
                    endpoint: endpoint_input_value,
                    api_key: api_key_input_value,
                    need_protect: false,
                });
                console.log(`new_endpoint: ${endpoint_input_value}`);
            }
            self.fill_available_models_select(endpoint_input_value);
        });

        let remove_endpoint_buttons = endpoint_and_api_key_item.find(
            ".remove-endpoint-button"
        );
        remove_endpoint_buttons.click(function () {
            let endpoint_input =
                endpoint_and_api_key_item.find(".endpoint-input");
            let endpoint_input_value = endpoint_input.val();
            endpoint_and_api_key_item.remove();
            if (
                endpoint_input_value.trim() === "" ||
                self.db.endpoints.get(endpoint_input_value) === undefined
            ) {
                console.log("Endpoint not in endpoints");
            } else {
                self.db.endpoints.delete(endpoint_input_value);
            }
            console.log(`remove endpoint: ${endpoint_input_value}`);

            // TODO: remove models of current endpoint from available_models_select
        });
    }
    fetch_available_models(endpoint) {
        console.log("fetch available models for endpoint:", endpoint);
        // if endpoint not starts with http(s), skip
        // such as "user-customized", which is used for other local functions or agents
        if (endpoint.startsWith("http")) {
            let available_models_requester = new AvailableModelsRequester(
                endpoint
            );
            // update available_models field of endpoint index in db.endpoints
            return available_models_requester.get().then((available_models) => {
                this.db.endpoints.update(endpoint, {
                    available_models: JSON.stringify(available_models),
                });
                return available_models;
            });
        } else {
            return Promise.resolve([]);
        }
    }
    fill_available_models_select() {
        // fetch available_models for all endpoints, and then fill available_models_select
        let available_models_select = $("#available-models-select");
        available_models_select.empty();
        this.db.endpoints.toArray().then((endpoints) => {
            let promises = endpoints.map((row) => {
                return this.fetch_available_models(row.endpoint).then(
                    (available_models) => {
                        available_models.forEach((model_id) => {
                            // if model duplicated in available_models_select values,
                            // then attach endpoint host name
                            let model_name = model_id;
                            let endpoint_hostname = new URL(
                                row.endpoint
                            ).hostname
                                .split(".")[0]
                                .split("-")[0];
                            let model_id_with_endpoint = `${row.endpoint}|${model_id}`;

                            model_name = `${model_id} (${endpoint_hostname})`;
                            const option = new Option(
                                model_name,
                                model_id_with_endpoint
                            );
                            available_models_select.append(option);
                        });
                    }
                );
            });
            Promise.all(promises).then(() => {
                this.set_default_model();
            });
        });
    }

    set_default_model() {
        let storage_default_model = localStorage.getItem("default_model");
        // format of storage_default_model is `{endpoint}|{model_id}`
        // if storage_default_model is null, or not in the available_models_select,
        // set as the first one of available_models_select
        let select = $("#available-models-select");
        if (
            storage_default_model &&
            select.find(`option[value="${storage_default_model}"]`).length > 0
        ) {
            select.val(storage_default_model);
            console.log(
                "load default model:",
                localStorage.getItem("default_model")
            );
        } else {
            let new_storage_default_model = select.find("option:first").val();
            select.val(new_storage_default_model);
            localStorage.setItem("default_model", new_storage_default_model);
            console.log(
                "set new default model:",
                localStorage.getItem("default_model")
            );
        }
    }
}

export let endpoint_storage = new EndpointStorage();