返回提交历史
Modified
g4f/Provider/ARTA.py
+3
-3
Modified
g4f/Provider/PollinationsAI.py
+10
-13
Modified
g4f/Provider/hf/HuggingFaceAPI.py
+42
-21
Modified
g4f/__init__.py
+2
-2
Modified
g4f/api/stubs.py
+11
-1
Modified
g4f/gui/client/static/js/chat.v1.js
+134
-109
Modified
g4f/tools/run_tools.py
+3
-3
XFEstudio/gpt4free
Add many parameters to API endpoints Support conversational HuggingFace providers Fix streaming in PollinationsAI provider
713ad2c8
代码差异
7 个文件
+205
-152
@@ -131,7 +131,7 @@ class ARTA(AsyncGeneratorProvider, ProviderModelMixin):
131
131
proxy: str = None,
132
132
prompt: str = None,
133
133
negative_prompt: str = "blurry, deformed hands, ugly",
134
images_num: int = 1,
134
n: int = 1,
135
135
guidance_scale: int = 7,
136
136
num_inference_steps: int = 30,
137
137
aspect_ratio: str = "1:1",
@@ -149,7 +149,7 @@ class ARTA(AsyncGeneratorProvider, ProviderModelMixin):
149
149
"prompt": prompt,
150
150
"negative_prompt": negative_prompt,
151
151
"style": model,
152
"images_num": str(images_num),
152
"images_num": str(n),
153
153
"cfg_scale": str(guidance_scale),
154
154
"steps": str(num_inference_steps),
155
155
"aspect_ratio": aspect_ratio,
@@ -181,7 +181,7 @@ class ARTA(AsyncGeneratorProvider, ProviderModelMixin):
181
181
return
182
182
elif status in ("IN_QUEUE", "IN_PROGRESS"):
183
183
yield Reasoning(status=("Waiting" if status == "IN_QUEUE" else "Generating") + "." * counter)
184
await asyncio.sleep(5) # Poll every 5 seconds
184
await asyncio.sleep(2) # Poll every 5 seconds
185
185
counter += 1
186
186
if counter > 3:
187
187
counter = 0
@@ -49,7 +49,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
49
49
# Models configuration
50
50
default_model = "openai"
51
51
default_image_model = "flux"
52
default_vision_model = "gpt-4o"
52
default_vision_model = default_model
53
53
text_models = [default_model]
54
54
image_models = [default_image_model]
55
55
extra_image_models = ["flux-pro", "flux-dev", "flux-schnell", "midjourney", "dall-e-3"]
@@ -141,6 +141,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
141
141
messages: Messages,
142
142
stream: bool = False,
143
143
proxy: str = None,
144
cache: bool = False,
145
# Image generation parameters
144
146
prompt: str = None,
145
147
width: int = 1024,
146
148
height: int = 1024,
@@ -149,19 +151,18 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
149
151
private: bool = False,
150
152
enhance: bool = False,
151
153
safe: bool = False,
154
# Text generation parameters
152
155
images: ImagesType = None,
153
156
temperature: float = None,
154
157
presence_penalty: float = None,
155
158
top_p: float = 1,
156
159
frequency_penalty: float = None,
157
160
response_format: Optional[dict] = None,
158
cache: bool = False,
159
extra_parameters: list[str] = ["tools", "parallel_tool_calls", "tool_choice", "reasoning_effort", "logit_bias"],
161
extra_parameters: list[str] = ["tools", "parallel_tool_calls", "tool_choice", "reasoning_effort", "logit_bias", "voice"],
160
162
**kwargs
161
163
) -> AsyncResult:
164
# Load model list
162
165
cls.get_models()
163
if images is not None and not model:
164
model = cls.default_vision_model
165
166
try:
166
167
model = cls.get_model(model)
167
168
except ModelNotFoundError:
@@ -231,7 +232,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
231
232
}
232
233
query = "&".join(f"{k}={quote_plus(v)}" for k, v in params.items() if v is not None)
233
234
url = f"{cls.image_api_endpoint}prompt/{quote_plus(prompt)}?{query}"
234
yield ImagePreview(url, prompt)
235
#yield ImagePreview(url, prompt)
235
236
236
237
async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
237
238
async with session.get(url, allow_redirects=True) as response:
@@ -276,7 +277,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
276
277
messages[-1] = last_message
277
278
278
279
async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
279
if model in cls.audio_models or stream:
280
if model in cls.audio_models:
280
281
#data["voice"] = random.choice(cls.audio_models[model])
281
282
url = cls.text_api_endpoint
282
283
stream = False
@@ -328,12 +329,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
328
329
if "tool_calls" in message:
329
330
yield ToolCalls(message["tool_calls"])
330
331
331
if content is not None:
332
if "</think>" in content and "<think>" not in content:
333
yield "<think>"
334
335
if content:
336
yield content.replace("\\(", "(").replace("\\)", ")")
332
if content:
333
yield content
337
334
338
335
if "usage" in result:
339
336
yield Usage(**result["usage"])
@@ -1,13 +1,15 @@
1
1
from __future__ import annotations
2
2
3
import requests
4
3
5
from ...providers.types import Messages
4
6
from ...typing import ImagesType
5
7
from ...requests import StreamSession, raise_for_status
6
8
from ...errors import ModelNotSupportedError
7
9
from ...providers.helper import get_last_user_message
10
from ...providers.response import ProviderInfo
8
11
from ..template.OpenaiTemplate import OpenaiTemplate
9
from .models import model_aliases, vision_models, default_vision_model, llama_models
10
from .HuggingChat import HuggingChat
12
from .models import model_aliases, vision_models, default_vision_model, llama_models, text_models
11
13
from ... import debug
12
14
13
15
class HuggingFaceAPI(OpenaiTemplate):
@@ -22,32 +24,47 @@ class HuggingFaceAPI(OpenaiTemplate):
22
24
default_vision_model = default_vision_model
23
25
vision_models = vision_models
24
26
model_aliases = model_aliases
27
fallback_models = text_models + vision_models
28
29
provider_mapping: dict[str, dict] = {}
25
30
26
pipeline_tags: dict[str, str] = {}
31
@classmethod
32
def get_model(cls, model: str, **kwargs) -> str:
33
try:
34
return super().get_model(model, **kwargs)
35
except ModelNotSupportedError:
36
return model
27
37
28
38
@classmethod
29
def get_models(cls, **kwargs):
39
def get_models(cls, **kwargs) -> list[str]:
30
40
if not cls.models:
31
HuggingChat.get_models()
32
cls.models = HuggingChat.text_models.copy()
33
for model in cls.vision_models:
34
if model not in cls.models:
35
cls.models.append(model)
41
url = "https://huggingface.co/api/models?inference=warm&&expand[]=inferenceProviderMapping"
42
response = requests.get(url)
43
if response.ok:
44
cls.models = [
45
model["id"]
46
for model in response.json()
47
if [
48
provider
49
for provider in model.get("inferenceProviderMapping")
50
if provider.get("task") == "conversational"]]
51
else:
52
cls.models = cls.fallback_models
36
53
return cls.models
37
54
38
55
@classmethod
39
async def get_pipline_tag(cls, model: str, api_key: str = None):
40
if model in cls.pipeline_tags:
41
return cls.pipeline_tags[model]
56
async def get_mapping(cls, model: str, api_key: str = None):
57
if model in cls.provider_mapping:
58
return cls.provider_mapping[model]
42
59
async with StreamSession(
43
60
timeout=30,
44
61
headers=cls.get_headers(False, api_key),
45
62
) as session:
46
async with session.get(f"https://huggingface.co/api/models/{model}") as response:
63
async with session.get(f"https://huggingface.co/api/models/{model}?expand[]=inferenceProviderMapping") as response:
47
64
await raise_for_status(response)
48
65
model_data = await response.json()
49
cls.pipeline_tags[model] = model_data.get("pipeline_tag")
50
return cls.pipeline_tags[model]
66
cls.provider_mapping[model] = model_data.get("inferenceProviderMapping")
67
return cls.provider_mapping[model]
51
68
52
69
@classmethod
53
70
async def create_async_generator(
@@ -65,12 +82,16 @@ class HuggingFaceAPI(OpenaiTemplate):
65
82
model = llama_models["text"] if images is None else llama_models["vision"]
66
83
if model in cls.model_aliases:
67
84
model = cls.model_aliases[model]
68
api_base = f"https://api-inference.huggingface.co/models/{model}/v1"
69
pipeline_tag = await cls.get_pipline_tag(model, api_key)
70
if pipeline_tag not in ("text-generation", "image-text-to-text"):
71
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__} pipeline_tag: {pipeline_tag}")
72
elif images and pipeline_tag != "image-text-to-text":
73
raise ModelNotSupportedError(f"Model does not support images: {model} in: {cls.__name__} pipeline_tag: {pipeline_tag}")
85
provider_mapping = await cls.get_mapping(model, api_key)
86
for provider_key in provider_mapping:
87
api_path = provider_key if provider_key == "novita" else f"{provider_key}/v1"
88
api_base = f"https://router.huggingface.co/{api_path}"
89
task = provider_mapping[provider_key]["task"]
90
if task != "conversational":
91
raise ModelNotSupportedError(f"Model is not supported: {model} in: {cls.__name__} task: {task}")
92
model = provider_mapping[provider_key]["providerId"]
93
yield ProviderInfo(**{**cls.get_dict(), "label": f"HuggingFace ({provider_key})"})
94
break
74
95
start = calculate_lenght(messages)
75
96
if start > max_inputs_lenght:
76
97
if len(messages) > 6:
@@ -50,7 +50,7 @@ class ChatCompletion:
50
50
51
51
result = provider.get_create_function()(model, messages, stream=stream, **kwargs)
52
52
53
return result if stream else concat_chunks(result)
53
return result if stream or ignore_stream else concat_chunks(result)
54
54
55
55
@staticmethod
56
56
def create_async(model : Union[Model, str],
@@ -74,7 +74,7 @@ class ChatCompletion:
74
74
75
75
result = provider.get_async_create_function()(model, messages, stream=stream, **kwargs)
76
76
77
if not stream:
77
if not stream and not ignore_stream:
78
78
if hasattr(result, "__aiter__"):
79
79
result = async_concat_chunks(result)
80
80
@@ -18,6 +18,9 @@ class ChatCompletionsConfig(BaseModel):
18
18
image_name: Optional[str] = None
19
19
images: Optional[list[tuple[str, str]]] = None
20
20
temperature: Optional[float] = None
21
presence_penalty: Optional[float] = None
22
frequency_penalty: Optional[float] = None
23
top_p: Optional[float] = None
21
24
max_tokens: Optional[int] = None
22
25
stop: Union[list[str], str, None] = None
23
26
api_key: Optional[str] = None
@@ -27,7 +30,6 @@ class ChatCompletionsConfig(BaseModel):
27
30
conversation_id: Optional[str] = None
28
31
conversation: Optional[dict] = None
29
32
history_disabled: Optional[bool] = None
30
auto_continue: Optional[bool] = None
31
33
timeout: Optional[int] = None
32
34
tool_calls: list = Field(default=[], examples=[[
33
35
{
@@ -48,6 +50,14 @@ class ImageGenerationConfig(BaseModel):
48
50
response_format: Optional[str] = None
49
51
api_key: Optional[str] = None
50
52
proxy: Optional[str] = None
53
width: Optional[int] = None
54
height: Optional[int] = None
55
num_inference_steps: Optional[int] = None
56
seed: Optional[int] = None
57
guidance_scale: Optional[int] = None
58
aspect_ratio: Optional[str] = None
59
n: Optional[int] = None
60
negative_prompt: Optional[str] = None
51
61
52
62
class ProviderResponseModel(BaseModel):
53
63
id: str
@@ -40,12 +40,12 @@ let parameters_storage = {};
40
40
let finish_storage = {};
41
41
let usage_storage = {};
42
42
let reasoning_storage = {};
43
let generate_storage = {};
44
43
let title_ids_storage = {};
45
44
let image_storage = {};
46
45
let is_demo = false;
47
46
let wakeLock = null;
48
47
let countTokensEnabled = true;
48
let reloadConversation = true;
49
49
50
50
messageInput.addEventListener("blur", () => {
51
51
document.documentElement.scrollTop = 0;
@@ -203,10 +203,12 @@ const highlight = (container) => {
203
203
204
204
const get_message_el = (el) => {
205
205
let message_el = el;
206
while(!("index" in message_el.dataset) && message_el.parentElement) {
206
while(!(message_el.classList.contains('message')) && message_el.parentElement) {
207
207
message_el = message_el.parentElement;
208
208
}
209
return message_el;
209
if (message_el.classList.contains('message')) {
210
return message_el;
211
}
210
212
}
211
213
212
214
function register_message_images() {
@@ -220,7 +222,7 @@ function register_message_images() {
220
222
el.onerror = () => {
221
223
let indexCommand;
222
224
if ((indexCommand = el.src.indexOf("/generate/")) >= 0) {
223
generate_storage[window.conversation_id] = true;
225
reloadConversation = false;
224
226
indexCommand = indexCommand + "/generate/".length + 1;
225
227
let newPath = el.src.substring(indexCommand)
226
228
let filename = newPath.replace(/(?:\?.+?|$)/, "");
@@ -282,147 +284,170 @@ const register_message_buttons = async () => {
282
284
});
283
285
});
284
286
285
message_box.querySelectorAll(".message .fa-xmark").forEach(async (el) => el.addEventListener("click", async () => {
287
message_box.querySelectorAll(".message .fa-xmark").forEach(async (el) => {
286
288
if (el.dataset.click) {
287
289
return
288
290
}
289
291
el.dataset.click = true;
290
const message_el = get_message_el(el);
291
await remove_message(window.conversation_id, message_el.dataset.index);
292
message_el.remove();
293
await safe_load_conversation(window.conversation_id, false);
294
}));
292
el.addEventListener("click", async () => {
293
const message_el = get_message_el(el);
294
if (message_el) {
295
if ("index" in message_el.dataset) {
296
await remove_message(window.conversation_id, message_el.dataset.index);
297
}
298
message_el.remove();
299
}
300
reloadConversation = true;
301
await safe_load_conversation(window.conversation_id, false);
302
});
303
});
295
304
296
message_box.querySelectorAll(".message .fa-clipboard").forEach(async (el) => el.addEventListener("click", async () => {
305
message_box.querySelectorAll(".message .fa-clipboard").forEach(async (el) => {
297
306
if (el.dataset.click) {
298
307
return
299
308
}
300
309
el.dataset.click = true;
301
let message_el = get_message_el(el);
302
let response = await fetch(message_el.dataset.object_url);
303
let copyText = await response.text();
304
try {
305
if (!navigator.clipboard) {
306
throw new Error("navigator.clipboard: Clipboard API unavailable.");
310
el.addEventListener("click", async () => {
311
let message_el = get_message_el(el);
312
let response = await fetch(message_el.dataset.object_url);
313
let copyText = await response.text();
314
try {
315
if (!navigator.clipboard) {
316
throw new Error("navigator.clipboard: Clipboard API unavailable.");
317
}
318
await navigator.clipboard.writeText(copyText);
319
} catch (e) {
320
console.error(e);
321
console.error("Clipboard API writeText() failed! Fallback to document.exec(\"copy\")...");
322
fallback_clipboard(copyText);
307
323
}
308
await navigator.clipboard.writeText(copyText);
309
} catch (e) {
310
console.error(e);
311
console.error("Clipboard API writeText() failed! Fallback to document.exec(\"copy\")...");
312
fallback_clipboard(copyText);
313
}
314
el.classList.add("clicked");
315
setTimeout(() => el.classList.remove("clicked"), 1000);
316
}))
324
el.classList.add("clicked");
325
setTimeout(() => el.classList.remove("clicked"), 1000);
326
});
327
})
317
328
318
message_box.querySelectorAll(".message .fa-file-export").forEach(async (el) => el.addEventListener("click", async () => {
329
message_box.querySelectorAll(".message .fa-file-export").forEach(async (el) => {
319
330
if (el.dataset.click) {
320
331
return
321
332
}
322
333
el.dataset.click = true;
323
const elem = window.document.createElement('a');
324
let filename = `chat ${new Date().toLocaleString()}.txt`.replaceAll(":", "-");
325
const conversation = await get_conversation(window.conversation_id);
326
let buffer = "";
327
conversation.items.forEach(message => {
328
if (message.reasoning) {
329
buffer += render_reasoning_text(message.reasoning);
330
}
331
buffer += `${message.role == 'user' ? 'User' : 'Assistant'}: ${message.content.trim()}\n\n`;
334
el.addEventListener("click", async () => {
335
const elem = window.document.createElement('a');
336
let filename = `chat ${new Date().toLocaleString()}.txt`.replaceAll(":", "-");
337
const conversation = await get_conversation(window.conversation_id);
338
let buffer = "";
339
conversation.items.forEach(message => {
340
if (message.reasoning) {
341
buffer += render_reasoning_text(message.reasoning);
342
}
343
buffer += `${message.role == 'user' ? 'User' : 'Assistant'}: ${message.content.trim()}\n\n`;
344
});
345
var download = document.getElementById("download");
346
download.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(buffer.trim()));
347
download.setAttribute("download", filename);
348
download.click();
349
el.classList.add("clicked");
350
setTimeout(() => el.classList.remove("clicked"), 1000);
332
351
});
333
var download = document.getElementById("download");
334
download.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(buffer.trim()));
335
download.setAttribute("download", filename);
336
download.click();
337
el.classList.add("clicked");
338
setTimeout(() => el.classList.remove("clicked"), 1000);
339
}))
340
341
message_box.querySelectorAll(".message .fa-volume-high").forEach(async (el) => el.addEventListener("click", async () => {
352
})
353
354
message_box.querySelectorAll(".message .fa-volume-high").forEach(async (el) => {
342
355
if (el.dataset.click) {
343
356
return
344
357
}
345
358
el.dataset.click = true;
346
const message_el = get_message_el(el);
347
let audio;
348
if (message_el.dataset.synthesize_url) {
349
el.classList.add("active");
350
setTimeout(()=>el.classList.remove("active"), 2000);
351
const media_player = document.querySelector(".media_player");
352
if (!media_player.classList.contains("show")) {
353
media_player.classList.add("show");
354
audio = new Audio(message_el.dataset.synthesize_url);
355
audio.controls = true;
356
media_player.appendChild(audio);
357
} else {
358
audio = media_player.querySelector("audio");
359
audio.src = message_el.dataset.synthesize_url;
359
el.addEventListener("click", async () => {
360
const message_el = get_message_el(el);
361
let audio;
362
if (message_el.dataset.synthesize_url) {
363
el.classList.add("active");
364
setTimeout(()=>el.classList.remove("active"), 2000);
365
const media_player = document.querySelector(".media_player");
366
if (!media_player.classList.contains("show")) {
367
media_player.classList.add("show");
368
audio = new Audio(message_el.dataset.synthesize_url);
369
audio.controls = true;
370
media_player.appendChild(audio);
371
} else {
372
audio = media_player.querySelector("audio");
373
audio.src = message_el.dataset.synthesize_url;
374
}
375
audio.play();
376
return;
360
377
}
361
audio.play();
362
return;
363
}
364
}));
378
});
379
});
365
380
366
message_box.querySelectorAll(".message .regenerate_button").forEach(async (el) => el.addEventListener("click", async () => {
381
message_box.querySelectorAll(".message .regenerate_button").forEach(async (el) => {
367
382
if (el.dataset.click) {
368
383
return
369
384
}
370
385
el.dataset.click = true;
371
const message_el = get_message_el(el);
372
el.classList.add("clicked");
373
setTimeout(() => el.classList.remove("clicked"), 1000);
374
await ask_gpt(get_message_id(), message_el.dataset.index);
375
}));
386
el.addEventListener("click", async () => {
387
const message_el = get_message_el(el);
388
el.classList.add("clicked");
389
setTimeout(() => el.classList.remove("clicked"), 1000);
390
await ask_gpt(get_message_id(), message_el.dataset.index);
391
});
392
});
376
393
377
message_box.querySelectorAll(".message .continue_button").forEach(async (el) => el.addEventListener("click", async () => {
394
message_box.querySelectorAll(".message .continue_button").forEach(async (el) => {
378
395
if (el.dataset.click) {
379
396
return
380
397
}
381
398
el.dataset.click = true;
382
if (!el.disabled) {
383
el.disabled = true;
384
const message_el = get_message_el(el);
385
el.classList.add("clicked");
386
setTimeout(() => {el.classList.remove("clicked"); el.disabled = false}, 1000);
387
await ask_gpt(get_message_id(), message_el.dataset.index, false, null, null, "continue");
388
}}
389
));
399
el.addEventListener("click", async () => {
400
if (!el.disabled) {
401
el.disabled = true;
402
const message_el = get_message_el(el);
403
el.classList.add("clicked");
404
setTimeout(() => {el.classList.remove("clicked"); el.disabled = false}, 1000);
405
await ask_gpt(get_message_id(), message_el.dataset.index, false, null, null, "continue");
406
}
407
});
408
});
390
409
391
message_box.querySelectorAll(".message .fa-whatsapp").forEach(async (el) => el.addEventListener("click", async () => {
410
message_box.querySelectorAll(".message .fa-whatsapp").forEach(async (el) => {
392
411
if (el.dataset.click) {
393
412
return
394
413
}
395
414
el.dataset.click = true;
396
const text = get_message_el(el).innerText;
397
window.open(`https://wa.me/?text=${encodeURIComponent(text)}`, '_blank');
398
}));
415
el.addEventListener("click", async () => {
416
const text = get_message_el(el).innerText;
417
window.open(`https://wa.me/?text=${encodeURIComponent(text)}`, '_blank');
418
});
419
});
399
420
400
message_box.querySelectorAll(".message .fa-print").forEach(async (el) => el.addEventListener("click", async () => {
421
message_box.querySelectorAll(".message .fa-print").forEach(async (el) => {
401
422
if (el.dataset.click) {
402
423
return
403
424
}
404
425
el.dataset.click = true;
405
const message_el = get_message_el(el);
406
el.classList.add("clicked");
407
message_box.scrollTop = 0;
408
message_el.classList.add("print");
409
setTimeout(() => {
410
el.classList.remove("clicked");
411
message_el.classList.remove("print");
412
}, 1000);
413
window.print()
414
}));
426
el.addEventListener("click", async () => {
427
const message_el = get_message_el(el);
428
el.classList.add("clicked");
429
message_box.scrollTop = 0;
430
message_el.classList.add("print");
431
setTimeout(() => {
432
el.classList.remove("clicked");
433
message_el.classList.remove("print");
434
}, 1000);
435
window.print()
436
});
437
});
415
438
416
message_box.querySelectorAll(".message .reasoning_title").forEach(async (el) => el.addEventListener("click", async () => {
439
message_box.querySelectorAll(".message .reasoning_title").forEach(async (el) => {
417
440
if (el.dataset.click) {
418
441
return
419
442
}
420
443
el.dataset.click = true;
421
let text_el = el.parentElement.querySelector(".reasoning_text");
422
if (text_el) {
423
text_el.classList[text_el.classList.contains("hidden") ? "remove" : "add"]("hidden");
424
}
425
}));
444
el.addEventListener("click", async () => {
445
let text_el = el.parentElement.querySelector(".reasoning_text");
446
if (text_el) {
447
text_el.classList.toogle("hidden");
448
}
449
});
450
});
426
451
}
427
452
428
453
const delete_conversations = async () => {
@@ -842,7 +867,7 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
842
867
audio.controls = true;
843
868
content_map.inner.appendChild(audio);
844
869
audio.play();
845
generate_storage[window.conversation_id] = true;
870
reloadConversation = false;
846
871
} else if (message.type == "content") {
847
872
message_storage[message_id] += message.content;
848
873
update_message(content_map, message_id, null, scroll);
@@ -866,10 +891,11 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
866
891
} else if (message.type == "reasoning") {
867
892
if (!reasoning_storage[message_id]) {
868
893
reasoning_storage[message_id] = message;
869
reasoning_storage[message_id].text = message.token || "";
894
reasoning_storage[message_id].text = message_storage[message_id];
895
message_storage[message_id] = "";
870
896
} else if (message.status) {
871
897
reasoning_storage[message_id].status = message.status;
872
} else if (message.token) {
898
} if (message.token) {
873
899
reasoning_storage[message_id].text += message.token;
874
900
}
875
901
update_message(content_map, message_id, render_reasoning(reasoning_storage[message_id]), scroll);
@@ -1039,7 +1065,7 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
1039
1065
delete controller_storage[message_id];
1040
1066
}
1041
1067
// Reload conversation if no error
1042
if (!error_storage[message_id] && !generate_storage[window.conversation_id]) {
1068
if (!error_storage[message_id] && reloadConversation) {
1043
1069
await safe_load_conversation(window.conversation_id, scroll);
1044
1070
}
1045
1071
let cursorDiv = message_el.querySelector(".cursor");
@@ -1077,18 +1103,17 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
1077
1103
}
1078
1104
}
1079
1105
const ignored = Array.from(settings.querySelectorAll("input.provider:not(:checked)")).map((el)=>el.value);
1080
let extra_parameters = {};
1081
document.getElementById(`${provider}-form`)?.querySelectorAll(".saved input, .saved textarea").forEach(async (el) => {
1106
let extra_parameters = [];
1107
for (el of document.getElementById(`${provider}-form`)?.querySelectorAll(".saved input, .saved textarea") || []) {
1082
1108
let value = el.type == "checkbox" ? el.checked : el.value;
1083
extra_parameters[el.name] = value;
1084
1109
if (el.type == "textarea") {
1085
1110
try {
1086
extra_parameters[el.name] = await JSON.parse(value);
1111
value = await JSON.parse(value);
1087
1112
} catch (e) {
1088
1113
}
1089
1114
}
1090
});
1091
console.log(extra_parameters);
1115
extra_parameters[el.name] = value;
1116
};
1092
1117
await api("conversation", {
1093
1118
id: message_id,
1094
1119
conversation_id: window.conversation_id,
@@ -5,7 +5,7 @@ import json
5
5
import asyncio
6
6
import time
7
7
from pathlib import Path
8
from typing import Optional, Callable, AsyncIterator, Dict, Any, Tuple, List, Union
8
from typing import Optional, Callable, AsyncIterator, Iterator, Dict, Any, Tuple, List, Union
9
9
10
10
from ..typing import Messages
11
11
from ..providers.helper import filter_none
@@ -154,7 +154,7 @@ class ThinkingProcessor:
154
154
results = []
155
155
156
156
# Handle non-thinking chunk
157
if not start_time and "<think>" not in chunk:
157
if not start_time and "<think>" not in chunk and "</think>" not in chunk:
158
158
return 0, [chunk]
159
159
160
160
# Handle thinking start
@@ -255,7 +255,7 @@ def iter_run_tools(
255
255
provider: Optional[str] = None,
256
256
tool_calls: Optional[List[dict]] = None,
257
257
**kwargs
258
) -> AsyncIterator:
258
) -> Iterator:
259
259
"""Run tools synchronously and yield results"""
260
260
# Process web search
261
261
web_search = kwargs.get('web_search')