Spaces:
Running
Running
Upload 36 files
Browse files- kimi-js/kimi-llm-manager.js +41 -25
- kimi-js/kimi-module.js +16 -3
- kimi-js/kimi-script.js +59 -4
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:
|
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:
|
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:
|
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 |
-
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
1206 |
|
1207 |
-
|
1208 |
-
|
|
|
|
|
1209 |
|
1210 |
-
|
1211 |
-
|
1212 |
-
|
1213 |
-
|
1214 |
-
|
1215 |
-
|
1216 |
-
|
1217 |
-
|
1218 |
-
|
1219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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("
|
836 |
if (apiKeyInput) {
|
837 |
const keyPref = window.KimiProviderUtils ? window.KimiProviderUtils.getKeyPrefForProvider(provider) : null;
|
838 |
-
const providerKey = keyPref && preferences[keyPref] ? preferences[keyPref] :
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
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 () => {
|