adam-community 1.0.27__tar.gz → 1.0.29__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 (46) hide show
  1. {adam_community-1.0.27 → adam_community-1.0.29}/PKG-INFO +78 -1
  2. {adam_community-1.0.27 → adam_community-1.0.29}/README.md +77 -0
  3. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/__init__.py +8 -6
  4. adam_community-1.0.29/adam_community/__version__.py +1 -0
  5. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/sif_build.py +13 -48
  6. adam_community-1.0.29/adam_community/util/__init__.py +73 -0
  7. adam_community-1.0.29/adam_community/util/api.py +178 -0
  8. adam_community-1.0.29/adam_community/util/cmd.py +166 -0
  9. adam_community-1.0.29/adam_community/util/config.py +8 -0
  10. adam_community-1.0.29/adam_community/util/markdown.py +8 -0
  11. adam_community-1.0.29/adam_community/util/rag.py +126 -0
  12. adam_community-1.0.29/adam_community/util/state.py +195 -0
  13. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community.egg-info/PKG-INFO +78 -1
  14. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community.egg-info/SOURCES.txt +7 -1
  15. {adam_community-1.0.27 → adam_community-1.0.29}/test/test_util_tool.py +139 -24
  16. adam_community-1.0.27/adam_community/__version__.py +0 -1
  17. adam_community-1.0.27/adam_community/util.py +0 -564
  18. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/__init__.py +0 -0
  19. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/build.py +0 -0
  20. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/cli.py +0 -0
  21. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/init.py +0 -0
  22. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/parser.py +0 -0
  23. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/Makefile.j2 +0 -0
  24. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/README_agent.md.j2 +0 -0
  25. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/README_kit.md.j2 +0 -0
  26. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/__init__.py +0 -0
  27. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/agent_python.py.j2 +0 -0
  28. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/configure.json.j2 +0 -0
  29. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/initial_assistant_message.md.j2 +0 -0
  30. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/initial_assistant_message_en.md.j2 +0 -0
  31. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/initial_system_prompt.md.j2 +0 -0
  32. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/initial_system_prompt_en.md.j2 +0 -0
  33. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/input.json.j2 +0 -0
  34. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/kit_python.py.j2 +0 -0
  35. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/long_description.md.j2 +0 -0
  36. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/long_description_en.md.j2 +0 -0
  37. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/templates/rag_python.py.j2 +0 -0
  38. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/cli/updater.py +0 -0
  39. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community/tool.py +0 -0
  40. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community.egg-info/dependency_links.txt +0 -0
  41. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community.egg-info/entry_points.txt +0 -0
  42. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community.egg-info/requires.txt +0 -0
  43. {adam_community-1.0.27 → adam_community-1.0.29}/adam_community.egg-info/top_level.txt +0 -0
  44. {adam_community-1.0.27 → adam_community-1.0.29}/setup.cfg +0 -0
  45. {adam_community-1.0.27 → adam_community-1.0.29}/setup.py +0 -0
  46. {adam_community-1.0.27 → adam_community-1.0.29}/test/test_sif_upload.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: adam_community
3
- Version: 1.0.27
3
+ Version: 1.0.29
4
4
  Summary: Adam Community Tools and Utilities
5
5
  Home-page: https://github.com/yourusername/adam-community
6
6
  Author: Adam Community
@@ -97,6 +97,83 @@ classes = parse_directory(Path("./"))
97
97
  success, errors, zip_name = build_package(Path("./"))
