Kevin Hu commited on
Commit
b7475cc
·
1 Parent(s): f5ebe5e

add agent completion API (#3192)

Browse files

### What problem does this PR solve?

#3105

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

api/apps/sdk/session.py CHANGED
@@ -14,16 +14,22 @@
14
  # limitations under the License.
15
  #
16
  import json
 
17
  from uuid import uuid4
18
 
19
  from flask import request, Response
20
 
 
21
  from api.db import StatusEnum
 
 
 
22
  from api.db.services.dialog_service import DialogService, ConversationService, chat
23
  from api.utils import get_uuid
24
  from api.utils.api_utils import get_error_data_result
25
  from api.utils.api_utils import get_result, token_required
26
 
 
27
  @manager.route('/chats/<chat_id>/sessions', methods=['POST'])
28
  @token_required
29
  def create(tenant_id,chat_id):
@@ -31,7 +37,7 @@ def create(tenant_id,chat_id):
31
  req["dialog_id"] = chat_id
32
  dia = DialogService.query(tenant_id=tenant_id, id=req["dialog_id"], status=StatusEnum.VALID.value)
33
  if not dia:
34
- return get_error_data_result(retmsg="You do not own the assistant")
35
  conv = {
36
  "id": get_uuid(),
37
  "dialog_id": req["dialog_id"],
@@ -50,6 +56,32 @@ def create(tenant_id,chat_id):
50
  del conv["reference"]
51
  return get_result(data=conv)
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  @manager.route('/chats/<chat_id>/sessions/<session_id>', methods=['PUT'])
54
  @token_required
55
  def update(tenant_id,chat_id,session_id):
@@ -74,7 +106,7 @@ def update(tenant_id,chat_id,session_id):
74
 
75
  @manager.route('/chats/<chat_id>/completions', methods=['POST'])
76
  @token_required
77
- def completion(tenant_id,chat_id):
78
  req = request.json
79
  if not req.get("session_id"):
80
  conv = {
@@ -158,6 +190,130 @@ def completion(tenant_id,chat_id):
158
  break
159
  return get_result(data=answer)
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  @manager.route('/chats/<chat_id>/sessions', methods=['GET'])
162
  @token_required
163
  def list(chat_id,tenant_id):
@@ -211,6 +367,7 @@ def list(chat_id,tenant_id):
211
  del conv["reference"]
212
  return get_result(data=convs)
213
 
 
214
  @manager.route('/chats/<chat_id>/sessions', methods=["DELETE"])
215
  @token_required
216
  def delete(tenant_id,chat_id):
 
14
  # limitations under the License.
15
  #
16
  import json
17
+ from functools import partial
18
  from uuid import uuid4
19
 
20
  from flask import request, Response
21
 
22
+ from agent.canvas import Canvas
23
  from api.db import StatusEnum
24
+ from api.db.db_models import API4Conversation
25
+ from api.db.services.api_service import API4ConversationService
26
+ from api.db.services.canvas_service import UserCanvasService
27
  from api.db.services.dialog_service import DialogService, ConversationService, chat
28
  from api.utils import get_uuid
29
  from api.utils.api_utils import get_error_data_result
30
  from api.utils.api_utils import get_result, token_required
31
 
32
+
33
  @manager.route('/chats/<chat_id>/sessions', methods=['POST'])
34
  @token_required
35
  def create(tenant_id,chat_id):
 
37
  req["dialog_id"] = chat_id
38
  dia = DialogService.query(tenant_id=tenant_id, id=req["dialog_id"], status=StatusEnum.VALID.value)
39
  if not dia:
40
+ return get_error_data_result(retmsg="You do not own the assistant.")
41
  conv = {
42
  "id": get_uuid(),
43
  "dialog_id": req["dialog_id"],
 
56
  del conv["reference"]
57
  return get_result(data=conv)
58
 
59
+
60
+ @manager.route('/agents/<agent_id>/sessions', methods=['POST'])
61
+ @token_required
62
+ def create_agent_session(tenant_id, agent_id):
63
+ req = request.json
64
+ e, cvs = UserCanvasService.get_by_id(agent_id)
65
+ if not e:
66
+ return get_error_data_result("Agent not found.")
67
+ if cvs.user_id != tenant_id:
68
+ return get_error_data_result(retmsg="You do not own the agent.")
69
+
70
+ if not isinstance(cvs.dsl, str):
71
+ cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
72
+
73
+ canvas = Canvas(cvs.dsl, tenant_id)
74
+ conv = {
75
+ "id": get_uuid(),
76
+ "dialog_id": cvs.id,
77
+ "user_id": req.get("user_id", ""),
78
+ "message": [{"role": "assistant", "content": canvas.get_prologue()}],
79
+ "source": "agent"
80
+ }
81
+ API4ConversationService.save(**conv)
82
+ return get_result(data=conv)
83
+
84
+
85
  @manager.route('/chats/<chat_id>/sessions/<session_id>', methods=['PUT'])
86
  @token_required
87
  def update(tenant_id,chat_id,session_id):
 
106
 
107
  @manager.route('/chats/<chat_id>/completions', methods=['POST'])
108
  @token_required
109
+ def completion(tenant_id, chat_id):
110
  req = request.json
111
  if not req.get("session_id"):
112
  conv = {
 
190
  break
191
  return get_result(data=answer)
192
 
193
+
194
+ @manager.route('/agents/<agent_id>/completions', methods=['POST'])
195
+ @token_required
196
+ def agent_completion(tenant_id, agent_id):
197
+ req = request.json
198
+ e, cvs = UserCanvasService.get_by_id(agent_id)
199
+ if not e:
200
+ return get_error_data_result("Agent not found.")
201
+ if cvs.user_id != tenant_id:
202
+ return get_error_data_result(retmsg="You do not own the agent.")
203
+ if not isinstance(cvs.dsl, str):
204
+ cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
205
+
206
+ canvas = Canvas(cvs.dsl, tenant_id)
207
+
208
+ msg = []
209
+ for m in req["messages"]:
210
+ if m["role"] == "system":
211
+ continue
212
+ if m["role"] == "assistant" and not msg:
213
+ continue
214
+ msg.append(m)
215
+ if not msg[-1].get("id"): msg[-1]["id"] = get_uuid()
216
+ message_id = msg[-1]["id"]
217
+
218
+ if not req.get("session_id"):
219
+ session_id = get_uuid()
220
+ conv = {
221
+ "id": session_id,
222
+ "dialog_id": cvs.id,
223
+ "user_id": req.get("user_id", ""),
224
+ "message": [{"role": "assistant", "content": canvas.get_prologue()}],
225
+ "source": "agent"
226
+ }
227
+ API4ConversationService.save(**conv)
228
+ conv = API4Conversation(**conv)
229
+ else:
230
+ session_id = req.get("session_id")
231
+ e, conv = API4ConversationService.get_by_id(req["session_id"])
232
+ if not e:
233
+ return get_error_data_result(retmsg="Session not found!")
234
+
235
+ if "quote" not in req: req["quote"] = False
236
+ stream = req.get("stream", True)
237
+
238
+ def fillin_conv(ans):
239
+ nonlocal conv, message_id
240
+ if not conv.reference:
241
+ conv.reference.append(ans["reference"])
242
+ else:
243
+ conv.reference[-1] = ans["reference"]
244
+ conv.message[-1] = {"role": "assistant", "content": ans["answer"], "id": message_id}
245
+ ans["id"] = message_id
246
+ ans["session_id"] = session_id
247
+
248
+ def rename_field(ans):
249
+ reference = ans['reference']
250
+ if not isinstance(reference, dict):
251
+ return
252
+ for chunk_i in reference.get('chunks', []):
253
+ if 'docnm_kwd' in chunk_i:
254
+ chunk_i['doc_name'] = chunk_i['docnm_kwd']
255
+ chunk_i.pop('docnm_kwd')
256
+ conv.message.append(msg[-1])
257
+
258
+ if not conv.reference:
259
+ conv.reference = []
260
+ conv.message.append({"role": "assistant", "content": "", "id": message_id})
261
+ conv.reference.append({"chunks": [], "doc_aggs": []})
262
+
263
+ final_ans = {"reference": [], "content": ""}
264
+
265
+ canvas.messages.append(msg[-1])
266
+ canvas.add_user_input(msg[-1]["content"])
267
+ answer = canvas.run(stream=stream)
268
+
269
+ assert answer is not None, "Nothing. Is it over?"
270
+
271
+ if stream:
272
+ assert isinstance(answer, partial), "Nothing. Is it over?"
273
+
274
+ def sse():
275
+ nonlocal answer, cvs, conv
276
+ try:
277
+ for ans in answer():
278
+ for k in ans.keys():
279
+ final_ans[k] = ans[k]
280
+ ans = {"answer": ans["content"], "reference": ans.get("reference", [])}
281
+ fillin_conv(ans)
282
+ rename_field(ans)
283
+ yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": ans},
284
+ ensure_ascii=False) + "\n\n"
285
+
286
+ canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id})
287
+ if final_ans.get("reference"):
288
+ canvas.reference.append(final_ans["reference"])
289
+ cvs.dsl = json.loads(str(canvas))
290
+ API4ConversationService.append_message(conv.id, conv.to_dict())
291
+ except Exception as e:
292
+ yield "data:" + json.dumps({"retcode": 500, "retmsg": str(e),
293
+ "data": {"answer": "**ERROR**: " + str(e), "reference": []}},
294
+ ensure_ascii=False) + "\n\n"
295
+ yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": True}, ensure_ascii=False) + "\n\n"
296
+
297
+ resp = Response(sse(), mimetype="text/event-stream")
298
+ resp.headers.add_header("Cache-control", "no-cache")
299
+ resp.headers.add_header("Connection", "keep-alive")
300
+ resp.headers.add_header("X-Accel-Buffering", "no")
301
+ resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
302
+ return resp
303
+
304
+ final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else ""
305
+ canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id})
306
+ if final_ans.get("reference"):
307
+ canvas.reference.append(final_ans["reference"])
308
+ cvs.dsl = json.loads(str(canvas))
309
+
310
+ result = {"answer": final_ans["content"], "reference": final_ans.get("reference", [])}
311
+ fillin_conv(result)
312
+ API4ConversationService.append_message(conv.id, conv.to_dict())
313
+ rename_field(result)
314
+ return get_result(data=result)
315
+
316
+
317
  @manager.route('/chats/<chat_id>/sessions', methods=['GET'])
318
  @token_required
319
  def list(chat_id,tenant_id):
 
367
  del conv["reference"]
368
  return get_result(data=convs)
369
 
370
+
371
  @manager.route('/chats/<chat_id>/sessions', methods=["DELETE"])
372
  @token_required
373
  def delete(tenant_id,chat_id):
api/db/services/canvas_service.py CHANGED
@@ -22,5 +22,6 @@ from api.db.services.common_service import CommonService
22
  class CanvasTemplateService(CommonService):
23
  model = CanvasTemplate
24
 
 
25
  class UserCanvasService(CommonService):
26
  model = UserCanvas
 
22
  class CanvasTemplateService(CommonService):
23
  model = CanvasTemplate
24
 
25
+
26
  class UserCanvasService(CommonService):
27
  model = UserCanvas