deva 1.3.1__tar.gz → 1.3.2__tar.gz
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.
- {deva-1.3.1 → deva-1.3.2}/PKG-INFO +1 -1
- {deva-1.3.1 → deva-1.3.2}/deva/__init__.py +2 -6
- {deva-1.3.1 → deva-1.3.2}/deva/admin.py +186 -34
- {deva-1.3.1 → deva-1.3.2}/deva/bus.py +2 -2
- {deva-1.3.1 → deva-1.3.2}/deva/core.py +5 -7
- {deva-1.3.1 → deva-1.3.2}/deva/gpt.py +98 -64
- {deva-1.3.1 → deva-1.3.2}/deva/page.py +0 -2
- {deva-1.3.1 → deva-1.3.2}/deva/pipe.py +34 -28
- {deva-1.3.1 → deva-1.3.2}/deva/sources.py +472 -195
- {deva-1.3.1 → deva-1.3.2}/deva/topic.py +1 -1
- {deva-1.3.1 → deva-1.3.2}/deva.egg-info/PKG-INFO +1 -1
- {deva-1.3.1 → deva-1.3.2}/deva.egg-info/SOURCES.txt +0 -1
- {deva-1.3.1 → deva-1.3.2}/setup.py +1 -1
- deva-1.3.1/deva/dask.py +0 -202
- {deva-1.3.1 → deva-1.3.2}/README.rst +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/browser.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/compute.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/endpoints.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/future.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/graph.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/lambdas.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/monitor.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/namespace.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/search.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/store.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/utils/__init__.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/utils/simhash.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/utils/sqlitedict.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/utils/time.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/utils/whooshalchemy.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva/when.py +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva.egg-info/dependency_links.txt +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva.egg-info/not-zip-safe +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva.egg-info/requires.txt +0 -0
- {deva-1.3.1 → deva-1.3.2}/deva.egg-info/top_level.txt +0 -0
- {deva-1.3.1 → deva-1.3.2}/setup.cfg +0 -0
|
@@ -18,11 +18,7 @@ from .lambdas import _
|
|
|
18
18
|
from .browser import browser,tab,tabs
|
|
19
19
|
from .gpt import async_gpt,sync_gpt
|
|
20
20
|
|
|
21
|
-
try:
|
|
22
|
-
# import panel as pn
|
|
23
|
-
from .dask import DaskStream, scatter
|
|
24
21
|
|
|
25
|
-
except ImportError:
|
|
26
|
-
pass
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
|
|
24
|
+
__version__ = '1.3.2'
|
|
@@ -27,44 +27,36 @@ Deva 管理面板 - 基于 PyWebIO 和 Tornado 的 Web 应用程序
|
|
|
27
27
|
# coding: utf-8
|
|
28
28
|
|
|
29
29
|
# In[2]:
|
|
30
|
+
import asyncio
|
|
30
31
|
import os
|
|
31
32
|
import traceback
|
|
33
|
+
import json
|
|
34
|
+
import time
|
|
32
35
|
from urllib.parse import urljoin
|
|
33
|
-
from deva import (
|
|
34
|
-
NW, NB, log, ls, Stream, first, sample, Deva, print, timer,NS,concat, Dtalk
|
|
35
|
-
)
|
|
36
|
-
from deva.browser import browser,tab,tabs
|
|
37
|
-
from deva.bus import warn
|
|
38
|
-
from deva.page import page #这里为了给流注入 webview 方法和sse 方法
|
|
39
|
-
from pywebio.output import (
|
|
40
|
-
put_text, put_markdown, set_scope, put_table,use_scope, clear, toast, put_button, put_collapse, put_datatable,
|
|
41
|
-
put_buttons, put_row, put_html, put_link, popup,close_popup
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
from tornado.web import create_signed_value, decode_signed_value
|
|
45
36
|
from typing import Callable, Union
|
|
46
|
-
from pywebio.platform.tornado import webio_handler
|
|
47
|
-
from pywebio_battery import put_logbox, logbox_append, set_localstorage, get_localstorage
|
|
48
|
-
from pywebio.pin import pin, put_input
|
|
49
|
-
from pywebio.session import set_env, run_async, run_js, run_asyncio_coroutine, get_session_implement
|
|
50
37
|
|
|
51
|
-
from pywebio.input import input, input_group, PASSWORD, textarea, actions, TEXT
|
|
52
38
|
import pandas as pd
|
|
53
|
-
import json
|
|
54
|
-
import time
|
|
55
|
-
|
|
56
39
|
from openai import AsyncOpenAI
|
|
57
40
|
from tornado.web import create_signed_value, decode_signed_value
|
|
58
|
-
|
|
41
|
+
|
|
42
|
+
from deva import (
|
|
43
|
+
NW, NB, log, ls, Stream, first, sample, Deva, print, timer, NS, concat, Dtalk
|
|
44
|
+
)
|
|
45
|
+
from deva.browser import browser, tab, tabs
|
|
46
|
+
from deva.bus import warn
|
|
47
|
+
from deva.page import page # 这里为了给流注入 webview 方法和sse 方法
|
|
48
|
+
from deva.gpt import async_gpt, sync_gpt
|
|
49
|
+
|
|
50
|
+
from pywebio.output import (
|
|
51
|
+
put_text, put_markdown, set_scope, put_table, use_scope, clear, toast,
|
|
52
|
+
put_button, put_collapse, put_datatable, put_buttons, put_row, put_html,
|
|
53
|
+
put_link, popup, close_popup, put_tabs
|
|
54
|
+
)
|
|
59
55
|
from pywebio.platform.tornado import webio_handler
|
|
60
56
|
from pywebio_battery import put_logbox, logbox_append, set_localstorage, get_localstorage
|
|
61
57
|
from pywebio.pin import pin, put_input
|
|
62
58
|
from pywebio.session import set_env, run_async, run_js, run_asyncio_coroutine, get_session_implement
|
|
63
59
|
from pywebio.input import input, input_group, PASSWORD, textarea, actions, TEXT
|
|
64
|
-
import pandas as pd
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
from deva.gpt import async_gpt,sync_gpt
|
|
68
60
|
|
|
69
61
|
|
|
70
62
|
@timer(5,start=False)
|
|
@@ -345,7 +337,7 @@ log.start_cache(200, cache_max_age_seconds=60 * 60 * 24 * 30)
|
|
|
345
337
|
log.map(lambda x: log.recent(10) >> concat('<br>'))\
|
|
346
338
|
>> NS('访问日志', cache_max_len=1, cache_max_age_seconds=60 * 60 * 24 * 30)
|
|
347
339
|
|
|
348
|
-
|
|
340
|
+
|
|
349
341
|
|
|
350
342
|
streams = [NS('访问日志'), NS('实时新闻'), NS(
|
|
351
343
|
'涨跌停'), NS('领涨领跌板块'), NS('1分钟板块异动'), NS('30秒板块异动')]
|
|
@@ -1099,6 +1091,7 @@ async def watch_topic(topic):
|
|
|
1099
1091
|
)
|
|
1100
1092
|
return result
|
|
1101
1093
|
|
|
1094
|
+
|
|
1102
1095
|
async def create_task():
|
|
1103
1096
|
"""创建定时任务"""
|
|
1104
1097
|
|
|
@@ -1136,7 +1129,7 @@ async def create_task():
|
|
|
1136
1129
|
"""
|
|
1137
1130
|
# 调用GPT生成Python代码
|
|
1138
1131
|
prompt = f"仅限于以下的功能和使用方法:{samplecode},根据以下描述: {description},生成一个Python异步函数,只生成函数主体就可以,不需要执行代码,所有 import 都放在函数内部,详细的代码注释"
|
|
1139
|
-
result =
|
|
1132
|
+
result = await asyncio.create_task(async_gpt(prompts=prompt))
|
|
1140
1133
|
def get_python_code_from_deepseek(content):
|
|
1141
1134
|
# 假设返回的内容中 Python 代码被标记为 ```python ... ```
|
|
1142
1135
|
import re
|
|
@@ -1251,9 +1244,9 @@ def manage_tasks():
|
|
|
1251
1244
|
row.append(put_row([
|
|
1252
1245
|
put_button("源码", onclick=lambda n=name: edit_code(n), color="primary"),
|
|
1253
1246
|
put_button("恢复", onclick=lambda n=name: recover_task(n), color="success"),
|
|
1247
|
+
put_button("彻底删除", onclick=lambda n=name: remove_task_forever(n), color="danger"),
|
|
1254
1248
|
]))
|
|
1255
1249
|
deleted_table_data.append(row)
|
|
1256
|
-
|
|
1257
1250
|
if active_table_data:
|
|
1258
1251
|
put_table(
|
|
1259
1252
|
active_table_data,
|
|
@@ -1327,6 +1320,19 @@ def recover_task(name):
|
|
|
1327
1320
|
|
|
1328
1321
|
|
|
1329
1322
|
manage_tasks()
|
|
1323
|
+
|
|
1324
|
+
def remove_task_forever(name):
|
|
1325
|
+
"""彻底删除任务,永久无法恢复"""
|
|
1326
|
+
try:
|
|
1327
|
+
scheduler.remove_job(name)>>log
|
|
1328
|
+
except Exception as e:
|
|
1329
|
+
e>>log
|
|
1330
|
+
del tasks[name]
|
|
1331
|
+
# 更新数据库中的任务状态为已删除
|
|
1332
|
+
db = NB('tasks')
|
|
1333
|
+
del db[name]
|
|
1334
|
+
toast(f"任务 '{name}' 已删除!", color="success")
|
|
1335
|
+
manage_tasks()
|
|
1330
1336
|
|
|
1331
1337
|
async def taskadmin():
|
|
1332
1338
|
await init_admin_ui('Deva任务管理')
|
|
@@ -1346,7 +1352,151 @@ async def streamadmin():
|
|
|
1346
1352
|
put_markdown('### 数据流')
|
|
1347
1353
|
put_buttons([s.name for s in streams],onclick=stream_click)
|
|
1348
1354
|
|
|
1349
|
-
|
|
1355
|
+
async def inspect_object(obj):
|
|
1356
|
+
"""在Web界面上展示Python对象的所有相关信息
|
|
1357
|
+
|
|
1358
|
+
参数:
|
|
1359
|
+
obj: 要展示的Python对象
|
|
1360
|
+
"""
|
|
1361
|
+
# 创建popup内容
|
|
1362
|
+
content = []
|
|
1363
|
+
|
|
1364
|
+
# 显示对象基本信息
|
|
1365
|
+
content.append(put_markdown(f"### 对象信息"))
|
|
1366
|
+
content.append(put_table([
|
|
1367
|
+
['类型', type(obj).__name__],
|
|
1368
|
+
['ID', id(obj)],
|
|
1369
|
+
['哈希值', hash(obj) if not isinstance(obj, dict) else 'N/A (字典不可哈希)'],
|
|
1370
|
+
['可调用', callable(obj)]
|
|
1371
|
+
]))
|
|
1372
|
+
|
|
1373
|
+
# 显示对象文档
|
|
1374
|
+
if obj.__doc__:
|
|
1375
|
+
content.append(put_markdown("#### 文档说明"))
|
|
1376
|
+
if obj.__doc__:
|
|
1377
|
+
# 将文档内容按行分割
|
|
1378
|
+
doc_lines = obj.__doc__.split('\n')
|
|
1379
|
+
formatted_doc = []
|
|
1380
|
+
in_code_block = False
|
|
1381
|
+
|
|
1382
|
+
for line in doc_lines:
|
|
1383
|
+
# 检测代码块开始
|
|
1384
|
+
if line.strip().startswith('>>>') or line.strip().startswith('...'):
|
|
1385
|
+
if not in_code_block:
|
|
1386
|
+
formatted_doc.append('```python')
|
|
1387
|
+
in_code_block = True
|
|
1388
|
+
formatted_doc.append(line)
|
|
1389
|
+
else:
|
|
1390
|
+
if in_code_block:
|
|
1391
|
+
formatted_doc.append('```')
|
|
1392
|
+
in_code_block = False
|
|
1393
|
+
formatted_doc.append(line)
|
|
1394
|
+
|
|
1395
|
+
# 处理最后一个代码块
|
|
1396
|
+
if in_code_block:
|
|
1397
|
+
formatted_doc.append('```')
|
|
1398
|
+
|
|
1399
|
+
# 将格式化后的文档内容添加到显示
|
|
1400
|
+
content.append(put_markdown('\n'.join(formatted_doc)))
|
|
1401
|
+
|
|
1402
|
+
# 显示对象属性
|
|
1403
|
+
content.append(put_markdown("#### 属性"))
|
|
1404
|
+
attrs = []
|
|
1405
|
+
for attr in dir(obj):
|
|
1406
|
+
if not attr.startswith('__'):
|
|
1407
|
+
try:
|
|
1408
|
+
value = getattr(obj, attr)
|
|
1409
|
+
# 过滤掉方法,只显示非可调用属性
|
|
1410
|
+
if not callable(value):
|
|
1411
|
+
attrs.append([attr, type(value).__name__, str(value)[:100]])
|
|
1412
|
+
except Exception as e:
|
|
1413
|
+
attrs.append([attr, '无法访问', str(e)])
|
|
1414
|
+
content.append(put_table([['属性名', '类型', '值']] + attrs))
|
|
1415
|
+
|
|
1416
|
+
# 显示对象方法
|
|
1417
|
+
content.append(put_markdown("#### 方法"))
|
|
1418
|
+
methods = []
|
|
1419
|
+
for attr in dir(obj):
|
|
1420
|
+
if not attr.startswith('__'):
|
|
1421
|
+
try:
|
|
1422
|
+
value = getattr(obj, attr)
|
|
1423
|
+
if callable(value):
|
|
1424
|
+
doc = value.__doc__ or '无文档说明'
|
|
1425
|
+
methods.append([attr, doc[:200]])
|
|
1426
|
+
except Exception as e:
|
|
1427
|
+
methods.append([attr, f'无法访问: {str(e)}'])
|
|
1428
|
+
content.append(put_table([['方法名', '文档说明']] + methods))
|
|
1429
|
+
|
|
1430
|
+
# 在popup中显示内容
|
|
1431
|
+
popup(title="对象详情", content=content, size='large')
|
|
1432
|
+
async def document():
|
|
1433
|
+
await init_admin_ui("Deva管理面板")
|
|
1434
|
+
# 获取deva下所有子模块
|
|
1435
|
+
import deva
|
|
1436
|
+
import pkgutil
|
|
1437
|
+
import inspect
|
|
1438
|
+
|
|
1439
|
+
# 遍历deva下的所有子模块
|
|
1440
|
+
# 创建tab容器
|
|
1441
|
+
tabs = []
|
|
1442
|
+
|
|
1443
|
+
for module_info in pkgutil.iter_modules(deva.__path__):
|
|
1444
|
+
module_name = module_info.name
|
|
1445
|
+
try:
|
|
1446
|
+
# 动态导入模块
|
|
1447
|
+
module = __import__(f'deva.{module_name}', fromlist=['*'])
|
|
1448
|
+
|
|
1449
|
+
# 创建模块内容
|
|
1450
|
+
with use_scope(f'module_{module_name}', clear=True):
|
|
1451
|
+
# 创建类和函数表格
|
|
1452
|
+
module_table = [['名称', '类型', '文档说明']]
|
|
1453
|
+
|
|
1454
|
+
# 获取模块中定义的成员,包括全局变量
|
|
1455
|
+
members = inspect.getmembers(module)
|
|
1456
|
+
|
|
1457
|
+
# 获取模块的全局变量
|
|
1458
|
+
global_vars = {k: v for k, v in module.__dict__.items()
|
|
1459
|
+
if not k.startswith('__') and not inspect.ismodule(v)}
|
|
1460
|
+
|
|
1461
|
+
# 合并成员和全局变量
|
|
1462
|
+
all_objects = {**dict(members), **global_vars}
|
|
1463
|
+
|
|
1464
|
+
# 过滤出类和函数和全局对象
|
|
1465
|
+
for name, obj in all_objects.items():
|
|
1466
|
+
# 检查是否为类、函数、被装饰的函数或全局对象
|
|
1467
|
+
if (inspect.isclass(obj) or inspect.isfunction(obj) or
|
|
1468
|
+
hasattr(obj, '__wrapped__') or
|
|
1469
|
+
(not inspect.ismodule(obj) and not name.startswith('_'))) :
|
|
1470
|
+
doc = obj.__doc__ or '无文档说明'
|
|
1471
|
+
if isinstance(doc, str):
|
|
1472
|
+
# 判断对象类型
|
|
1473
|
+
if inspect.isclass(obj):
|
|
1474
|
+
obj_type = '类'
|
|
1475
|
+
elif inspect.isfunction(obj) or hasattr(obj, '__wrapped__'):
|
|
1476
|
+
obj_type = '函数'
|
|
1477
|
+
else:
|
|
1478
|
+
obj_type = '全局对象'
|
|
1479
|
+
|
|
1480
|
+
# 对于被装饰器修饰的函数,获取原始函数的文档
|
|
1481
|
+
if hasattr(obj, '__wrapped__'):
|
|
1482
|
+
doc = obj.__wrapped__.__doc__ or doc
|
|
1483
|
+
|
|
1484
|
+
# 添加点击事件
|
|
1485
|
+
action_button = put_button(name, onclick=lambda o=obj: run_async(inspect_object(o)))
|
|
1486
|
+
module_table.append([action_button, obj_type, doc[:200]])
|
|
1487
|
+
# 添加模块内容到tab
|
|
1488
|
+
tabs.append({
|
|
1489
|
+
'title': module_name,
|
|
1490
|
+
'content': put_table(module_table)
|
|
1491
|
+
})
|
|
1492
|
+
except ImportError as e:
|
|
1493
|
+
print(f"无法导入模块 {module_name}: {e}")
|
|
1494
|
+
tabs.append({
|
|
1495
|
+
'title': module_name,
|
|
1496
|
+
'content': put_text(f"无法加载模块: {str(e)}")
|
|
1497
|
+
})
|
|
1498
|
+
# 显示所有模块的tab
|
|
1499
|
+
put_tabs(tabs)
|
|
1350
1500
|
async def main():
|
|
1351
1501
|
# await my_timer()
|
|
1352
1502
|
# 这个将会把会话协程卡在这里不动,采用 run_async则不会堵塞
|
|
@@ -1972,7 +2122,7 @@ def create_nav_menu():
|
|
|
1972
2122
|
{name: '数据库', path: '/dbadmin', action: () => window.location.href = '/dbadmin'},
|
|
1973
2123
|
{name: '实时流', path: '/streamadmin', action: () => window.location.href = '/streamadmin'},
|
|
1974
2124
|
{name: '任务', path: '/taskadmin', action: () => window.location.href = '/taskadmin'},
|
|
1975
|
-
{name: '
|
|
2125
|
+
{name: '文档', path: '/document', action: () => window.location.href = '/document'}
|
|
1976
2126
|
];
|
|
1977
2127
|
|
|
1978
2128
|
menuItems.forEach(item => {
|
|
@@ -2039,11 +2189,13 @@ if __name__ == '__main__':
|
|
|
2039
2189
|
# 创建一个名为'stream_webview'的Web服务器实例,监听所有网络接口(0.0.0.0)
|
|
2040
2190
|
# 然后为该服务器添加路由处理器,将'/admin'路径映射到dbadmin处理函数
|
|
2041
2191
|
# 使用PyWebIO的webio_handler进行封装,并指定CDN地址
|
|
2192
|
+
cdn='https://fastly.jsdelivr.net/gh/wang0618/PyWebIO-assets@v1.8.3/'
|
|
2042
2193
|
handlers = [
|
|
2043
|
-
(r'/dbadmin', webio_handler(dbadmin, cdn=
|
|
2044
|
-
(r'/streamadmin', webio_handler(streamadmin, cdn=
|
|
2045
|
-
(r'/', webio_handler(main, cdn=
|
|
2046
|
-
(r'/taskadmin', webio_handler(taskadmin, cdn=
|
|
2194
|
+
(r'/dbadmin', webio_handler(dbadmin, cdn=cdn)),
|
|
2195
|
+
(r'/streamadmin', webio_handler(streamadmin, cdn=cdn)),
|
|
2196
|
+
(r'/', webio_handler(main, cdn=cdn)),
|
|
2197
|
+
(r'/taskadmin', webio_handler(taskadmin, cdn=cdn)),
|
|
2198
|
+
(r'/document', webio_handler(document, cdn=cdn))
|
|
2047
2199
|
]
|
|
2048
2200
|
NW('stream_webview',host='0.0.0.0').application.add_handlers('.*$', handlers)
|
|
2049
2201
|
|
|
@@ -72,7 +72,7 @@ warn = NS('warn')
|
|
|
72
72
|
warn.sink(logging.warning)
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def
|
|
75
|
+
def _log_print(x):
|
|
76
76
|
try:
|
|
77
77
|
# from termcolor import cprint
|
|
78
78
|
# _log_print = (lambda x: cprint(x, 'red', 'on_cyan'))@P
|
|
@@ -86,7 +86,7 @@ def log_print(x):
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
log = NS('log', cache_max_len=10, cache_max_age_seconds=60 * 60 * 24)
|
|
89
|
-
log.sink(
|
|
89
|
+
log.sink(_log_print)
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
bus = NT('bus')
|
|
@@ -787,10 +787,6 @@ class Stream(object):
|
|
|
787
787
|
upstream.downstreams.remove(self)
|
|
788
788
|
self.upstreams.remove(upstream)
|
|
789
789
|
|
|
790
|
-
def scatter(self, **kwargs):
|
|
791
|
-
"""分散流到dask"""
|
|
792
|
-
from .dask import scatter
|
|
793
|
-
return scatter(self, **kwargs)
|
|
794
790
|
|
|
795
791
|
def remove(self, predicate):
|
|
796
792
|
"""只传递predicate返回False的元素"""
|
|
@@ -1038,9 +1034,11 @@ class Stream(object):
|
|
|
1038
1034
|
for handler in self._subscribers[topic]:
|
|
1039
1035
|
handler(message)
|
|
1040
1036
|
|
|
1041
|
-
def __call__(self,
|
|
1042
|
-
|
|
1043
|
-
|
|
1037
|
+
def __call__(self, obj):
|
|
1038
|
+
if callable(obj):
|
|
1039
|
+
return self.catch(func=obj)
|
|
1040
|
+
else:
|
|
1041
|
+
return self.emit(obj)
|
|
1044
1042
|
|
|
1045
1043
|
|
|
1046
1044
|
class Sink(Stream):
|
|
@@ -5,27 +5,69 @@ from deva.namespace import NB
|
|
|
5
5
|
from deva import warn
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
class GPT:
|
|
10
|
+
"""
|
|
11
|
+
GPT类封装了与大型语言模型(如DeepSeek、Sambanova)的交互功能。
|
|
12
|
+
提供同步和异步两种调用方式,支持普通文本和JSON格式的响应。
|
|
13
|
+
|
|
14
|
+
主要功能:
|
|
15
|
+
- 同步查询:sync_query()
|
|
16
|
+
- 异步查询:async_query()
|
|
17
|
+
- 异步JSON查询:async_json_query()
|
|
18
|
+
- 自动模型切换:当某个模型失败时自动切换到备用模型
|
|
19
|
+
- 资源管理:支持上下文管理器自动关闭连接
|
|
20
|
+
|
|
21
|
+
属性:
|
|
22
|
+
- model_type: 当前使用的模型类型(deepseek或sambanova)
|
|
23
|
+
- config: 模型配置信息
|
|
24
|
+
- api_key: API访问密钥
|
|
25
|
+
- base_url: API基础URL
|
|
26
|
+
- model: 模型名称
|
|
27
|
+
- last_used_model: 最后使用的模型类型
|
|
28
|
+
|
|
29
|
+
示例用法:
|
|
30
|
+
1. 同步查询
|
|
31
|
+
>>> gpt = GPT()
|
|
32
|
+
>>> response = gpt.sync_query("你好")
|
|
33
|
+
>>> print(response)
|
|
34
|
+
|
|
35
|
+
2. 异步查询
|
|
36
|
+
>>> async def main():
|
|
37
|
+
... gpt = GPT()
|
|
38
|
+
... response = await gpt.async_query("你好")
|
|
39
|
+
... print(response)
|
|
40
|
+
|
|
41
|
+
3. 使用上下文管理器
|
|
42
|
+
>>> async def main():
|
|
43
|
+
... async with GPT() as gpt:
|
|
44
|
+
... response = await gpt.async_query("你好")
|
|
45
|
+
... print(response)
|
|
46
|
+
|
|
47
|
+
4. JSON格式查询
|
|
48
|
+
>>> async def main():
|
|
49
|
+
... gpt = GPT()
|
|
50
|
+
... json_response = await gpt.async_json_query("返回JSON格式的天气数据")
|
|
51
|
+
... print(json_response)
|
|
52
|
+
"""
|
|
9
53
|
def __init__(self, model_type='deepseek'):
|
|
10
54
|
"""
|
|
11
|
-
初始化GPT
|
|
55
|
+
初始化GPT实例
|
|
12
56
|
|
|
13
57
|
参数:
|
|
14
|
-
model_type: 模型类型,默认为'deepseek'
|
|
58
|
+
model_type (str): 模型类型,默认为'deepseek',可选'sambanova'
|
|
59
|
+
|
|
60
|
+
异常:
|
|
61
|
+
ValueError: 当缺少必要配置项时抛出
|
|
15
62
|
"""
|
|
16
63
|
self.model_type = model_type
|
|
17
|
-
# 从对应类型的数据库获取配置
|
|
18
64
|
self.config = NB(model_type)
|
|
19
65
|
|
|
20
|
-
#
|
|
66
|
+
# 验证配置项
|
|
21
67
|
required_configs = ['api_key', 'base_url', 'model']
|
|
22
|
-
missing_configs = []
|
|
23
|
-
for config in required_configs:
|
|
24
|
-
if config not in self.config:
|
|
25
|
-
missing_configs.append(config)
|
|
68
|
+
missing_configs = [c for c in required_configs if c not in self.config]
|
|
26
69
|
|
|
27
70
|
if missing_configs:
|
|
28
|
-
# 生成配置示例代码
|
|
29
71
|
example_code = "from deva.namespace import NB\n\n"
|
|
30
72
|
example_code += "# 配置示例:\n"
|
|
31
73
|
example_code += "NB('deepseek').update({\n"
|
|
@@ -41,40 +83,28 @@ class GPT:
|
|
|
41
83
|
"请确保在其他地方正确设置这些配置项的值" >> warn
|
|
42
84
|
example_code >> warn
|
|
43
85
|
|
|
44
|
-
# 设置默认值为None
|
|
45
86
|
self.api_key = self.config.get('api_key')
|
|
46
87
|
self.base_url = self.config.get('base_url')
|
|
47
88
|
self.model = self.config.get('model')
|
|
48
|
-
self.last_used_model = model_type
|
|
89
|
+
self.last_used_model = model_type
|
|
49
90
|
|
|
50
|
-
#
|
|
91
|
+
# 初始化客户端
|
|
51
92
|
self.sync_client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
52
93
|
self.async_client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
94
|
+
|
|
95
|
+
def sync_query(self, prompts):
|
|
53
96
|
"""
|
|
54
|
-
|
|
97
|
+
同步查询大模型
|
|
55
98
|
|
|
56
99
|
参数:
|
|
57
|
-
|
|
100
|
+
prompts (str|list): 提示词,可以是字符串或字符串列表
|
|
101
|
+
|
|
102
|
+
返回:
|
|
103
|
+
str: 模型生成的文本
|
|
104
|
+
|
|
105
|
+
异常:
|
|
106
|
+
Exception: 查询失败时抛出,并自动切换模型类型
|
|
58
107
|
"""
|
|
59
|
-
self.model_type = model_type
|
|
60
|
-
# 从对应类型的数据库获取配置
|
|
61
|
-
self.config = NB(model_type)
|
|
62
|
-
|
|
63
|
-
# 验证必要的配置项
|
|
64
|
-
required_configs = ['api_key', 'base_url', 'model']
|
|
65
|
-
for config in required_configs:
|
|
66
|
-
if config not in self.config:
|
|
67
|
-
raise ValueError(f"缺少必要的配置项: {config}")
|
|
68
|
-
|
|
69
|
-
self.api_key = self.config['api_key']
|
|
70
|
-
self.base_url = self.config['base_url']
|
|
71
|
-
self.model = self.config['model']
|
|
72
|
-
|
|
73
|
-
# 初始化同步和异步客户端
|
|
74
|
-
self.sync_client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
75
|
-
self.async_client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
76
|
-
|
|
77
|
-
def sync_query(self, prompts):
|
|
78
108
|
try:
|
|
79
109
|
if isinstance(prompts, str):
|
|
80
110
|
prompts = [prompts]
|
|
@@ -91,23 +121,23 @@ class GPT:
|
|
|
91
121
|
return response.choices[0].message.content
|
|
92
122
|
except Exception as e:
|
|
93
123
|
print(f"同步查询失败: {traceback.format_exc()}")
|
|
94
|
-
|
|
95
|
-
if self.model_type == 'deepseek':
|
|
96
|
-
self.model_type = 'sambanova'
|
|
97
|
-
else:
|
|
98
|
-
self.model_type = 'deepseek'
|
|
99
|
-
self.config = NB(self.model_type)
|
|
100
|
-
self.api_key = self.config['api_key']
|
|
101
|
-
self.base_url = self.config['base_url']
|
|
102
|
-
self.model = self.config['model']
|
|
103
|
-
self.sync_client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
104
|
-
self.async_client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
105
|
-
self.last_used_model = self.model_type # 更新最后使用的模型
|
|
124
|
+
self._switch_model()
|
|
106
125
|
raise
|
|
107
126
|
|
|
108
127
|
async def async_query(self, prompts):
|
|
128
|
+
"""
|
|
129
|
+
异步查询大模型
|
|
130
|
+
|
|
131
|
+
参数:
|
|
132
|
+
prompts (str|list): 提示词,可以是字符串或字符串列表
|
|
133
|
+
|
|
134
|
+
返回:
|
|
135
|
+
str: 模型生成的文本
|
|
136
|
+
|
|
137
|
+
异常:
|
|
138
|
+
Exception: 查询失败时抛出,并自动切换模型类型
|
|
139
|
+
"""
|
|
109
140
|
try:
|
|
110
|
-
# 异常处理
|
|
111
141
|
if isinstance(prompts, str):
|
|
112
142
|
prompts = [prompts]
|
|
113
143
|
|
|
@@ -123,30 +153,22 @@ class GPT:
|
|
|
123
153
|
return completion.choices[0].message.content
|
|
124
154
|
except Exception as e:
|
|
125
155
|
print(f"异步查询失败: {traceback.format_exc()}")
|
|
126
|
-
|
|
127
|
-
if self.model_type == 'deepseek':
|
|
128
|
-
self.model_type = 'sambanova'
|
|
129
|
-
else:
|
|
130
|
-
self.model_type = 'deepseek'
|
|
131
|
-
self.config = NB(self.model_type)
|
|
132
|
-
self.api_key = self.config['api_key']
|
|
133
|
-
self.base_url = self.config['base_url']
|
|
134
|
-
self.model = self.config['model']
|
|
135
|
-
self.sync_client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
136
|
-
self.async_client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
137
|
-
self.last_used_model = self.model_type # 更新最后使用的模型
|
|
156
|
+
self._switch_model()
|
|
138
157
|
raise
|
|
139
158
|
|
|
140
|
-
|
|
141
159
|
async def async_json_query(self, prompts):
|
|
142
160
|
"""
|
|
143
|
-
|
|
161
|
+
异步查询大模型并返回JSON格式结果
|
|
144
162
|
|
|
145
163
|
参数:
|
|
146
|
-
prompts:
|
|
164
|
+
prompts (str|list): 提示词,可以是字符串或字符串列表
|
|
147
165
|
|
|
148
166
|
返回:
|
|
149
|
-
|
|
167
|
+
str: JSON格式的模型响应
|
|
168
|
+
|
|
169
|
+
示例:
|
|
170
|
+
>>> response = await gpt.async_json_query("返回JSON格式的天气数据")
|
|
171
|
+
>>> print(response) # 输出: {"weather": "sunny", "temperature": 25}
|
|
150
172
|
"""
|
|
151
173
|
if isinstance(prompts, str):
|
|
152
174
|
prompts = [prompts]
|
|
@@ -166,16 +188,28 @@ class GPT:
|
|
|
166
188
|
return completion.choices[0].message.content
|
|
167
189
|
|
|
168
190
|
async def close(self):
|
|
169
|
-
"""
|
|
191
|
+
"""关闭客户端连接,释放资源"""
|
|
170
192
|
await self.async_client.close()
|
|
171
193
|
self.sync_client.close()
|
|
172
194
|
|
|
173
195
|
async def __aenter__(self):
|
|
196
|
+
"""上下文管理器入口"""
|
|
174
197
|
return self
|
|
175
198
|
|
|
176
199
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
200
|
+
"""上下文管理器出口,自动关闭连接"""
|
|
177
201
|
await self.close()
|
|
178
|
-
|
|
202
|
+
|
|
203
|
+
def _switch_model(self):
|
|
204
|
+
"""内部方法:在模型失败时切换模型类型"""
|
|
205
|
+
self.model_type = 'sambanova' if self.model_type == 'deepseek' else 'deepseek'
|
|
206
|
+
self.config = NB(self.model_type)
|
|
207
|
+
self.api_key = self.config['api_key']
|
|
208
|
+
self.base_url = self.config['base_url']
|
|
209
|
+
self.model = self.config['model']
|
|
210
|
+
self.sync_client = OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
211
|
+
self.async_client = AsyncOpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
212
|
+
self.last_used_model = self.model_type
|
|
179
213
|
_gpt = GPT()
|
|
180
214
|
async_gpt = _gpt.async_query
|
|
181
215
|
sync_gpt = _gpt.sync_query
|
|
@@ -322,7 +322,6 @@ class PageServer(object):
|
|
|
322
322
|
page: Page实例,包含要添加的路由
|
|
323
323
|
"""
|
|
324
324
|
self.application.add_handlers('.*$', page.get_routes())
|
|
325
|
-
page.get_routes() >> ls >> log
|
|
326
325
|
|
|
327
326
|
def start(self,):
|
|
328
327
|
"""启动Web服务器并在浏览器中打开"""
|
|
@@ -516,7 +515,6 @@ def webview(s, url='/', server=None):
|
|
|
516
515
|
'./templates/streams.html', streams=server.streams[url]))
|
|
517
516
|
server.add_page(page)
|
|
518
517
|
print('start webview:', 'http://'+server.host+':'+str(server.port)+url)
|
|
519
|
-
print('with these streams:', server.streams[url])
|
|
520
518
|
return server
|
|
521
519
|
|
|
522
520
|
|