98
98
  ```
99
99
 
100
+ ### 命令执行
101
+
102
+ #### execCmd - 复杂执行
103
+
104
+ `execCmd` 是新的命令执行函数,支持异常处理、超时控制、实时输出等特性。
105
+
106
+ ```python
107
+ from adam_community import execCmd, CmdResult
108
+ from subprocess import CalledProcessError, TimeoutExpired
109
+
110
+ # 基本用法
111
+ result = execCmd("python train.py")
112
+ print(result.stdout) # 标准输出
113
+ print(result.stderr) # 错误输出
114
+ print(result.returncode) # 退出码
115
+ print(result.duration) # 执行耗时(秒)
116
+
117
+ # 异常处理
118
+ try:
119
+ result = execCmd("python train.py", timeout=3600)
120
+ except TimeoutExpired as e:
121
+ print(f"超时: {e.output}")
122
+ except CalledProcessError as e:
123
+ print(f"失败 (exit {e.returncode}): {e.stderr}")
124
+
125
+ # 实时输出到控制台
126
+ result = execCmd("python train.py", echo=True)
127
+
128
+ # 自定义回调(如写日志、发送到前端)
129
+ result = execCmd(
130
+ "python train.py",
131
+ on_stdout=lambda line: logger.info(f"[OUT] {line}"),
132
+ on_stderr=lambda line: logger.error(f"[ERR] {line}"),
133
+ )
134
+
135
+ # 指定工作目录和环境变量
136
+ result = execCmd(
137
+ "python train.py",
138
+ cwd="/workspace/project",
139
+ env={"CUDA_VISIBLE_DEVICES": "0,1"}, # 合并到当前环境
140
+ )
141
+ ```
142
+
143
+ **参数说明:**
144
+
145
+ | 参数 | 类型 | 默认值 | 说明 |
146
+ |------|------|--------|------|
147
+ | `cmd` | str | - | 要执行的命令 |
148
+ | `timeout` | float | None | 超时秒数,None 表示无限制 |
149
+ | `cwd` | str | None | 工作目录 |
150
+ | `env` | dict | None | 环境变量(合并到当前环境) |
151
+ | `shell` | str | /bin/bash | shell 路径 |
152
+ | `echo` | bool | False | 是否实时打印到控制台 |
153
+ | `on_stdout` | Callable | None | stdout 回调 `(line: str) -> None` |
154
+ | `on_stderr` | Callable | None | stderr 回调 `(line: str) -> None` |
155
+
156
+ **返回值 `CmdResult`:**
157
+
158
+ | 字段 | 类型 | 说明 |
159
+ |------|------|------|
160
+ | `stdout` | str | 完整标准输出 |
161
+ | `stderr` | str | 完整错误输出 |
162
+ | `returncode` | int | 退出码 |
163
+ | `duration` | float | 执行耗时(秒) |
164
+ | `command` | str | 原始命令 |
165
+ | `timed_out` | bool | 是否超时 |
166
+
167
+ #### runCmd - 最简执行
168
+
169
+ `runCmd` 是最简化版命令执行函数,实时输出到控制台,失败时直接退出进程。
170
+
171
+ ```python
172
+ from adam_community import runCmd
173
+
174
+ runCmd("echo 'Hello, World!'")
175
+ ```
176
+
100
177
  ### States Management(任务状态管理)
101
178
 
102
179
  用于在任务执行过程中记录和读取状态,与服务端共享 `states.json` 文件。
@@ -67,6 +67,83 @@ classes = parse_directory(Path("./"))
67
67
  success, errors, zip_name = build_package(Path("./"))
68
68
  ```
69
69
 
