oneliai 1.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
oneliai/__init__.py ADDED
File without changes
oneliai/client.py ADDED
@@ -0,0 +1,930 @@
1
+ import requests
2
+ import asyncio
3
+ import uuid
4
+ import logging
5
+ import os
6
+ import pymysql
7
+ import json
8
+ from typing import List, Dict, Any, Optional,Callable
9
+ from datetime import datetime, date, time, timedelta
10
+ import decimal
11
+ import jwt
12
+ import redis
13
+ URL="https://apis.oneli.chat"
14
+ # URL="http://localhost:8085"
15
+ SECRET_KEY = "your-256-bit-secret" # 生产环境应从配置读取
16
+ ALGORITHM = "HS256"
17
+ appurl="http://localhost:3000"
18
+ protocol="http"
19
+ class AIClient:
20
+ def __init__(self, client_id, client_secret, base_url=f'{URL}/v1/strategy',mode = "auto",callback:Callable=None):
21
+ self.client_id = client_id
22
+ self.client_secret = client_secret
23
+ self.base_url = base_url
24
+ self.access_token = self._get_access_token()
25
+ self.mode = mode
26
+ self.local=False
27
+ self.local_ports = [3000, 3001, 3002, 3003, 3004] # Electron 可能使用的端口
28
+ self.remote_port = 8001 # 远程服务固定端口
29
+ self.callback=callback
30
+ self._task_checker: Optional[asyncio.Task] = None
31
+ self._should_stop = False
32
+
33
+ try:
34
+ # 同步Redis客户端(用于触发代码)
35
+ self.redis_client = redis.Redis(
36
+ host="124.70.82.221",
37
+ port=6379,
38
+ db=1,
39
+ password="cgpe34!",
40
+ decode_responses=True,
41
+ socket_connect_timeout=5,
42
+ socket_keepalive=True
43
+ )
44
+ # 测试连接
45
+ self.redis_client.ping()
46
+ logging.info("Redis同步客户端连接成功")
47
+ except redis.ConnectionError as e:
48
+ logging.error(f"Redis连接失败: {e}")
49
+ self.redis_client = None
50
+ raise
51
+
52
+ async def _get_userid(self):
53
+ payload = jwt.decode(
54
+ self.access_token,
55
+ SECRET_KEY,
56
+ algorithms=[ALGORITHM],
57
+ options={
58
+ "verify_signature": True,
59
+ "verify_exp": True,
60
+ "verify_aud": False # 根据需求调整
61
+ }
62
+ )
63
+ return payload["user_id"]
64
+
65
+ def _get_access_token(self):
66
+ response = requests.post(
67
+ f'{self.base_url}/auth/token',
68
+ json={
69
+ 'client_id': self.client_id,
70
+ 'client_secret': self.client_secret,
71
+ 'grant_type': 'client_credentials'
72
+ }
73
+ )
74
+ if response.status_code == 200:
75
+ return response.json().get('access_token')
76
+ else:
77
+ raise Exception('Failed to get access token')
78
+
79
+
80
+ def detect_environment(self) -> str:
81
+ """自动检测运行环境"""
82
+ if self.mode != "auto":
83
+ return self.mode
84
+
85
+ # 尝试连接本地 Electron 服务
86
+ for port in self.local_ports:
87
+ try:
88
+ url = f"{protocol}://127.0.0.1:{port}/api/health"
89
+ # url = f"http://127.0.0.1:{port}/api/health"
90
+ response = requests.get(url, timeout=2)
91
+
92
+ if response.status_code == 200:
93
+ data=response.json()
94
+
95
+ print(data['status'])
96
+ if data['status']=="healthy":
97
+ logging.info(f"检测到本地 Electron 服务 (端口 {port})")
98
+ return "local"
99
+ except:
100
+ continue
101
+
102
+ # 尝试连接远程服务
103
+ try:
104
+ url = f"{protocol}://127.0.0.1:{self.remote_port}/api/health"
105
+ response = requests.get(url, timeout=2)
106
+ if response.status_code == 200:
107
+ logging.info("检测到远程服务")
108
+ return "remote"
109
+ except:
110
+ pass
111
+
112
+ logging.warning("未检测到可用服务,使用默认模式: remote")
113
+ return "remote"
114
+
115
+ def get_service_url(self, endpoint: str) -> str:
116
+ """获取服务地址"""
117
+ # if self.base_url:
118
+ # return f"{self.base_url}{endpoint}"
119
+
120
+ mode = self.detect_environment()
121
+
122
+ if mode == "local":
123
+ self.local=True
124
+ # 动态发现 Electron 端口
125
+ for port in self.local_ports:
126
+ try:
127
+ health_url = f"{protocol}://127.0.0.1:{port}/api/health"
128
+ response = requests.get(health_url, timeout=1)
129
+ if response.status_code == 200:
130
+ return f"{protocol}://127.0.0.1:{port}{endpoint}"
131
+ except:
132
+ continue
133
+ # 如果没找到,使用第一个端口
134
+ return f"{protocol}://127.0.0.1:{self.local_ports[0]}{endpoint}"
135
+ else:
136
+ return f"{protocol}://127.0.0.1:{self.remote_port}{endpoint}"
137
+
138
+ def notify(self, action: str, params: Optional[Dict[str, Any]] = None,
139
+ timeout: int = 10) -> Dict[str, Any]:
140
+ """
141
+ 发送通知到 Electron
142
+
143
+ Args:
144
+ action: 动作类型
145
+ params: 参数字典
146
+ timeout: 超时时间
147
+
148
+ Returns:
149
+ 响应结果
150
+ """
151
+
152
+
153
+ try:
154
+
155
+ url = self.get_service_url("/api/run-task")
156
+ print(url)
157
+ data = {
158
+ "type": action,
159
+ "data": params or {}
160
+ }
161
+
162
+ logging.debug(f"发送通知到: {url}, 数据: {data}")
163
+
164
+ if self.local:
165
+ print("post")
166
+ response = requests.post(
167
+ url,
168
+ json=data,
169
+ verify=False,
170
+ headers={'Content-Type': 'application/json'}
171
+ )
172
+
173
+ result = response.json()
174
+ logging.info(f"通知发送成功: {action}, 响应: {result}")
175
+ return result
176
+ else:
177
+ print("Redis模式")
178
+ # response = requests.get(
179
+ # url,
180
+ # params={"request": json.dumps(data)}
181
+ # )
182
+ # result = response.json()
183
+ # Redis模式
184
+ if not self.redis_client:
185
+ raise Exception("Redis客户端未初始化")
186
+
187
+ channel = "sdk_signals"
188
+ message = json.dumps(data, ensure_ascii=False)
189
+
190
+ subscribers = self.redis_client.publish(channel, message)
191
+
192
+ result = {
193
+ "code": 200,
194
+ "msg": f"信号已通过Redis发布",
195
+ "subscribers": subscribers,
196
+ "channel": channel,
197
+ "action": action
198
+ }
199
+
200
+ logging.info(f"Redis通知发送成功,订阅者数: {subscribers}")
201
+ return result
202
+ # logging.info(f"通知发送成功: {action}, 响应: {result}")
203
+ # return result
204
+
205
+ except requests.exceptions.ConnectionError:
206
+ error_msg = "无法连接到 Electron 服务,请确保应用正在运行"
207
+ logging.error(error_msg)
208
+ return {"code": 500, "msg": error_msg}
209
+ except requests.exceptions.Timeout:
210
+ error_msg = "请求超时,请检查网络连接"
211
+ logging.error(error_msg)
212
+ return {"code": 500, "msg": error_msg}
213
+ except Exception as e:
214
+ error_msg = f"发送通知失败: {str(e)}"
215
+ logging.error(error_msg)
216
+ return {"code": 500, "msg": error_msg}
217
+
218
+ def health_check(self) -> Dict[str, Any]:
219
+ """健康检查"""
220
+ try:
221
+ url = self.get_service_url("/health")
222
+ response = requests.get(url, timeout=5)
223
+ return response.json()
224
+ except Exception as e:
225
+ return {"code": 500, "msg": f"健康检查失败: {str(e)}"}
226
+
227
+
228
+ def generate_response(self, question,template_id, variables):
229
+ response = requests.post(
230
+ f'{self.base_url}/dynamic-response',
231
+ json={
232
+ 'question':question,
233
+ 'template_id': template_id,
234
+ 'variables': variables
235
+ },
236
+ headers={'Authorization': f'Bearer {self.access_token}'}
237
+ )
238
+ if response.status_code == 200:
239
+ return response.json().get('response')
240
+ else:
241
+ return response.json()
242
+ # raise Exception('Failed to generate response')
243
+
244
+ def query_data(self, arg, template_id):
245
+ response = requests.post(
246
+ f'{self.base_url}/query-data',
247
+ json={
248
+ 'arg': arg,
249
+ 'template_id': template_id
250
+ },
251
+ headers={'Authorization': f'Bearer {self.access_token}'}
252
+ )
253
+ if response.status_code == 200:
254
+ return response.json()
255
+ else:
256
+ res=response.json()
257
+ raise Exception(res['error'])
258
+
259
+
260
+ def query_intention(self, question):
261
+ response = requests.post(
262
+ f'{self.base_url}/query-intention',
263
+ json={
264
+ 'question': question
265
+
266
+ },
267
+ headers={'Authorization': f'Bearer {self.access_token}'}
268
+ )
269
+ if response.status_code == 200:
270
+ return response.json()
271
+ else:
272
+
273
+ raise Exception('Failed to start intention query')
274
+
275
+ def voc(self,productname,text):
276
+ response = requests.post(
277
+ f'{self.base_url}/voc',
278
+ json={
279
+ 'productname': productname,
280
+ 'text':text
281
+ },
282
+ headers={'Authorization': f'Bearer {self.access_token}'}
283
+ )
284
+ if response.status_code == 200:
285
+ return response.json()
286
+ else:
287
+ raise Exception('Failed to start voc ')
288
+
289
+
290
+ def spec(self,asin):
291
+ response = requests.post(
292
+ f'{self.base_url}/spec',
293
+ json={
294
+ 'asin': asin
295
+
296
+ },
297
+ headers={'Authorization': f'Bearer {self.access_token}'}
298
+ )
299
+ if response.status_code == 200:
300
+ return response.json()
301
+ else:
302
+ res=response.json()
303
+ raise Exception(f'Failed to start voc,reason:{res["error"]}')
304
+
305
+ #选品建议
306
+ def suggestion(self, selected_products):
307
+ response = requests.post(
308
+ f'{self.base_url}/suggestion',
309
+ json={
310
+ "final_selected_products": selected_products
311
+
312
+ },
313
+ headers={'Authorization': f'Bearer {self.access_token}'}
314
+ )
315
+ if response.status_code == 200:
316
+ return response.json()
317
+ else:
318
+ raise Exception('Failed to start suggestion')
319
+
320
+ async def competitor_analysis(self,missionid="",asins=[]):
321
+ if not missionid: # Check if missionid is empty
322
+ missionid = str(uuid.uuid4()) # Generate a random UUID
323
+
324
+ task_id = await self.get_competitive_data(missionid,asins)
325
+
326
+ # logging.info(task_id)
327
+
328
+ if task_id:
329
+
330
+ task = await self.check_task_status(task_id)
331
+
332
+
333
+ if task["status"] == "SUCCESS" and task['result']['code'] == 200:
334
+
335
+ ret=await self.analysis_details(missionid)
336
+ return ret
337
+ else:
338
+ return {'code': 400, 'data': None, 'msg':task['result']['msg']}
339
+
340
+
341
+ async def analysis_details(self,missionid):
342
+ response = requests.post(
343
+ f'{URL}/v1/conv/analysis/getid',
344
+ json={
345
+ 'missionid': missionid
346
+
347
+ },
348
+ headers={"Content-Type": "application/json"}
349
+ )
350
+ if response.status_code == 200:
351
+ return response.json()
352
+ else:
353
+ raise Exception('Failed to start analysis')
354
+
355
+
356
+ async def get_competitive_data(self,missionid,asins=[]):
357
+
358
+ response = requests.post(
359
+ f'{self.base_url}/competitor_analysis',
360
+ json={
361
+ "asins": asins,
362
+ "missionid":missionid
363
+
364
+ },
365
+ headers={'Authorization': f'Bearer {self.access_token}'}
366
+ )
367
+
368
+ if response.status_code == 200:
369
+
370
+ result=response.json()
371
+
372
+ return result['data']['task_id']
373
+ else:
374
+ raise Exception('Failed to request get_competitive_data')
375
+
376
+
377
+
378
+ #获取所有asins
379
+ async def productList(self,missionid):
380
+ data=await self.get_token()
381
+ print(data['token'])
382
+ response = requests.post(
383
+ f"{URL}/v1/agent/productList",
384
+ json={
385
+ 'missionid':missionid
386
+ },
387
+ headers={'Authorization': f"Bearer {data['token']}"}
388
+ )
389
+
390
+ print(response)
391
+ if response.status_code == 200:
392
+ return response.json()
393
+ else:
394
+ raise Exception('Failed to request task_status')
395
+
396
+
397
+
398
+ async def getTaskStatus(self,taskId):
399
+ data=await self.get_token()
400
+
401
+ response =requests.get(f"{URL}/v1/task/task_status/{taskId}",
402
+ headers={'Authorization': f"Bearer {data['token']}"})
403
+
404
+
405
+ # if response.status_code == 200:
406
+ return response.json()
407
+ # else:
408
+ # raise Exception('Failed to request task_status')
409
+
410
+ #检查竞品分析任务状态
411
+ async def check_task_status(self,task_id):
412
+ print("start")
413
+
414
+ while True:
415
+ try:
416
+
417
+ response = await self.getTaskStatus(task_id)
418
+ print(response)
419
+ logging.info(response)
420
+ status = response.get('status')
421
+ logging.info(f"Task status: {status}")
422
+ print("------------------")
423
+ if status == "SUCCESS":
424
+ return {"status": "SUCCESS", "result": response.get('result')}
425
+ else:
426
+ return {"status": "ERROR", "result": {"code":400,"msg":"竞品分析发生错误"}}
427
+
428
+ except Exception as error:
429
+ logging.error(f"Error checking task status: {error}")
430
+
431
+ await asyncio.sleep(1) # Sleep for 1 second
432
+
433
+ #获取asin 列表
434
+ async def getAsinfromAmazon(self,title,keywords):
435
+ missionid = str(uuid.uuid4())
436
+ res=await self.update_mission("app_"+missionid, title,keywords)
437
+ print(res)
438
+
439
+
440
+ response=self.notify("list",{"keywords":keywords,"missionid":"app_"+missionid})
441
+ if self.local:
442
+ return response
443
+ else:
444
+ res=await self.start_checking("app_"+missionid)
445
+ print(res)
446
+ if res['status']=="Productlist_complete":
447
+ return {"success": True,"missionid":"app_"+missionid}
448
+ #获取asin 详情
449
+ async def getGoodinfofromAmazon(self, asins,missionid=None,filename=None):
450
+ if filename:
451
+ locafile,filename=self.upload_file(filename)
452
+ print(filename)
453
+
454
+
455
+ response=self.create_asin_mission(locafile,filename)
456
+ asins=response['asins']
457
+ missionid=response['missionid']
458
+
459
+
460
+
461
+ if missionid is None:
462
+ missionid = str(uuid.uuid4())
463
+ res=await self.update_mission("app_"+missionid,"获取asinlist" ,"无关键词")
464
+ print(res)
465
+
466
+ response=self.notify("asin",{"asins": asins,"missionid":"app_"+missionid})
467
+
468
+ if self.local:
469
+ return response
470
+ else:
471
+ res=await self.start_checking("app_"+missionid)
472
+ print(res)
473
+ if res['status']=="Completed":
474
+ return {"success": True,"missionid":"app_"+missionid}
475
+
476
+ #检查任务状态
477
+ async def check_mission_status(self, task_id):
478
+ print("start")
479
+
480
+ # 重置停止标志
481
+ self._should_stop = False
482
+
483
+ while True:
484
+ # 检查是否应该停止
485
+ if self._should_stop:
486
+ print("任务检查器被停止")
487
+ return {"status": "stopped", "result": {"code": 400, "msg": "任务检查被手动停止"}}
488
+
489
+ try:
490
+ response = await self.findMissionbyid(task_id)
491
+ print(response)
492
+ logging.info(response)
493
+ status = response.get('task_status')
494
+ logging.info(f"Task status: {status}")
495
+ print("------------------")
496
+
497
+ if status == "Completed":
498
+ return {"status": status, "result": {"code": 200, "msg": "商品详情数据抓取数据成功"}}
499
+ elif status == "Productlist_complete":
500
+ return {"status": status, "result": {"code": 200, "msg": "商品列表抓取数据成功"}}
501
+ else:
502
+ pass
503
+ except Exception as error:
504
+ logging.error(f"Error checking task status: {error}")
505
+
506
+ await asyncio.sleep(1) # Sleep for 1 second
507
+
508
+ # def start_checking(self, task_id):
509
+ # """启动状态检查任务"""
510
+ # # 先停止之前的任务(如果存在)
511
+ # self.stop_checking()
512
+
513
+ # # 重置停止标志
514
+ # self._should_stop = False
515
+
516
+ # # 创建新任务
517
+ # self._task_checker = asyncio.create_task(self.check_mission_status(task_id))
518
+ # return self._task_checker
519
+
520
+ def start_checking(self, task_id):
521
+ """启动状态检查任务"""
522
+ # 先停止之前的任务(如果存在)
523
+ self.stop_checking()
524
+
525
+ # 重置停止标志
526
+ self._should_stop = False
527
+
528
+ # 创建新任务
529
+ self._task_checker = asyncio.create_task(self.check_mission_status(task_id))
530
+ return self._task_checker
531
+
532
+ async def stop_checking(self):
533
+ """停止状态检查任务"""
534
+ # 设置停止标志
535
+ self._should_stop = True
536
+
537
+ # 取消任务(如果存在)
538
+ if self._task_checker and not self._task_checker.done():
539
+ self._task_checker.cancel()
540
+ try:
541
+ await self._task_checker
542
+ except asyncio.CancelledError:
543
+ print("任务检查器已取消")
544
+ pass
545
+ self._task_checker = None
546
+
547
+ # async def check_mission_status(self,task_id):
548
+ # print("start")
549
+
550
+ # while True:
551
+ # try:
552
+
553
+ # response = await self.findMissionbyid(task_id)
554
+ # print(response)
555
+ # logging.info(response)
556
+ # status = response.get('task_status')
557
+ # logging.info(f"Task status: {status}")
558
+ # print("------------------")
559
+ # if status == "Completed":
560
+ # return {"status": status, "result": {"code":200,"msg":"商品详情数据抓取数据成功"}}
561
+ # elif status=="Productlist_complete":
562
+ # return {"status": status, "result": {"code":200,"msg":"商品列表抓取数据成功"}}
563
+ # else:
564
+ # pass
565
+ # except Exception as error:
566
+ # logging.error(f"Error checking task status: {error}")
567
+
568
+ # await asyncio.sleep(1) # Sleep for 1 second
569
+
570
+
571
+
572
+
573
+
574
+ def upload_file(self,file_path):
575
+ url =f"{URL}/v1/task/upload"
576
+ file_name = os.path.basename(file_path)
577
+ with open(file_path, 'rb') as f:
578
+ files = {'file': (file_name, f)}
579
+ response = requests.post(url, files=files)
580
+
581
+ if response.status_code == 200:
582
+ data = response.json()
583
+ print(f"Uploaded filename: {data['filename']}")
584
+ return file_name,data['filename']
585
+ else:
586
+ print(f"Upload failed: {response.text}")
587
+ return None
588
+
589
+
590
+ async def create_asin_mission(self,locafile,filename):
591
+ # Start ASIN extraction process
592
+ task_id = await self.create_asin_mission_api(filename)
593
+
594
+ if task_id:
595
+ task = await self.check_task_status(task_id)
596
+
597
+ if task["status"] == "SUCCESS" and task["result"]["code"] == 200:
598
+ missionid = task["result"]["missionid"]
599
+
600
+ # Generate report title using AI
601
+ gentitle_prompt = f"""
602
+ 以下是选品报告标题的关键要素
603
+ 今天是{datetime.now()}
604
+ 用户了提交了asin 列表,文件名称为{locafile}
605
+ 请生成亚马逊选品分析报告的标题
606
+ """
607
+ response_title = await self.call_ai_api([{"role": "user", "content": gentitle_prompt}])
608
+ title = response_title["data"]
609
+
610
+ # Update mission with generated title
611
+ task_res = await self.update_mission(missionid, title, "无需关键词")
612
+
613
+ if task_res["code"] == 200:
614
+ # Get ASIN list for the mission
615
+ res = await self.get_asin_list(missionid)
616
+
617
+ if res["code"] == 200:
618
+ asinlist = res["data"]
619
+ asins = [item["asin"] for item in asinlist]
620
+ return {"missionid": missionid, "asins": asins}
621
+
622
+
623
+
624
+ # Return None if any step fails
625
+ return None
626
+
627
+
628
+
629
+ # Example implementations of the required service functions
630
+ async def create_asin_mission_api(self,filename: str) -> str:
631
+
632
+ response = requests.post(
633
+ f"{URL}/task/start_task",
634
+ json={
635
+ 'name':"create_asin_mission",
636
+ 'data': {"file_name":filename},
637
+ }
638
+
639
+ )
640
+ """Mock implementation - replace with actual API call"""
641
+ if response.status_code == 200:
642
+ res=response.json()
643
+ return res['task_id']
644
+ else:
645
+ raise Exception('Failed to request task_status')
646
+
647
+
648
+ # async def check_task_status(self,task_id: str) -> dict:
649
+ # response =requests.get(f"{URL}/task/task_status/{task_id}")
650
+ # if response.status_code == 200:
651
+ # ret= response.json()
652
+ # """Mock implementation - replace with actual status check"""
653
+ # return {
654
+ # "status": ret["status"],
655
+ # "result": {
656
+ # "code": 200,
657
+ # "missionid": ret["result"]["missionid"]
658
+ # }
659
+ # }
660
+ # else:
661
+ # raise Exception('Failed to request task_status')
662
+
663
+ async def get_token(self):
664
+
665
+ # response =requests.get(f"{appurl}/api/gettoken")
666
+ # if response.status_code == 200:
667
+ # return response.json()
668
+ # else:
669
+ # raise Exception('Failed to request task_status')
670
+ response =requests.get(f"{URL}/v1/selectproduct/sdklogin")
671
+ if response.status_code == 200:
672
+ return response.json()
673
+ else:
674
+ raise Exception('Failed to request task_status')
675
+
676
+ async def call_ai_api(self,messages,tools):
677
+ data=await self.get_token()
678
+ print(data)
679
+ response = requests.post(
680
+ f"{URL}/agent/fn",
681
+ json={"tools":tools,"messages":messages},
682
+ headers={'Authorization': f"Bearer {data['token']}"}
683
+ )
684
+
685
+ if response.status_code == 200:
686
+ return response.json()
687
+ else:
688
+ raise Exception('Failed to request task_status')
689
+
690
+
691
+ async def update_mission(self,missionid: str, title: str, keywords: str) :
692
+ data=await self.get_token()
693
+ print(data['token'])
694
+ userid=await self._get_userid()
695
+ print(userid)
696
+ response = requests.post(
697
+ f"{URL}/v1/agent/intertMissioinlist",
698
+ json={
699
+ 'userid':userid,
700
+ 'task_id':missionid,
701
+ 'report_title': title,
702
+ 'keywords': keywords,
703
+ 'task_status':'In Progress',
704
+ 'task_status_description':'数据采集任务开始'
705
+ },
706
+ headers={'Authorization': f"Bearer {data['token']}"}
707
+ )
708
+
709
+
710
+ if response.status_code == 200:
711
+ return response.json()
712
+ else:
713
+ raise Exception('Failed to request task_status')
714
+
715
+
716
+ async def findMissionbyid(self,missionid: str):
717
+ data=await self.get_token()
718
+ print(data['token'])
719
+ userid=await self._get_userid()
720
+ print(userid)
721
+ response = requests.post(
722
+ f"{URL}/v1/agent/findMissionbyid",
723
+ json={
724
+ 'task_id':missionid,
725
+ 'user_id':userid
726
+ },
727
+ headers={'Authorization': f"Bearer {data['token']}"}
728
+ )
729
+
730
+ print(response)
731
+ if response.status_code == 200:
732
+ result=response.json()
733
+ print(result)
734
+ if result["code"]==200:
735
+ return result["data"]
736
+ else:
737
+ raise Exception('Failed to request task_status')
738
+
739
+
740
+
741
+ async def get_asin_list(self,missionid: str):
742
+ response = requests.post(
743
+ f"{URL}/conv/getid",
744
+ json={
745
+ 'missionid':missionid}
746
+ )
747
+
748
+
749
+ if response.status_code == 200:
750
+ return response.json()
751
+ else:
752
+ raise Exception('Failed to request task_status')
753
+
754
+
755
+
756
+
757
+
758
+ async def getallpageData(self,missionid: str) :
759
+ data=await self.get_token()
760
+ print(data['token'])
761
+ response = requests.post(
762
+ f"{URL}/v1/agent/test/getallpageData",
763
+ json={
764
+ 'missionid':missionid
765
+ },
766
+ headers={'Authorization': f"Bearer {data['token']}"}
767
+ )
768
+
769
+ print(response)
770
+ if response.status_code == 200:
771
+ return response.json()
772
+ else:
773
+ raise Exception('Failed to request task_status')
774
+
775
+ #保存数据到云
776
+ async def CloudStorage(self,input_data:Any,config: dict={}) :
777
+ data=await self.get_token()
778
+ missionid=str(uuid.uuid4()) # Generate a random UUID
779
+ response = requests.post(
780
+ f"{URL}/v1/conv/analysis/save",
781
+ json={
782
+ 'id':missionid,
783
+ 'data':input_data
784
+ },
785
+ headers={'Authorization': f"Bearer {data['token']}"}
786
+ )
787
+
788
+
789
+ if response.status_code == 200:
790
+ return {"missionid": missionid, "msg":"已经保存完成"}
791
+ else:
792
+ raise Exception('Failed to request task_status')
793
+
794
+
795
+
796
+ #读取数据库
797
+ async def Database(self,input_data: str, config: dict={}) -> Optional[object]:
798
+ """
799
+ 执行 MySQL 查询并返回包含 database-source 属性的对象
800
+ 只支持 SELECT 查询,自动限制最多1000条数据
801
+ """
802
+ # 从 config 中获取参数
803
+ sql_query = config.get('query', '')
804
+ connection_string = config.get('connectionString', '')
805
+
806
+ if not sql_query:
807
+ print("SQL 查询语句不能为空")
808
+ return None
809
+
810
+ if not connection_string:
811
+ print("数据库连接字符串不能为空")
812
+ return None
813
+
814
+ # 检查是否只支持 SELECT 查询
815
+ sql_upper = sql_query.strip().upper()
816
+ if not sql_upper.startswith('SELECT'):
817
+ print("只支持 SELECT 查询语句")
818
+ return None
819
+
820
+ # 解析连接字符串
821
+ config_dict = {}
822
+ try:
823
+ for param in connection_string.split(';'):
824
+ if '=' in param:
825
+ key, value = param.split('=', 1)
826
+ config_dict[key.strip()] = value.strip()
827
+ except Exception as e:
828
+ print(f"连接字符串解析失败: {e}")
829
+ return None
830
+
831
+ # 建立数据库连接
832
+ connection = None
833
+ try:
834
+ connection = pymysql.connect(
835
+ host=config_dict.get('host', 'localhost'),
836
+ port=int(config_dict.get('port', 3306)),
837
+ user=config_dict.get('user', ''),
838
+ password=config_dict.get('password', ''),
839
+ database=config_dict.get('database', ''),
840
+ charset='utf8mb4',
841
+ cursorclass=pymysql.cursors.DictCursor
842
+ )
843
+
844
+ # 执行查询
845
+ with connection.cursor() as cursor:
846
+ # 检查查询是否已经有 LIMIT 子句
847
+ if 'LIMIT' not in sql_upper:
848
+ # 如果没有 LIMIT,自动添加 LIMIT 1000
849
+ modified_query = sql_query.rstrip(';') + ' LIMIT 1000'
850
+ print(f"自动添加 LIMIT 1000,执行查询: {modified_query}")
851
+ cursor.execute(modified_query)
852
+ else:
853
+ # 如果已经有 LIMIT,检查是否超过1000条
854
+ # 提取 LIMIT 后面的数字
855
+ limit_index = sql_upper.find('LIMIT')
856
+ limit_part = sql_upper[limit_index:].split()
857
+
858
+ if len(limit_part) >= 2:
859
+ try:
860
+ limit_value = int(limit_part[1])
861
+ if limit_value > 1000:
862
+ # 如果限制超过1000,修改为1000
863
+ original_limit = f"LIMIT {limit_value}"
864
+ new_limit = "LIMIT 1000"
865
+ modified_query = sql_query.replace(original_limit, new_limit)
866
+ print(f"限制条数从 {limit_value} 改为 1000,执行查询: {modified_query}")
867
+ cursor.execute(modified_query)
868
+ else:
869
+ # 如果限制在1000以内,直接执行
870
+ cursor.execute(sql_query)
871
+ except ValueError:
872
+ # 如果 LIMIT 参数不是数字,使用默认查询
873
+ cursor.execute(sql_query)
874
+ else:
875
+ # 如果 LIMIT 格式不正确,使用默认查询
876
+ cursor.execute(sql_query)
877
+
878
+ result = cursor.fetchall()
879
+
880
+ # 检查实际返回的数据条数
881
+ actual_count = len(result)
882
+ if actual_count == 1000:
883
+ print("警告:查询结果已达到1000条限制,可能有不完整数据")
884
+
885
+ # JSON 序列化器
886
+ def json_serializer(obj):
887
+ """支持多种数据类型的 JSON 序列化器"""
888
+ if isinstance(obj, (datetime, date)):
889
+ return obj.isoformat()
890
+ elif isinstance(obj, decimal.Decimal):
891
+ return float(obj)
892
+ elif isinstance(obj, (bytes, bytearray)):
893
+ return obj.decode('utf-8', errors='ignore')
894
+ elif isinstance(obj, time):
895
+ return obj.isoformat()
896
+ elif isinstance(obj, timedelta):
897
+ return str(obj)
898
+ else:
899
+ return str(obj)
900
+
901
+ # 转换为 JSON
902
+ json_result = json.dumps(result, ensure_ascii=False, default=json_serializer, indent=2)
903
+
904
+ # 返回包含 database-source 属性的对象
905
+ class DatabaseResult:
906
+ def __init__(self, result):
907
+ self.database_source = result
908
+
909
+ return DatabaseResult(json_result)
910
+
911
+ except pymysql.Error as e:
912
+ print(f"数据库操作失败: {e}")
913
+ return None
914
+ except Exception as e:
915
+ print(f"其他错误: {e}")
916
+ return None
917
+ finally:
918
+ # 确保连接被关闭
919
+ if connection:
920
+ connection.close()
921
+
922
+
923
+
924
+
925
+
926
+
927
+
928
+
929
+
930
+
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.1
2
+ Name: oneliai
3
+ Version: 1.2.7
4
+ Summary: SDK for ONELI.AI Customer API
5
+ Author: oneli.ai
6
+ Requires-Python: >=3.6
7
+ Requires-Dist: requests
8
+
@@ -0,0 +1,8 @@
1
+ oneliai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ oneliai/client.py,sha256=I_h53HA6n8QE2y8QUSk2ffbtSaOaFhu2ReecwYKoJE0,33604
3
+ sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ sdk/client.py,sha256=EenoF_sh_EsovxqLCKfcs0_9mMV1LqC3GnysQupgL1g,4296
5
+ oneliai-1.2.7.dist-info/METADATA,sha256=gMARNnr4rVLDqcroUoeGOsTt8aPVWWoAa9umKUGcQ4I,155
6
+ oneliai-1.2.7.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
7
+ oneliai-1.2.7.dist-info/top_level.txt,sha256=7zBSOMx80pMmYiAfyZoqmb8S5_lloaTESpnFRO3KoYk,12
8
+ oneliai-1.2.7.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.37.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ oneliai
2
+ sdk
sdk/__init__.py ADDED
File without changes
sdk/client.py ADDED
@@ -0,0 +1,129 @@
1
+ import requests
2
+ import jwt
3
+ import datetime
4
+
5
+ # URL="https://apis.oneli.chat"
6
+ URL="http://localhost:8085"
7
+ class AICustomerClient:
8
+ def __init__(self, client_id, client_secret, base_url=f'{URL}/v1/strategy'):
9
+ self.client_id = client_id
10
+ self.client_secret = client_secret
11
+ self.base_url = base_url
12
+ self.access_token = self._get_access_token()
13
+
14
+ def _get_access_token(self):
15
+ response = requests.post(
16
+ f'{self.base_url}/auth/token',
17
+ json={
18
+ 'client_id': self.client_id,
19
+ 'client_secret': self.client_secret,
20
+ 'grant_type': 'client_credentials'
21
+ }
22
+ )
23
+ if response.status_code == 200:
24
+ return response.json().get('access_token')
25
+ else:
26
+ raise Exception('Failed to get access token')
27
+
28
+ def generate_response(self, template_id, variables):
29
+ response = requests.post(
30
+ f'{self.base_url}/dynamic-response',
31
+ json={
32
+ 'template_id': template_id,
33
+ 'variables': variables
34
+ },
35
+ headers={'Authorization': f'Bearer {self.access_token}'}
36
+ )
37
+ if response.status_code == 200:
38
+ return response.json().get('response')
39
+ else:
40
+ return response.json()
41
+ # raise Exception('Failed to generate response')
42
+
43
+ def query_data(self, arg, template_id):
44
+ response = requests.post(
45
+ f'{self.base_url}/query-data',
46
+ json={
47
+ 'arg': arg,
48
+ 'template_id': template_id
49
+ },
50
+ headers={'Authorization': f'Bearer {self.access_token}'}
51
+ )
52
+ if response.status_code == 200:
53
+ return response.json()
54
+ else:
55
+ res=response.json()
56
+ raise Exception(res['error'])
57
+
58
+
59
+ def query_intention(self, question):
60
+ response = requests.post(
61
+ f'{self.base_url}/query-intention',
62
+ json={
63
+ 'question': question
64
+
65
+ },
66
+ headers={'Authorization': f'Bearer {self.access_token}'}
67
+ )
68
+ if response.status_code == 200:
69
+ return response.json()
70
+ else:
71
+ raise Exception('Failed to start intention query')
72
+
73
+ def registEndpoint(self, name,endpointpath):
74
+ response = requests.post(
75
+ f'{self.base_url}/createEndpoints',
76
+ json={
77
+ "endpointpath": endpointpath,
78
+ "method": "POST",
79
+ "name":name
80
+ },
81
+ headers={'Authorization': f'Bearer {self.access_token}'}
82
+ )
83
+ if response.status_code == 200:
84
+ return response.json()
85
+ else:
86
+ raise Exception('Failed to createEndpoints')
87
+
88
+
89
+
90
+ def createRoles(self, role_name, description):
91
+ response = requests.post(
92
+ f'{self.base_url}/createRoles',
93
+ json={
94
+ "role_name": role_name,
95
+ "description":description
96
+ },
97
+ headers={'Authorization': f'Bearer {self.access_token}'}
98
+ )
99
+ if response.status_code == 200:
100
+ return response.json()
101
+ else:
102
+ raise Exception('Failed to createRoles')
103
+
104
+ def roles_endpoint(self, role_id, endpoint_id):
105
+ response = requests.post(
106
+ f'{self.base_url}/roles/{role_id}/endpoints',
107
+ json={
108
+ "endpoint_id": endpoint_id
109
+ },
110
+ headers={'Authorization': f'Bearer {self.access_token}'}
111
+ )
112
+ if response.status_code == 200:
113
+ return response.json()
114
+ else:
115
+ raise Exception('Failed to roles_endpoint')
116
+
117
+
118
+ def user_roles(self, user_id, role_id):
119
+ response = requests.post(
120
+ f'{self.base_url}/users/{user_id}/roles',
121
+ json={
122
+ "role_id": role_id
123
+ },
124
+ headers={'Authorization': f'Bearer {self.access_token}'}
125
+ )
126
+ if response.status_code == 200:
127
+ return response.json()
128
+ else:
129
+ raise Exception('Failed to user_roles')