Skip to content

Commit da5aec0

Browse files
committed
Merge remote-tracking branch 'upstream/main' into fix233
2 parents 13318ba + 5a2011e commit da5aec0

77 files changed

Lines changed: 2498 additions & 1140 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/apps/conversation_app.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from api.apps import current_user, login_required
2424
from api.db.db_models import APIToken
2525
from api.db.services.conversation_service import ConversationService, structure_answer
26-
from api.db.services.dialog_service import DialogService, ask, chat, gen_mindmap
26+
from api.db.services.dialog_service import DialogService, async_ask, async_chat, gen_mindmap
2727
from api.db.services.llm_service import LLMBundle
2828
from api.db.services.search_service import SearchService
2929
from api.db.services.tenant_llm_service import TenantLLMService
@@ -218,10 +218,10 @@ async def completion():
218218
dia.llm_setting = chat_model_config
219219

220220
is_embedded = bool(chat_model_id)
221-
def stream():
221+
async def stream():
222222
nonlocal dia, msg, req, conv
223223
try:
224-
for ans in chat(dia, msg, True, **req):
224+
async for ans in async_chat(dia, msg, True, **req):
225225
ans = structure_answer(conv, ans, message_id, conv.id)
226226
yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n"
227227
if not is_embedded:
@@ -241,7 +241,7 @@ def stream():
241241

242242
else:
243243
answer = None
244-
for ans in chat(dia, msg, **req):
244+
async for ans in async_chat(dia, msg, **req):
245245
answer = structure_answer(conv, ans, message_id, conv.id)
246246
if not is_embedded:
247247
ConversationService.update_by_id(conv.id, conv.to_dict())
@@ -406,10 +406,10 @@ async def ask_about():
406406
if search_app:
407407
search_config = search_app.get("search_config", {})
408408