70
+ ### 命令执行
71
+
72
+ #### execCmd - 复杂执行
73
+
74
+ `execCmd` 是新的命令执行函数,支持异常处理、超时控制、实时输出等特性。
75
+
76
+ ```python
77
+ from adam_community import execCmd, CmdResult
78
+ from subprocess import CalledProcessError, TimeoutExpired
79
+
80
+ # 基本用法
81
+ result = execCmd("python train.py")
82
+ print(result.stdout) # 标准输出
83
+ print(result.stderr) # 错误输出
84
+ print(result.returncode) # 退出码
85
+ print(result.duration) # 执行耗时(秒)
86
+
87
+ # 异常处理
88
+ try:
89
+ result = execCmd("python train.py", timeout=3600)
90
+ except TimeoutExpired as e:
91
+ print(f"超时: {e.output}")
92
+ except CalledProcessError as e:
93
+ print(f"失败 (exit {e.returncode}): {e.stderr}")
94
+
95
+ # 实时输出到控制台
96
+ result = execCmd("python train.py", echo=True)
97
+
98
+ # 自定义回调(如写日志、发送到前端)
99
+ result = execCmd(
100
+ "python train.py",
101
+ on_stdout=lambda line: logger.info(f"[OUT] {line}"),
102
+ on_stderr=lambda line: logger.error(f"[ERR] {line}"),
103
+ )
104
+
105
+ # 指定工作目录和环境变量
106
+ result = execCmd(
107
+ "python train.py",
108
+ cwd="/workspace/project",
109
+ env={"CUDA_VISIBLE_DEVICES": "0,1"}, # 合并到当前环境
110
+ )
111
+ ```
112
+
113
+ **参数说明:**
114
+
115
+ | 参数 | 类型 | 默认值 | 说明 |
116
+ |------|------|--------|------|
117
+ | `cmd` | str | - | 要执行的命令 |
118
+ | `timeout` | float | None | 超时秒数,None 表示无限制 |
119
+ | `cwd` | str | None | 工作目录 |
120
+ | `env` | dict | None | 环境变量(合并到当前环境) |
121
+ | `shell` | str | /bin/bash | shell 路径 |
122
+ | `echo` | bool | False | 是否实时打印到控制台 |
123
+ | `on_stdout` | Callable | None | stdout 回调 `(line: str) -> None` |
124
+ | `on_stderr` | Callable | None | stderr 回调 `(line: str) -> None` |
125
+
126
+ **返回值 `CmdResult`:**
127
+
128
+ | 字段 | 类型 | 说明 |
129
+ |------|------|------|
130
+ | `stdout` | str | 完整标准输出 |
131
+ | `stderr` | str | 完整错误输出 |
132
+ | `returncode` | int | 退出码 |
133
+ | `duration` | float | 执行耗时(秒) |
134
+ | `command` | str | 原始命令 |
135
+ | `timed_out` | bool | 是否超时 |
136
+
137
+ #### runCmd - 最简执行
138
+
139
+ `runCmd` 是最简化版命令执行函数,实时输出到控制台,失败时直接退出进程。
140
+
141
+ ```python
142
+ from adam_community import runCmd
143
+
144
+ runCmd("echo 'Hello, World!'")
145
+ ```
146
+
70
147
  ### States Management(任务状态管理)
71
148
 
72
149
  用于在任务执行过程中记录和读取状态,与服务端共享 `states.json` 文件。
@@ -1,15 +1,17 @@
1
- from .util import messageSend, knowledgeSearch, completionCreate, runCmd
1
+ from .util import messageSend, knowledgeSearch, completionCreate, runCmd, execCmd, CmdResult
2
2
  from .tool import Tool
3
3
  from .cli.parser import parse_python_file, parse_directory, convert_python_type_to_json_schema
4
4
  from .__version__ import __version__
5
5
 
6
6
  __all__ = [
7
- 'Tool',
8
- 'messageSend',
9
- 'knowledgeSearch',
10
- 'completionCreate',
7
+ 'Tool',
8
+ 'messageSend',
9
+ 'knowledgeSearch',
10
+ 'completionCreate',
11
11
  'runCmd',
12
+ 'execCmd',
13
+ 'CmdResult',
12
14
  'parse_python_file',
13
- 'parse_directory',
15
+ 'parse_directory',
14
16
  'convert_python_type_to_json_schema'
15
17
  ]
@@ -0,0 +1 @@
1
+ __version__ = "1.0.29"
@@ -410,18 +410,11 @@ def sif():
410
410
  @click.argument('image_url')
411
411
  @click.option('--username', help='Docker 仓库用户名')
412
412
  @click.option('--password', help='Docker 仓库密码')
413
- @click.option('--chunk-size', help='覆盖自适应计算的切片大小(如 100M, 500M)')
414
413
  @click.option('--keep-temp', is_flag=True, help='保留临时文件')
415
- def upload(sif_file, image_url, username, password, chunk_size, keep_temp):
416
- """将 SIF 文件切片并构建 Docker 镜像推送到仓库
414
+ def upload(sif_file, image_url, username, password, keep_temp):
415
+ """将 SIF 文件打包为 Docker 镜像并推送到仓库
417
416
 
418
- 切片策略:
419
- - 文件 < 500MB:不切片,直接使用原文件
420
- - 文件 500MB-2GB:切片大小 100MB
421
- - 文件 2GB-10GB:切片大小 500MB
422
- - 文件 > 10GB:切片大小 1GB
423
-
424
- 可通过 --chunk-size 参数覆盖自适应策略
417
+ 直接使用完整的 SIF 文件,不进行切片,避免合并问题。
425
418
 
426
419
  示例:
427
420
  adam-cli sif upload ./xxx.sif xxx.cn-hangzhou.cr.aliyuncs.com/openscore/openscore-core:1.0.0
