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.
Files changed (36) hide show
  1. {deva-1.3.1 → deva-1.3.2}/PKG-INFO +1 -1
  2. {deva-1.3.1 → deva-1.3.2}/deva/__init__.py +2 -6
  3. {deva-1.3.1 → deva-1.3.2}/deva/admin.py +186 -34
  4. {deva-1.3.1 → deva-1.3.2}/deva/bus.py +2 -2
  5. {deva-1.3.1 → deva-1.3.2}/deva/core.py +5 -7
  6. {deva-1.3.1 → deva-1.3.2}/deva/gpt.py +98 -64
  7. {deva-1.3.1 → deva-1.3.2}/deva/page.py +0 -2
  8. {deva-1.3.1 → deva-1.3.2}/deva/pipe.py +34 -28
  9. {deva-1.3.1 → deva-1.3.2}/deva/sources.py +472 -195
  10. {deva-1.3.1 → deva-1.3.2}/deva/topic.py +1 -1
  11. {deva-1.3.1 → deva-1.3.2}/deva.egg-info/PKG-INFO +1 -1
  12. {deva-1.3.1 → deva-1.3.2}/deva.egg-info/SOURCES.txt +0 -1
  13. {deva-1.3.1 → deva-1.3.2}/setup.py +1 -1
  14. deva-1.3.1/deva/dask.py +0 -202
  15. {deva-1.3.1 → deva-1.3.2}/README.rst +0 -0
  16. {deva-1.3.1 → deva-1.3.2}/deva/browser.py +0 -0
  17. {deva-1.3.1 → deva-1.3.2}/deva/compute.py +0 -0
  18. {deva-1.3.1 → deva-1.3.2}/deva/endpoints.py +0 -0
  19. {deva-1.3.1 → deva-1.3.2}/deva/future.py +0 -0
  20. {deva-1.3.1 → deva-1.3.2}/deva/graph.py +0 -0
  21. {deva-1.3.1 → deva-1.3.2}/deva/lambdas.py +0 -0
  22. {deva-1.3.1 → deva-1.3.2}/deva/monitor.py +0 -0
  23. {deva-1.3.1 → deva-1.3.2}/deva/namespace.py +0 -0
  24. {deva-1.3.1 → deva-1.3.2}/deva/search.py +0 -0
  25. {deva-1.3.1 → deva-1.3.2}/deva/store.py +0 -0
  26. {deva-1.3.1 → deva-1.3.2}/deva/utils/__init__.py +0 -0
  27. {deva-1.3.1 → deva-1.3.2}/deva/utils/simhash.py +0 -0
  28. {deva-1.3.1 → deva-1.3.2}/deva/utils/sqlitedict.py +0 -0
  29. {deva-1.3.1 → deva-1.3.2}/deva/utils/time.py +0 -0
  30. {deva-1.3.1 → deva-1.3.2}/deva/utils/whooshalchemy.py +0 -0
  31. {deva-1.3.1 → deva-1.3.2}/deva/when.py +0 -0
  32. {deva-1.3.1 → deva-1.3.2}/deva.egg-info/dependency_links.txt +0 -0
  33. {deva-1.3.1 → deva-1.3.2}/deva.egg-info/not-zip-safe +0 -0
  34. {deva-1.3.1 → deva-1.3.2}/deva.egg-info/requires.txt +0 -0
  35. {deva-1.3.1 → deva-1.3.2}/deva.egg-info/top_level.txt +0 -0
  36. {deva-1.3.1 → deva-1.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deva
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: data eval in future
5
5
  Home-page: https://github.com/sostc/deva
6
6
  Author: spark
@@ -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
- __version__ = '1.3.1'
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
- from typing import Callable, Union
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
- os.getpid() >> log
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 = sync_gpt(prompts=prompt)
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: '关于', path: '#', action: () => alert('Deva管理面板 v1.0')}
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='https://fastly.jsdelivr.net/gh/wang0618/PyWebIO-assets@v1.8.3/')),
2044
- (r'/streamadmin', webio_handler(streamadmin, cdn='https://fastly.jsdelivr.net/gh/wang0618/PyWebIO-assets@v1.8.3/')),
2045
- (r'/', webio_handler(main, cdn='https://fastly.jsdelivr.net/gh/wang0618/PyWebIO-assets@v1.8.3/')),
2046
- (r'/taskadmin', webio_handler(taskadmin, cdn='https://fastly.jsdelivr.net/gh/wang0618/PyWebIO-assets@v1.8.3/'))
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 log_print(x):
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(log_print)
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,func):
1042
- return self.catch(func=func)
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类,默认使用deepseek模型,可以选择sambanova
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
- 初始化GPT类,默认使用deepseek模型,可以选择sambanova
97
+ 同步查询大模型
55
98
 
56
99
  参数:
57
- model_type: 模型类型,默认为'deepseek'
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