409-
def stream():
409+
async def stream():
410410
nonlocal req, uid
411411
try:
412-
for ans in ask(req["question"], req["kb_ids"], uid, search_config=search_config):
412+
async for ans in async_ask(req["question"], req["kb_ids"], uid, search_config=search_config):
413413
yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n"
414414
except Exception as e:
415415
yield "data:" + json.dumps({"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, ensure_ascii=False) + "\n\n"

api/apps/langfuse_app.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ async def set_api_key():
3434
if not all([secret_key, public_key, host]):
3535
return get_error_data_result(message="Missing required fields")
3636

37+
current_user_id = current_user.id
3738
langfuse_keys = dict(
38-
tenant_id=current_user.id,
39+
tenant_id=current_user_id,
3940
secret_key=secret_key,
4041
public_key=public_key,
4142
host=host,
@@ -45,23 +46,24 @@ async def set_api_key():
4546
if not langfuse.auth_check():
4647
return get_error_data_result(message="Invalid Langfuse keys")
4748

48-
langfuse_entry = TenantLangfuseService.filter_by_tenant(tenant_id=current_user.id)
49+
langfuse_entry = TenantLangfuseService.filter_by_tenant(tenant_id=current_user_id)
4950
with DB.atomic():
5051
try:
5152
if not langfuse_entry:
5253
TenantLangfuseService.save(**langfuse_keys)
5354
else:
54-
TenantLangfuseService.update_by_tenant(tenant_id=current_user.id, langfuse_keys=langfuse_keys)
55+
TenantLangfuseService.update_by_tenant(tenant_id=current_user_id, langfuse_keys=langfuse_keys)
5556
return get_json_result(data=langfuse_keys)
5657
except Exception as e:
57-
server_error_response(e)
58+
return server_error_response(e)
5859

5960

6061
@manager.route("/api_key", methods=["GET"]) # noqa: F821
6162
@login_required
6263
@validate_request()
6364
def get_api_key():
64-
langfuse_entry = TenantLangfuseService.filter_by_tenant_with_info(tenant_id=current_user.id)
65+
current_user_id = current_user.id
66+
langfuse_entry = TenantLangfuseService.filter_by_tenant_with_info(tenant_id=current_user_id)
6567
if not langfuse_entry:
6668
return get_json_result(message="Have not record any Langfuse keys.")
6769

@@ -72,7 +74,7 @@ def get_api_key():
7274
except langfuse.api.core.api_error.ApiError as api_err:
7375
return get_json_result(message=f"Error from Langfuse: {api_err}")
7476
except Exception as e:
75-
server_error_response(e)
77+
return server_error_response(e)
7678

7779
langfuse_entry["project_id"] = langfuse.api.projects.get().dict()["data"][0]["id"]
7880
langfuse_entry["project_name"] = langfuse.api.projects.get().dict()["data"][0]["name"]
@@ -84,7 +86,8 @@ def get_api_key():
8486
@login_required
8587
@validate_request()
8688
def delete_api_key():
87-
langfuse_entry = TenantLangfuseService.filter_by_tenant(tenant_id=current_user.id)
89+
current_user_id = current_user.id
90+
langfuse_entry = TenantLangfuseService.filter_by_tenant(tenant_id=current_user_id)
8891
if not langfuse_entry:
8992
return get_json_result(message="Have not record any Langfuse keys.")
9093

@@ -93,4 +96,4 @@ def delete_api_key():
9396
TenantLangfuseService.delete_model(langfuse_entry)
9497
return get_json_result(data=True)
9598
except Exception as e:
96-
server_error_response(e)
99+
return server_error_response(e)

api/apps/llm_app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ async def set_api_key():
7474
assert factory in ChatModel, f"Chat model from {factory} is not supported yet."
7575
mdl = ChatModel[factory](req["api_key"], llm.llm_name, base_url=req.get("base_url"), **extra)
7676
try:
77-
m, tc = mdl.chat(None, [{"role": "user", "content": "Hello! How are you doing!"}], {"temperature": 0.9, "max_tokens": 50})
77+
m, tc = await mdl.async_chat(None, [{"role": "user", "content": "Hello! How are you doing!"}], {"temperature": 0.9, "max_tokens": 50})
7878
if m.find("**ERROR**") >= 0:
7979
raise Exception(m)
8080
chat_passed = True
@@ -217,7 +217,7 @@ def apikey_json(keys):
217217
**extra,
218218
)
219219
try:
220-
m, tc = mdl.chat(None, [{"role": "user", "content": "Hello! How are you doing!"}], {"temperature": 0.9})
220+
m, tc = await mdl.async_chat(None, [{"role": "user", "content": "Hello! How are you doing!"}], {"temperature": 0.9})
221221
if not tc and m.find("**ERROR**:") >= 0:
222222
raise Exception(m)
223223
except Exception as e:

api/apps/sdk/session.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626
from api.db.services.api_service import API4ConversationService
2727
from api.db.services.canvas_service import UserCanvasService, completion_openai
2828
from api.db.services.canvas_service import completion as agent_completion
29-
from api.db.services.conversation_service import ConversationService, iframe_completion
30-
from api.db.services.conversation_service import completion as rag_completion
31-
from api.db.services.dialog_service import DialogService, ask, chat, gen_mindmap, meta_filter
29+
from api.db.services.conversation_service import ConversationService
30+
from api.db.services.conversation_service import async_iframe_completion as iframe_completion
31+
from api.db.services.conversation_service import async_completion as rag_completion
32+
from api.db.services.dialog_service import DialogService, async_ask, async_chat, gen_mindmap, meta_filter
3233
from api.db.services.document_service import DocumentService
3334
from api.db.services.knowledgebase_service import KnowledgebaseService
3435
from api.db.services.llm_service import LLMBundle
@@ -141,7 +142,7 @@ async def chat_completion(tenant_id, chat_id):
141142
return resp
142143
else:
143144
answer = None
144-
for ans in rag_completion(tenant_id, chat_id, **req):
145+
async for ans in rag_completion(tenant_id, chat_id, **req):
145146
answer = ans
146147
break
147148
return get_result(data=answer)
@@ -245,7 +246,7 @@ async def chat_completion_openai_like(tenant_id, chat_id):
245246
# The value for the usage field on all chunks except for the last one will be null.
246247
# The usage field on the last chunk contains token usage statistics for the entire request.
247248
# The choices field on the last chunk will always be an empty array [].
248-
def streamed_response_generator(chat_id, dia, msg):
249+
async def streamed_response_generator(chat_id, dia, msg):
249250
token_used = 0
250251
answer_cache = ""
251252
reasoning_cache = ""
@@ -274,7 +275,7 @@ def streamed_response_generator(chat_id, dia, msg):
274275
}
275276

276277
try:
277-
for ans in chat(dia, msg, True, toolcall_session=toolcall_session, tools=tools, quote=need_reference):
278+
async for ans in async_chat(dia, msg, True, toolcall_session=toolcall_session, tools=tools, quote=need_reference):
278279
last_ans = ans
279280
answer = ans["answer"]
280281

@@ -342,7 +343,7 @@ def streamed_response_generator(chat_id, dia, msg):
342343
return resp
343344
else:
344345
answer = None
345-
for ans in chat(dia, msg, False, toolcall_session=toolcall_session, tools=tools, quote=need_reference):
346+
async for ans in async_chat(dia, msg, False, toolcall_session=toolcall_session, tools=tools, quote=need_reference):
346347
# focus answer content only
347348
answer = ans
348349
break
@@ -733,10 +734,10 @@ async def ask_about(tenant_id):
733734
return get_error_data_result(f"The dataset {kb_id} doesn't own parsed file")
734735
uid = tenant_id
735736

736-
def stream():
737+
async def stream():
737738
nonlocal req, uid
738739
try:
739-
for ans in ask(req["question"], req["kb_ids"], uid):
740+
async for ans in async_ask(req["question"], req["kb_ids"], uid):
740741
yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n"
741742
except Exception as e:
742743
yield "data:" + json.dumps(
@@ -827,7 +828,7 @@ async def chatbot_completions(dialog_id):
827828
resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
828829
return resp
829830

830-
for answer in iframe_completion(dialog_id, **req):
831+
async for answer in iframe_completion(dialog_id, **req):
831832
return get_result(data=answer)
832833

833834

@@ -918,10 +919,10 @@ async def ask_about_embedded():
918919
if search_app := SearchService.get_detail(search_id):
919920
search_config = search_app.get("search_config", {})
920921

921-
def stream():
922+
async def stream():
922923
nonlocal req, uid
923924
try:
924-
for ans in ask(req["question"], req["kb_ids"], uid, search_config=search_config):
925+
async for ans in async_ask(req["question"], req["kb_ids"], uid, search_config=search_config):
925926
yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n"
926927
except Exception as e:
927928
yield "data:" + json.dumps(

api/db/services/conversation_service.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from api.db.db_models import Conversation, DB
2020
from api.db.services.api_service import API4ConversationService
2121
from api.db.services.common_service import CommonService
22-
from api.db.services.dialog_service import DialogService, chat
22+
from api.db.services.dialog_service import DialogService, async_chat
2323
from common.misc_utils import get_uuid
2424
import json
2525

@@ -89,8 +89,7 @@ def structure_answer(conv, ans, message_id, session_id):
8989
conv.reference[-1] = reference
9090
return ans
9191

92-
93-
def completion(tenant_id, chat_id, question, name="New session", session_id=None, stream=True, **kwargs):
92+
async def async_completion(tenant_id, chat_id, question, name="New session", session_id=None, stream=True, **kwargs):
9493
assert name, "`name` can not be empty."
9594
dia = DialogService.query(id=chat_id, tenant_id=tenant_id, status=StatusEnum.VALID.value)
9695
assert dia, "You do not own the chat."
@@ -112,7 +111,7 @@ def completion(tenant_id, chat_id, question, name="New session", session_id=None
112111
"reference": {},
113112
"audio_binary": None,
114113
"id": None,
115-
"session_id": session_id
114+
"session_id": session_id
116115
}},
117116
ensure_ascii=False) + "\n\n"
118117
yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n"
@@ -148,7 +147,7 @@ def completion(tenant_id, chat_id, question, name="New session", session_id=None
148147

149148
if stream:
150149
try:
151-
for ans in chat(dia, msg, True, **kwargs):
150+
async for ans in async_chat(dia, msg, True, **kwargs):
152151
ans = structure_answer(conv, ans, message_id, session_id)
153152
yield "data:" + json.dumps({"code": 0, "data": ans}, ensure_ascii=False) + "\n\n"
154153
ConversationService.update_by_id(conv.id, conv.to_dict())
@@ -160,14 +159,13 @@ def completion(tenant_id, chat_id, question, name="New session", session_id=None
160159

161160
else:
162161
answer = None
163-
for ans in chat(dia, msg, False, **kwargs):
162+
async for ans in async_chat(dia, msg, False, **kwargs):
164163
answer = structure_answer(conv, ans, message_id, session_id)
165164
ConversationService.update_by_id(conv.id, conv.to_dict())
166165
break
167166
yield answer
168167

169-
170-
def iframe_completion(dialog_id, question, session_id=None, stream=True, **kwargs):
168+
async def async_iframe_completion(dialog_id, question, session_id=None, stream=True, **kwargs):
171169
e, dia = DialogService.get_by_id(dialog_id)
172170
assert e, "Dialog not found"
173171
if not session_id:
@@ -222,7 +220,7 @@ def iframe_completion(dialog_id, question, session_id=None, stream=True, **kwarg
222220

223221
if stream:
224222
try:
225-
for ans in chat(dia, msg, True, **kwargs):
223+
async for ans in async_chat(dia, msg, True, **kwargs):
226224
ans = structure_answer(conv, ans, message_id, session_id)
227225
yield "data:" + json.dumps({"code": 0, "message": "", "data": ans},
228226
ensure_ascii=False) + "\n\n"
@@ -235,7 +233,7 @@ def iframe_completion(dialog_id, question, session_id=None, stream=True, **kwarg
235233

236234
else:
237235
answer = None
238-
for ans in chat(dia, msg, False, **kwargs):
236+
async for ans in async_chat(dia, msg, False, **kwargs):
239237
answer = structure_answer(conv, ans, message_id, session_id)
240238
API4ConversationService.append_message(conv.id, conv.to_dict())
241239
break

api/db/services/dialog_service.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ def get_all_dialogs_by_tenant_id(cls, tenant_id):
178178
offset += limit
179179
return res
180180

181-
def chat_solo(dialog, messages, stream=True):
181+
182+
async def async_chat_solo(dialog, messages, stream=True):
182183
attachments = ""
183184
if "files" in messages[-1]:
184185
attachments = "\n\n".join(FileService.get_files(messages[-1]["files"]))
@@ -197,7 +198,8 @@ def chat_solo(dialog, messages, stream=True):
197198
if stream:
198199
last_ans = ""
199200
delta_ans = ""
200-
for ans in chat_mdl.chat_streamly(prompt_config.get("system", ""), msg, dialog.llm_setting):
201+
answer = ""
202+
async for ans in chat_mdl.async_chat_streamly(prompt_config.get("system", ""), msg, dialog.llm_setting):
201203
answer = ans
202204
delta_ans = ans[len(last_ans):]
203205
if num_tokens_from_string(delta_ans) < 16:
@@ -208,7 +210,7 @@ def chat_solo(dialog, messages, stream=True):
208210
if delta_ans:
209211
yield {"answer": answer, "reference": {}, "audio_binary": tts(tts_mdl, delta_ans), "prompt": "", "created_at": time.time()}
210212
else:
211-
answer = chat_mdl.chat(prompt_config.get("system", ""), msg, dialog.llm_setting)
213+
answer = await chat_mdl.async_chat(prompt_config.get("system", ""), msg, dialog.llm_setting)
212214
user_content = msg[-1].get("content", "[content not available]")
213215
logging.debug("User: {}|Assistant: {}".format(user_content, answer))
214216
yield {"answer": answer, "reference": {}, "audio_binary": tts(tts_mdl, answer), "prompt": "", "created_at": time.time()}
@@ -347,13 +349,12 @@ def filter_out(v2docs, operator, value):
347349
return []
348350
return list(doc_ids)
349351

350-
351-
def chat(dialog, messages, stream=True, **kwargs):
352+
async def async_chat(dialog, messages, stream=True, **kwargs):
352353
assert messages[-1]["role"] == "user", "The last content of this conversation is not from user."
353354
if not dialog.kb_ids and not dialog.prompt_config.get("tavily_api_key"):
354-
for ans in chat_solo(dialog, messages, stream):
355+
async for ans in async_chat_solo(dialog, messages, stream):
355356
yield ans
356-
return None
357+
return
357358

358359
chat_start_ts = timer()
359360

@@ -400,7 +401,7 @@ def chat(dialog, messages, stream=True, **kwargs):
400401
ans = use_sql(questions[-1], field_map, dialog.tenant_id, chat_mdl, prompt_config.get("quote", True), dialog.kb_ids)
401402
if ans:
402403
yield ans
403-
return None
404+
return
404405

405406
for p in prompt_config["parameters"]:
406407
if p["key"] == "knowledge":
@@ -508,7 +509,8 @@ def chat(dialog, messages, stream=True, **kwargs):
508509
empty_res = prompt_config["empty_response"]
509510
yield {"answer": empty_res, "reference": kbinfos, "prompt": "\n\n### Query:\n%s" % " ".join(questions),
510511
"audio_binary": tts(tts_mdl, empty_res)}
511-
return {"answer": prompt_config["empty_response"], "reference": kbinfos}
512+
yield {"answer": prompt_config["empty_response"], "reference": kbinfos}
513+
return
512514

513515
kwargs["knowledge"] = "\n------\n" + "\n\n------\n\n".join(knowledges)
514516
gen_conf = dialog.llm_setting
@@ -612,7 +614,7 @@ def decorate_answer(answer):
612614
if stream:
613615
last_ans = ""
614616
answer = ""
615-
for ans in chat_mdl.chat_streamly(prompt + prompt4citation, msg[1:], gen_conf):
617+
async for ans in chat_mdl.async_chat_streamly(prompt + prompt4citation, msg[1:], gen_conf):
616618
if thought:
617619
ans = re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
618620
answer = ans
@@ -626,19 +628,19 @@ def decorate_answer(answer):
626628
yield {"answer": thought + answer, "reference": {}, "audio_binary": tts(tts_mdl, delta_ans)}
627629
yield decorate_answer(thought + answer)
628630
else:
629-
answer = chat_mdl.chat(prompt + prompt4citation, msg[1:], gen_conf)
631+
answer = await chat_mdl.async_chat(prompt + prompt4citation, msg[1:], gen_conf)
630632
user_content = msg[-1].get("content", "[content not available]")
631633
logging.debug("User: {}|Assistant: {}".format(user_content, answer))
632634
res = decorate_answer(answer)
633635
res["audio_binary"] = tts(tts_mdl, answer)
634636
yield res
635637

636-
return None
638+
return
637639

638640

639641
def use_sql(question, field_map, tenant_id, chat_mdl, quota=True, kb_ids=None):
640642
sys_prompt = """
641-
You are a Database Administrator. You need to check the fields of the following tables based on the user's list of questions and write the SQL corresponding to the last question.
643+
You are a Database Administrator. You need to check the fields of the following tables based on the user's list of questions and write the SQL corresponding to the last question.
642644
Ensure that:
643645
1. Field names should not start with a digit. If any field name starts with a digit, use double quotes around it.
644646
2. Write only the SQL, no explanations or additional text.
@@ -805,8 +807,7 @@ def tts(tts_mdl, text):
805807
return None
806808
return binascii.hexlify(bin).decode("utf-8")
807809

808-
809-
def ask(question, kb_ids, tenant_id, chat_llm_name=None, search_config={}):
810+
async def async_ask(question, kb_ids, tenant_id, chat_llm_name=None, search_config={}):
810811
doc_ids = search_config.get("doc_ids", [])
811812
rerank_mdl = None
812813
kb_ids = search_config.get("kb_ids", kb_ids)
@@ -880,7 +881,7 @@ def decorate_answer(answer):
880881
return {"answer": answer, "reference": refs}
881882

882883
answer = ""
883-
for ans in chat_mdl.chat_streamly(sys_prompt, msg, {"temperature": 0.1}):
884+
async for ans in chat_mdl.async_chat_streamly(sys_prompt, msg, {"temperature": 0.1}):
884885
answer = ans
885886
yield {"answer": answer, "reference": {}}
886887
yield decorate_answer(answer)

0 commit comments

Comments
 (0)