@@ -444,7 +437,7 @@ def upload(sif_file, image_url, username, password, chunk_size, keep_temp):
444
437
 
445
438
  try:
446
439
  # ===== 步骤 1: 验证环境 =====
447
- console.print("\n[bold blue]📦 步骤 1/5: 验证环境[/bold blue]")
440
+ console.print("\n[bold blue]📦 步骤 1/4: 验证环境[/bold blue]")
448
441
 
449
442
  # 验证 SIF 文件
450
443
  valid, error_msg = validateSifFile(sif_path)
@@ -475,51 +468,24 @@ def upload(sif_file, image_url, username, password, chunk_size, keep_temp):
475
468
  border_style="red"
476
469
  ))
477
470
  sys.exit(1)
478
- console.print(" ✓ split 命令可用")
479
471
  console.print(" ✓ Docker 已安装并运行")
480
472
 
481
473
  # ===== 步骤 2: 创建工作目录 =====
482
- console.print("\n[bold blue]📦 步骤 2/5: 创建工作目录[/bold blue]")
474
+ console.print("\n[bold blue]📦 步骤 2/4: 创建工作目录[/bold blue]")
483
475
  work_dir = createWorkDir(sif_path)
484
476
  console.print(f" ✓ 工作目录: {work_dir}")
485
477
 
486
- # ===== 步骤 3: 切片 SIF 文件 =====
487
- console.print("\n[bold blue]📦 步骤 3/5: 切片 SIF 文件[/bold blue]")
488
-
489
- # 计算切片大小
490
- if chunk_size:
491
- actual_chunk_size = chunk_size
492
- console.print(f" 切片策略: 自定义 ({chunk_size})")
493
- else:
494
- actual_chunk_size = calculateOptimalChunkSize(file_size)
495
- if actual_chunk_size:
496
- console.print(f" 切片策略: 自适应 ({actual_chunk_size})")
497
- else:
498
- console.print(f" 切片策略: 不切片 (文件 < 500MB)")
478
+ # ===== 步骤 3: 复制 SIF 文件 =====
479
+ console.print("\n[bold blue]📦 步骤 3/4: 复制 SIF 文件[/bold blue]")
499
480
 
481
+ # 直接复制完整的 SIF 文件,不进行切片
482
+ dest_file = work_dir / sif_path.name
483
+ shutil.copy2(sif_path, dest_file)
484
+ console.print(f" ✓ SIF 文件已复制: {dest_file.name}")
500
485
  console.print(f" 文件大小: {file_size_mb:.2f} MB")
501
486
 
502
- # 执行切片
503
- try:
504
- chunks = splitSifFile(sif_path, actual_chunk_size, work_dir)
505
- console.print(f" ✓ 切片完成: {len(chunks)} 个文件")
506
-
507
- # 显示切片详情
508
- console.print(f"\n 📄 切片详情:")
509
- for chunk in chunks:
510
- chunk_size_mb = chunk.stat().st_size / (1024 * 1024)
511
- console.print(f" - {chunk.name} ({chunk_size_mb:.2f} MB)")
512
- except subprocess.CalledProcessError as e:
513
- console.print(Panel.fit(
514
- f"[bold red]❌ SIF 文件切片失败[/bold red]\n"
515
- f"错误: {e.stderr}",
516
- border_style="red"
517
- ))
518
- cleanupTempFiles(work_dir, keep_temp, console)
519
- sys.exit(1)
520
-
521
487
  # ===== 步骤 4: 生成 Dockerfile =====
522
- console.print("\n[bold blue]📦 步骤 4/5: 生成 Dockerfile[/bold blue]")
488
+ console.print("\n[bold blue]📦 步骤 4/4: 生成 Dockerfile[/bold blue]")
523
489
  dockerfile_path = generateDockerfile(work_dir)
524
490
  console.print(f" ✓ Dockerfile 已生成")
525
491
 
@@ -554,8 +520,7 @@ def upload(sif_file, image_url, username, password, chunk_size, keep_temp):
554
520
  console.print(Panel.fit(
555
521
  f"[bold green]✅ 构建成功![/bold green]\n"
556
522
  f"镜像: {image_url}\n"
557
- f"大小: {file_size_mb:.2f} MB\n"
558
- f"切片数: {len(chunks)}",
523
+ f"大小: {file_size_mb:.2f} MB",
559
524
  border_style="green"
560
525
  ))
