VirtualKimi commited on
Commit
4644bfd
·
verified ·
1 Parent(s): e8ca17d

Upload 36 files

Browse files
kimi-js/kimi-llm-manager.js CHANGED
@@ -933,6 +933,8 @@ class KimiLLMManager {
933
  ? window.getUnifiedDefaults()
934
  : { temperature: 0.9, maxTokens: 400, top_p: 0.9, frequency_penalty: 0.9, presence_penalty: 0.8 };
935
 
 
 
936
  const llmSettings = {
937
  temperature: await this.db.getPreference("llmTemperature", unifiedDefaults.temperature),
938
  maxTokens: await this.db.getPreference("llmMaxTokens", unifiedDefaults.maxTokens),
@@ -944,7 +946,7 @@ class KimiLLMManager {
944
  const payload = {
945
  model: this.currentModel,
946
  messages: messages,
947
- stream: true, // Enable streaming
948
  temperature: typeof options.temperature === "number" ? options.temperature : llmSettings.temperature,
949
  max_tokens: typeof options.maxTokens === "number" ? options.maxTokens : llmSettings.maxTokens,
950
  top_p: typeof options.topP === "number" ? options.topP : llmSettings.top_p,
@@ -1061,6 +1063,8 @@ class KimiLLMManager {
1061
  ? window.getUnifiedDefaults()
1062
  : { temperature: 0.9, maxTokens: 400, top_p: 0.9, frequency_penalty: 0.9, presence_penalty: 0.8 };
1063
 
 
 
1064
  const llmSettings = {
1065
  temperature: await this.db.getPreference("llmTemperature", unifiedDefaults.temperature),
1066
  maxTokens: await this.db.getPreference("llmMaxTokens", unifiedDefaults.maxTokens),
@@ -1072,7 +1076,7 @@ class KimiLLMManager {
1072
  const payload = {
1073
  model: this.currentModel,
1074
  messages: messages,
1075
- stream: true,
1076
  temperature: typeof options.temperature === "number" ? options.temperature : llmSettings.temperature,
1077
  max_tokens: typeof options.maxTokens === "number" ? options.maxTokens : llmSettings.maxTokens,
1078
  top_p: typeof options.topP === "number" ? options.topP : llmSettings.top_p,
@@ -1171,6 +1175,7 @@ class KimiLLMManager {
1171
 
1172
  async chatWithLocalStreaming(userMessage, onToken, options = {}) {
1173
  const systemPromptContent = await this.assemblePrompt(userMessage);
 
1174
 
1175
  const payload = {
1176
  model: this.currentModel || "llama2",
@@ -1179,7 +1184,7 @@ class KimiLLMManager {
1179
  ...this.conversationContext.slice(-this.maxContextLength),
1180
  { role: "user", content: userMessage }
1181
  ],
1182
- stream: true
1183
  };
1184
 
1185
  try {
@@ -1195,36 +1200,47 @@ class KimiLLMManager {
1195
  throw new Error("Ollama not available");
1196
  }
1197
 
1198
- const reader = response.body.getReader();
1199
- const decoder = new TextDecoder();
1200
  let fullResponse = "";
1201
 
1202
- try {
1203
- while (true) {
1204
- const { done, value } = await reader.read();
1205
- if (done) break;
1206
 
1207
- const chunk = decoder.decode(value, { stream: true });
1208
- const lines = chunk.split("\n").filter(line => line.trim());
 
 
1209
 
1210
- for (const line of lines) {
1211
- try {
1212
- const parsed = JSON.parse(line);
1213
- const content = parsed.message?.content;
1214
- if (content) {
1215
- fullResponse += content;
1216
- onToken(content);
1217
- }
1218
- if (parsed.done) {
1219
- break;
 
 
 
 
 
 
1220
  }
1221
- } catch (parseError) {
1222
- console.warn("Failed to parse Ollama streaming chunk:", parseError);
1223
  }
1224
  }
 
 
 
 
 
 
 
 
 
1225
  }
1226
- } finally {
1227
- reader.releaseLock();
1228
  }
1229
 
1230
  // Add to context
 
933
  ? window.getUnifiedDefaults()
934
  : { temperature: 0.9, maxTokens: 400, top_p: 0.9, frequency_penalty: 0.9, presence_penalty: 0.8 };
935
 
936
+ const enableStreaming = await this.db.getPreference("enableStreaming", true);
937
+
938
  const llmSettings = {
939
  temperature: await this.db.getPreference("llmTemperature", unifiedDefaults.temperature),
940
  maxTokens: await this.db.getPreference("llmMaxTokens", unifiedDefaults.maxTokens),
 
946
  const payload = {
947
  model: this.currentModel,
948
  messages: messages,
949
+ stream: enableStreaming, // Use user preference for streaming
950
  temperature: typeof options.temperature === "number" ? options.temperature : llmSettings.temperature,
951
  max_tokens: typeof options.maxTokens === "number" ? options.maxTokens : llmSettings.maxTokens,
952
  top_p: typeof options.topP === "number" ? options.topP : llmSettings.top_p,
 
1063
  ? window.getUnifiedDefaults()
1064
  : { temperature: 0.9, maxTokens: 400, top_p: 0.9, frequency_penalty: 0.9, presence_penalty: 0.8 };
1065
 
1066
+ const enableStreaming = await this.db.getPreference("enableStreaming", true);
1067
+
1068
  const llmSettings = {
1069
  temperature: await this.db.getPreference("llmTemperature", unifiedDefaults.temperature),
1070
  maxTokens: await this.db.getPreference("llmMaxTokens", unifiedDefaults.maxTokens),
 
1076
  const payload = {
1077
  model: this.currentModel,
1078
  messages: messages,
1079
+ stream: enableStreaming,
1080
  temperature: typeof options.temperature === "number" ? options.temperature : llmSettings.temperature,
1081
  max_tokens: typeof options.maxTokens === "number" ? options.maxTokens : llmSettings.maxTokens,
1082
  top_p: typeof options.topP === "number" ? options.topP : llmSettings.top_p,
 
1175
 
1176
  async chatWithLocalStreaming(userMessage, onToken, options = {}) {
1177
  const systemPromptContent = await this.assemblePrompt(userMessage);
1178
+ const enableStreaming = await this.db.getPreference("enableStreaming", true);
1179
 
1180
  const payload = {
1181
  model: this.currentModel || "llama2",
 
1184
  ...this.conversationContext.slice(-this.maxContextLength),
1185
  { role: "user", content: userMessage }
1186
  ],
1187
+ stream: enableStreaming
1188
  };
1189
 
1190
  try {
 
1200
  throw new Error("Ollama not available");
1201
  }
1202
 
 
 
1203
  let fullResponse = "";
1204
 
1205
+ if (enableStreaming) {
1206
+ // Streaming mode
1207
+ const reader = response.body.getReader();
1208
+ const decoder = new TextDecoder();
1209
 
1210
+ try {
1211
+ while (true) {
1212
+ const { done, value } = await reader.read();
1213
+ if (done) break;
1214
 
1215
+ const chunk = decoder.decode(value, { stream: true });
1216
+ const lines = chunk.split("\n").filter(line => line.trim());
1217
+
1218
+ for (const line of lines) {
1219
+ try {
1220
+ const parsed = JSON.parse(line);
1221
+ const content = parsed.message?.content;
1222
+ if (content) {
1223
+ fullResponse += content;
1224
+ onToken(content);
1225
+ }
1226
+ if (parsed.done) {
1227
+ break;
1228
+ }
1229
+ } catch (parseError) {
1230
+ console.warn("Failed to parse Ollama streaming chunk:", parseError);
1231
  }
 
 
1232
  }
1233
  }
1234
+ } finally {
1235
+ reader.releaseLock();
1236
+ }
1237
+ } else {
1238
+ // Non-streaming mode
1239
+ const data = await response.json();
1240
+ fullResponse = data.message?.content || "";
1241
+ if (fullResponse && onToken) {
1242
+ onToken(fullResponse);
1243
  }
 
 
1244
  }
1245
 
1246
  // Add to context
kimi-js/kimi-module.js CHANGED
@@ -777,7 +777,8 @@ async function loadSettingsData() {
777
  "llmMaxTokens",
778
  "llmTopP",
779
  "llmFrequencyPenalty",
780
- "llmPresencePenalty"
 
781
  ];
782
  const preferences = await kimiDB.getPreferencesBatch(preferenceKeys);
783
 
@@ -796,6 +797,7 @@ async function loadSettingsData() {
796
  const llmTopP = preferences.llmTopP !== undefined ? preferences.llmTopP : 0.9;
797
  const llmFrequencyPenalty = preferences.llmFrequencyPenalty !== undefined ? preferences.llmFrequencyPenalty : 0.9;
798
  const llmPresencePenalty = preferences.llmPresencePenalty !== undefined ? preferences.llmPresencePenalty : 0.8;
 
799
 
800
  // Update UI with voice settings
801
  const languageSelect = document.getElementById("language-selection");
@@ -811,6 +813,17 @@ async function loadSettingsData() {
811
  updateSlider("llm-frequency-penalty", llmFrequencyPenalty);
812
  updateSlider("llm-presence-penalty", llmPresencePenalty);
813
 
 
 
 
 
 
 
 
 
 
 
 
814
  // Batch load personality traits
815
  const traitNames = ["affection", "playfulness", "intelligence", "empathy", "humor", "romance"];
816
  const personality = await kimiDB.getPersonalityTraitsBatch(traitNames, selectedCharacter);
@@ -832,10 +845,10 @@ async function loadSettingsData() {
832
  await updateStats();
833
 
834
  // Update API key input
835
- const apiKeyInput = document.getElementById("openrouter-api-key");
836
  if (apiKeyInput) {
837
  const keyPref = window.KimiProviderUtils ? window.KimiProviderUtils.getKeyPrefForProvider(provider) : null;
838
- const providerKey = keyPref && preferences[keyPref] ? preferences[keyPref] : genericKey;
839
  apiKeyInput.value = providerKey || "";
840
  }
841
  const providerSelect = document.getElementById("llm-provider");
 
777
  "llmMaxTokens",
778
  "llmTopP",
779
  "llmFrequencyPenalty",
780
+ "llmPresencePenalty",
781
+ "enableStreaming"
782
  ];
783
  const preferences = await kimiDB.getPreferencesBatch(preferenceKeys);
784
 
 
797
  const llmTopP = preferences.llmTopP !== undefined ? preferences.llmTopP : 0.9;
798
  const llmFrequencyPenalty = preferences.llmFrequencyPenalty !== undefined ? preferences.llmFrequencyPenalty : 0.9;
799
  const llmPresencePenalty = preferences.llmPresencePenalty !== undefined ? preferences.llmPresencePenalty : 0.8;
800
+ const enableStreaming = preferences.enableStreaming !== undefined ? preferences.enableStreaming : true;
801
 
802
  // Update UI with voice settings
803
  const languageSelect = document.getElementById("language-selection");
 
813
  updateSlider("llm-frequency-penalty", llmFrequencyPenalty);
814
  updateSlider("llm-presence-penalty", llmPresencePenalty);
815
 
816
+ // Update streaming toggle
817
+ const streamingToggle = document.getElementById("enable-streaming");
818
+ if (streamingToggle) {
819
+ if (enableStreaming) {
820
+ streamingToggle.classList.add("active");
821
+ } else {
822
+ streamingToggle.classList.remove("active");
823
+ }
824
+ streamingToggle.setAttribute("aria-checked", String(enableStreaming));
825
+ }
826
+
827
  // Batch load personality traits
828
  const traitNames = ["affection", "playfulness", "intelligence", "empathy", "humor", "romance"];
829
  const personality = await kimiDB.getPersonalityTraitsBatch(traitNames, selectedCharacter);
 
845
  await updateStats();
846
 
847
  // Update API key input
848
+ const apiKeyInput = document.getElementById("provider-api-key");
849
  if (apiKeyInput) {
850
  const keyPref = window.KimiProviderUtils ? window.KimiProviderUtils.getKeyPrefForProvider(provider) : null;
851
+ const providerKey = keyPref && preferences[keyPref] ? preferences[keyPref] : apiKey;
852
  apiKeyInput.value = providerKey || "";
853
  }
854
  const providerSelect = document.getElementById("llm-provider");
kimi-js/kimi-script.js CHANGED
@@ -140,7 +140,14 @@ document.addEventListener("DOMContentLoaded", async function () {
140
  const baseUrlInput = ApiUi.baseUrlInput();
141
  const modelIdInput = ApiUi.modelIdInput();
142
  const apiKeyInput = ApiUi.apiKeyInput();
143
- if (baseUrlInput) baseUrlInput.value = baseUrl || "";
 
 
 
 
 
 
 
144
  // Only prefill model for OpenRouter, others should show placeholder only
145
  if (modelIdInput) {
146
  if (provider === "openrouter") {
@@ -193,6 +200,11 @@ document.addEventListener("DOMContentLoaded", async function () {
193
  }
194
  });
195
 
 
 
 
 
 
196
  const providerSelectEl = document.getElementById("llm-provider");
197
  if (providerSelectEl) {
198
  providerSelectEl.addEventListener("change", async function (e) {
@@ -241,7 +253,21 @@ document.addEventListener("DOMContentLoaded", async function () {
241
  const p = placeholders[provider] || placeholders.openai;
242
  if (baseUrlInput) {
243
  baseUrlInput.placeholder = p.url;
244
- baseUrlInput.value = provider === "openrouter" ? placeholders.openrouter.url : p.url;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
246
  if (apiKeyInput) {
247
  apiKeyInput.placeholder = p.keyPh;
@@ -263,7 +289,7 @@ document.addEventListener("DOMContentLoaded", async function () {
263
  }
264
  if (window.kimiDB) {
265
  await window.kimiDB.setPreference("llmProvider", provider);
266
- await window.kimiDB.setPreference("llmBaseUrl", provider === "openrouter" ? placeholders.openrouter.url : p.url);
267
  const apiKeyLabel = document.getElementById("api-key-label");
268
  // Load provider-specific key into the input for clarity
269
  const keyPref = window.KimiProviderUtils
@@ -296,6 +322,15 @@ document.addEventListener("DOMContentLoaded", async function () {
296
  }
297
  }
298
  ApiUi.clearStatus();
 
 
 
 
 
 
 
 
 
299
  }
300
  });
301
 
@@ -315,6 +350,27 @@ document.addEventListener("DOMContentLoaded", async function () {
315
  }
316
  });
317
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  }
319
 
320
  // Loading screen management
@@ -773,7 +829,6 @@ document.addEventListener("DOMContentLoaded", async function () {
773
  [],
774
  500
775
  );
776
-
777
  kimiInit.register(
778
  "dataManager",
779
  async () => {
 
140
  const baseUrlInput = ApiUi.baseUrlInput();
141
  const modelIdInput = ApiUi.modelIdInput();
142
  const apiKeyInput = ApiUi.apiKeyInput();
143
+
144
+ // Set base URL based on modifiability
145
+ if (baseUrlInput) {
146
+ const isModifiable = isUrlModifiable(provider);
147
+ baseUrlInput.value = baseUrl || "";
148
+ baseUrlInput.disabled = !isModifiable;
149
+ baseUrlInput.style.opacity = isModifiable ? "1" : "0.6";
150
+ }
151
  // Only prefill model for OpenRouter, others should show placeholder only
152
  if (modelIdInput) {
153
  if (provider === "openrouter") {
 
200
  }
201
  });
202
 
203
+ // Helper function to check if URL is modifiable for current provider
204
+ function isUrlModifiable(provider) {
205
+ return provider === "openai-compatible" || provider === "ollama";
206
+ }
207
+
208
  const providerSelectEl = document.getElementById("llm-provider");
209
  if (providerSelectEl) {
210
  providerSelectEl.addEventListener("change", async function (e) {
 
253
  const p = placeholders[provider] || placeholders.openai;
254
  if (baseUrlInput) {
255
  baseUrlInput.placeholder = p.url;
256
+ // Only allow URL modification for custom and ollama providers
257
+ const isModifiable = isUrlModifiable(provider);
258
+
259
+ if (isModifiable) {
260
+ // For custom and ollama: load saved URL or use default
261
+ const savedUrl = await window.kimiDB.getPreference("llmBaseUrl", p.url);
262
+ baseUrlInput.value = savedUrl;
263
+ baseUrlInput.disabled = false;
264
+ baseUrlInput.style.opacity = "1";
265
+ } else {
266
+ // For other providers: fixed URL, not modifiable
267
+ baseUrlInput.value = p.url;
268
+ baseUrlInput.disabled = true;
269
+ baseUrlInput.style.opacity = "0.6";
270
+ }
271
  }
272
  if (apiKeyInput) {
273
  apiKeyInput.placeholder = p.keyPh;
 
289
  }
290
  if (window.kimiDB) {
291
  await window.kimiDB.setPreference("llmProvider", provider);
292
+
293
  const apiKeyLabel = document.getElementById("api-key-label");
294
  // Load provider-specific key into the input for clarity
295
  const keyPref = window.KimiProviderUtils
 
322
  }
323
  }
324
  ApiUi.clearStatus();
325
+
326
+ // Save URL after all UI updates are complete
327
+ const isModifiable = isUrlModifiable(provider);
328
+ if (isModifiable && baseUrlInput) {
329
+ await window.kimiDB.setPreference("llmBaseUrl", baseUrlInput.value);
330
+ } else {
331
+ // For fixed providers, save the standard URL
332
+ await window.kimiDB.setPreference("llmBaseUrl", p.url);
333
+ }
334
  }
335
  });
336
 
 
350
  }
351
  });
352
  }
353
+
354
+ // Listen for Base URL changes and save for modifiable providers
355
+ const baseUrlInput = ApiUi.baseUrlInput();
356
+ if (baseUrlInput) {
357
+ baseUrlInput.addEventListener("blur", async function (e) {
358
+ const providerSelect = ApiUi.providerSelect();
359
+ const provider = providerSelect ? providerSelect.value : "openrouter";
360
+ const isModifiable = isUrlModifiable(provider);
361
+
362
+ if (isModifiable && window.kimiDB) {
363
+ const newUrl = e.target.value.trim();
364
+ if (newUrl) {
365
+ try {
366
+ await window.kimiDB.setPreference("llmBaseUrl", newUrl);
367
+ } catch (error) {
368
+ console.warn("Failed to save base URL:", error.message);
369
+ }
370
+ }
371
+ }
372
+ });
373
+ }
374
  }
375
 
376
  // Loading screen management
 
829
  [],
830
  500
831
  );
 
832
  kimiInit.register(
833
  "dataManager",
834
  async () => {