561
526
 
@@ -0,0 +1,73 @@
1
+ # Re-export all public APIs to maintain backward compatibility
2
+ # Usage: from adam_community.util import execCmd, runCmd, ...
3
+
4
+ from .config import (
5
+ ADAM_API_HOST,
6
+ ADAM_API_TOKEN,
7
+ ADAM_TASK_ID,
8
+ ADAM_USER_ID,
9
+ CONDA_ENV,
10
+ ADAM_TASK_DIR,
11
+ )
12
+
13
+ from .cmd import (
14
+ runCmd,
15
+ execCmd,
16
+ CmdResult,
17
+ )
18
+
19
+ from .api import (
20
+ retry_on_exception,
21
+ _make_http_request,
22
+ messageSend,
23
+ completionCreate,
24
+ DynamicObject,
25
+ )
26
+
27
+ from .rag import (
28
+ RAG,
29
+ knowledgeSearch,
30
+ )
31
+
32
+ from .state import (
33
+ _StatesManager,
34
+ setState,
35
+ getState,
36
+ trackPath,
37
+ )
38
+
39
+ from .markdown import (
40
+ markdown_color,
41
+ markdown_terminal,
42
+ )
43
+
44
+ __all__ = [
45
+ # config
46
+ 'ADAM_API_HOST',
47
+ 'ADAM_API_TOKEN',
48
+ 'ADAM_TASK_ID',
49
+ 'ADAM_USER_ID',
50
+ 'CONDA_ENV',
51
+ 'ADAM_TASK_DIR',
52
+ # cmd
53
+ 'runCmd',
54
+ 'execCmd',
55
+ 'CmdResult',
56
+ # api
57
+ 'retry_on_exception',
58
+ '_make_http_request',
59
+ 'messageSend',
60
+ 'completionCreate',
61
+ 'DynamicObject',
62
+ # rag
63
+ 'RAG',
64
+ 'knowledgeSearch',
65
+ # state
66
+ '_StatesManager',
67
+ 'setState',
68
+ 'getState',
69
+ 'trackPath',
70
+ # markdown
71
+ 'markdown_color',
72
+ 'markdown_terminal',
73
+ ]
@@ -0,0 +1,178 @@
1
+ import time
2
+ import urllib.request
3
+ import urllib.error
4
+ import json
5
+ import ssl
6
+ from functools import wraps
7
+
8
+ from .config import ADAM_API_HOST, ADAM_API_TOKEN, ADAM_TASK_ID
9
+
10
+
11
+ def retry_on_exception(max_retries=3, delay=5):
12
+ """
13
+ 重试装饰器,在发生异常时最多重试指定次数
14
+
15
+ Args:
16
+ max_retries: 最大重试次数
17
+ delay: 重试间隔时间(秒)
18
+ """
19
+ def decorator(func):
20
+ @wraps(func)
21
+ def wrapper(*args, **kwargs):
22
+ last_exception = None
23
+ for attempt in range(max_retries):
24
+ try:
25
+ return func(*args, **kwargs)
26
+ except Exception as e:
27
+ last_exception = e
28
+ if attempt < max_retries - 1:
29
+ time.sleep(delay)
30
+ continue
31
+ raise last_exception
32
+ return None
33
+ return wrapper
34
+ return decorator
35
+
36
+
37
+ def _make_http_request(url, data, return_json=True):
38
+ """
39
+ 通用的 HTTP POST 请求函数
40
+
41
+ Args:
42
+ url: 请求的 URL
43
+ data: 请求数据(字典格式)
44
+ return_json: 是否将响应解析为 JSON,False 则返回文本
45
+
46
+ Returns:
47
+ 响应数据(JSON 或文本)
48
+ """
49
+ if not ADAM_API_TOKEN:
50
+ raise ValueError("ADAM_API_TOKEN environment variable is not set")
51
+
52
+ headers = {
53
+ "Authorization": f"Bearer {ADAM_API_TOKEN}",
54
+ "Content-Type": "application/json"
55
+ }
56
+
57
+ try:
58
+ # 将请求数据转换为JSON字符串并编码
59
+ json_data = json.dumps(data).encode('utf-8')
60
+
61
+ # 创建请求对象
62
+ req = urllib.request.Request(url, data=json_data, headers=headers, method='POST')
63
+
64
+ context = ssl._create_unverified_context()
65
+
66
+ # 发送请求
67
+ with urllib.request.urlopen(req, context=context) as response:
68
+ # 检查响应状态码
69
+ if response.status >= 400:
70
+ raise Exception(f"HTTP错误: {response.status}")
71
+
72
+ # 读取响应
73
+ response_data = response.read().decode('utf-8')
74
+
75
+ # 根据需要返回 JSON 或文本
76
+ if return_json:
77
+ return json.loads(response_data)
78
+ else:
79
+ return response_data
80
+
81
+ except urllib.error.HTTPError as e:
82
+ raise Exception(f"HTTP请求失败 - HTTP错误 {e.code}: {e.reason}")
83
+ except urllib.error.URLError as e:
84
+ raise Exception(f"HTTP请求失败 - 网络错误: {str(e)}")
85
+ except json.JSONDecodeError as e:
86
+ if return_json:
87
+ raise Exception(f"HTTP请求失败 - JSON解析错误: {str(e)}")
88
+ else:
89
+ raise Exception(f"HTTP请求失败 - 响应解码错误: {str(e)}")
90
+ except Exception as e:
91
+ raise Exception(f"HTTP请求失败: {str(e)}")
92
+
93
+
94
+ def messageSend(message):
95
+ """
96
+ 发送消息给用户
97
+ """
98
+ if not ADAM_API_HOST:
99
+ raise ValueError("ADAM_API_HOST environment variable is not set")
100
+ if not ADAM_TASK_ID:
101
+ raise ValueError("ADAM_TASK_ID environment variable is not set")
102
+
103
+ url = f"{ADAM_API_HOST}/api/task/create_message"
104
+
105
+ request_data = {
106
+ "task_id": ADAM_TASK_ID,
107
+ "message": message,
108
+ "role": "tool"
109
+ }
110
+
111
+ try:
112
+ return _make_http_request(url, request_data, return_json=True)
113
+ except Exception as e:
114
+ raise Exception(f"发送消息失败: {str(e)}")
115
+
116
+
117
+ class DynamicObject:
118
+ """
119
+ 动态对象类,用于处理 JSON 响应
120
+
121
+ 这个类可以动态地将字典转换为对象属性,支持嵌套的字典结构
122
+ 同时保持原始数据的访问能力
123
+ """
124
+ def __init__(self, data):
125
+ self._raw_data = data
126
+ if isinstance(data, dict):
127
+ for key, value in data.items():
128
+ if isinstance(value, (dict, list)):
129
+ setattr(self, key, self._convert_value(value))
130
+ else:
131
+ setattr(self, key, value)
132
+
133
+ def _convert_value(self, value):
134
+ """递归转换嵌套的数据结构"""
135
+ if isinstance(value, dict):
136
+ return DynamicObject(value)
137
+ elif isinstance(value, list):
138
+ return [self._convert_value(item) for item in value]
139
+ return value
140
+
141
+ def __getattr__(self, name):
142
+ """处理未定义的属性访问"""
143
+ return self._raw_data.get(name)
144
+
145
+ def __getitem__(self, key):
146
+ """支持字典式访问"""
147
+ return self._raw_data.get(key)
148
+
149
+ def __str__(self):
150
+ """返回可读的字符串表示"""
151
+ return f"DynamicObject({self._raw_data})"
152
+
153
+ def __repr__(self):
154
+ return self.__str__()
155
+
156
+ def to_dict(self):
157
+ """将对象转换回字典"""
158
+ return self._raw_data
159
+
160
+
161
+ @retry_on_exception(max_retries=3)
162
+ def completionCreate(request_params):
163
+ """
164
+ 创建 openai 的代理函数
165
+
166
+ Returns:
167
+ DynamicObject: 一个动态对象,可以像访问属性一样访问 API 响应的所有字段
168
+ """
169
+ if not ADAM_API_HOST:
170
+ raise ValueError("ADAM_API_HOST environment variable is not set")
171
+
172
+ url = f"{ADAM_API_HOST}/api/chat/completions"
173
+
174
+ try:
175
+ response = _make_http_request(url, request_params, return_json=True)
176
+ return DynamicObject(response)
177
+ except Exception as e:
178
+ raise Exception(f"调用聊天补全接口失败: {str(e)}")