adam-community 1.0.23__py3-none-any.whl → 1.0.24__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.
@@ -1 +1 @@
1
- __version__ = "1.0.23"
1
+ __version__ = "1.0.24"
adam_community/util.py CHANGED
@@ -10,6 +10,7 @@ import logging
10
10
  import ssl
11
11
  from functools import wraps
12
12
  from subprocess import run, CalledProcessError
13
+ import time
13
14
 
14
15
  logger = logging.getLogger(__name__)
15
16
 
@@ -18,6 +19,7 @@ ADAM_API_TOKEN = os.getenv('ADAM_API_TOKEN')
18
19
  ADAM_TASK_ID = os.getenv('ADAM_TASK_ID')
19
20
  ADAM_USER_ID = os.getenv('ADAM_USER_ID')
20
21
  CONDA_ENV = os.getenv('CONDA_ENV')
22
+ ADAM_TASK_DIR = os.getenv('ADAM_TASK_DIR')
21
23
 
22
24
 
23
25
  def _build_akb_query_command(query: str, collection: str) -> str:
@@ -368,3 +370,195 @@ def markdown_terminal(content, conda_env="base", user="Adam", workdir=""):
368
370
  user = markdown_color(f"{user}@Adam", "green")
369
371
  workdir = markdown_color(f":~/{workdir}", "blue")
370
372
  return f'({conda_env}) {user}{workdir}$ {content}'
373
+
374
+
375
+ # ========== States Management ==========
376
+
377
+ class _StatesManager:
378
+ """内部状态管理类"""
379
+ _tool_name = "tool"
380
+
381
+ def _get_states_file(self) -> str:
382
+ """获取 states.json 文件路径"""
383
+ # 1. 优先使用环境变量
384
+ task_dir = ADAM_TASK_DIR
385
+ if task_dir:
386
+ return os.path.join(task_dir, ".slurm", "states.json")
387
+
388
+ # 2. 尝试当前目录的 .slurm
389
+ current_dir = os.getcwd()
390
+ local_states = os.path.join(current_dir, ".slurm", "states.json")
391
+ if os.path.exists(local_states):
392
+ return local_states
393
+
394
+ # 3. 在当前目录创建
395
+ return local_states
396
+
397
+ def _read(self) -> dict:
398
+ """读取 states.json,保留所有来源的数据"""
399
+ states_file = self._get_states_file()
400
+ if not os.path.exists(states_file):
401
+ return {
402
+ "files": {"updated_at": None, "sources": {}},
403
+ "states": {"source": None, "updated_at": None, "data": {}}
404
+ }
405
+ try:
406
+ with open(states_file, 'r', encoding='utf-8') as f:
407
+ return json.load(f)
408
+ except (json.JSONDecodeError, IOError):
409
+ return {
410
+ "files": {"updated_at": None, "sources": {}},
411
+ "states": {"source": None, "updated_at": None, "data": {}}
412
+ }
413
+
414
+ def _write(self, data: dict):
415
+ """写入 states.json"""
416
+ states_file = self._get_states_file()
417
+ os.makedirs(os.path.dirname(states_file), exist_ok=True)
418
+ with open(states_file, 'w', encoding='utf-8') as f:
419
+ json.dump(data, f, ensure_ascii=False, indent=2)
420
+
421
+ def _set_nested(self, data: dict, key: str, value):
422
+ """设置嵌套值,支持 dot notation"""
423
+ keys = key.split('.')
424
+ current = data
425
+ for k in keys[:-1]:
426
+ current = current.setdefault(k, {})
427
+ current[keys[-1]] = value
428
+
429
+ def _get_nested(self, data: dict, key: str, default=None):
430
+ """获取嵌套值,支持 dot notation"""
431
+ keys = key.split('.')
432
+ current = data
433
+ for k in keys:
434
+ if isinstance(current, dict):
435
+ current = current.get(k)
436
+ else:
437
+ return default
438
+ if current is None:
439
+ return default
440
+ return current
441
+
442
+ def set(self, key: str, value):
443
+ """
444
+ 设置状态
445
+
446
+ 特殊 key:
447
+ "files" - 记录文件列表,会自动补充到 files.sources[tool_name]
448
+ 其他 - 记录到 states.data
449
+ """
450
+ data = self._read()
451
+
452
+ if key == "files":
453
+ data["files"]["sources"][self._tool_name] = {
454
+ "updated_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
455
+ "items": value
456
+ }
457
+ else:
458
+ self._set_nested(data["states"]["data"], key, value)
459
+
460
+ self._write(data)
461
+
462
+ def get(self, key: str):
463
+ """
464
+ 获取状态
465
+
466
+ Returns:
467
+ 状态值,不存在返回 None
468
+ """
469
+ data = self._read()
470
+
471
+ if key == "files":
472
+ # 合并所有来源的文件列表
473
+ merged = {}
474
+ for source, content in data["files"]["sources"].items():
475
+ for item in content.get("items", []):
476
+ path = item.get("path")
477
+ if not path:
478
+ continue
479
+ mtime = item.get("mtime", 0)
480
+ if path not in merged or mtime > merged[path]["mtime"]:
481
+ merged[path] = item
482
+ result = list(merged.values())
483
+ # 按 mtime 降序排序
484
+ result.sort(key=lambda x: x.get("mtime", 0), reverse=True)
485
+ return result
486
+
487
+ return self._get_nested(data["states"]["data"], key)
488
+
489
+ def cleanup(self):
490
+ """清理当前工具的所有记录"""
491
+ data = self._read()
492
+ data["files"]["sources"].pop(self._tool_name, None)
493
+ self._write(data)
494
+
495
+ def trackPath(self, path: str, max_items: int = 30):
496
+ """
497
+ 追踪文件/目录,自动检测文件信息并记录
498
+
499
+ Args:
500
+ path: 文件或目录路径
501
+ max_items: 最大记录数量,先进先出
502
+ """
503
+ if not os.path.exists(path):
504
+ return
505
+
506
+ # 检测文件信息
507
+ try:
508
+ is_dir = os.path.isdir(path)
509
+ mtime = int(os.path.getmtime(path))
510
+ # 从环境变量获取 task_dir
511
+ task_dir = os.getenv('ADAM_TASK_DIR')
512
+ if task_dir:
513
+ rel_path = os.path.relpath(path, task_dir)
514
+ else:
515
+ rel_path = path
516
+ except (OSError, ValueError):
517
+ return
518
+
519
+ # 获取当前文件列表
520
+ data = self._read()
521
+ source = self._tool_name
522
+ items = data["files"]["sources"].get(source, {}).get("items", [])
523
+
524
+ # 检查是否已存在相同路径,存在则移除(后面会加到末尾,保持最新追踪的在最后)
525
+ items = [item for item in items if item.get("path") != rel_path]
526
+
527
+ # 添加到末尾(最新的位置)
528
+ items.append({"path": rel_path, "is_dir": is_dir, "mtime": mtime})
529
+
530
+ # 先进先出,保留最多 max_items 条
531
+ if len(items) > max_items:
532
+ items = items[-max_items:]
533
+
534
+ # 写回
535
+ data["files"]["sources"][source] = {
536
+ "updated_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
537
+ "items": items
538
+ }
539
+ self._write(data)
540
+
541
+
542
+ # 全局实例
543
+ _states = _StatesManager()
544
+
545
+
546
+ def setState(key: str, value):
547
+ """设置状态"""
548
+ return _states.set(key, value)
549
+
550
+
551
+ def getState(key: str):
552
+ """获取状态"""
553
+ return _states.get(key)
554
+
555
+
556
+ def trackPath(path: str, max_items: int = 30):
557
+ """
558
+ 追踪文件/目录,自动检测文件信息并记录
559
+
560
+ Args:
561
+ path: 文件或目录路径
562
+ max_items: 最大记录数量,先进先出,默认 30
563
+ """
564
+ return _states.trackPath(path, max_items)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: adam_community
3
- Version: 1.0.23
3
+ Version: 1.0.24
4
4
  Summary: Adam Community Tools and Utilities
5
5
  Home-page: https://github.com/yourusername/adam-community
6
6
  Author: Adam Community
@@ -80,6 +80,35 @@ classes = parse_directory(Path("./"))
80
80
  success, errors, zip_name = build_package(Path("./"))
81
81
  ```
82
82
 
83
+ ### States Management(任务状态管理)
84
+
85
+ 用于在任务执行过程中记录和读取状态,与服务端共享 `states.json` 文件。
86
+
87
+ ```python
88
+ from adam_community.util import setState, getState, trackPath
89
+
90
+ # 记录文件列表(自动与服务端和其他 Tool 的文件合并)
91
+ setState("files", [
92
+ {"path": "output/result.json", "is_dir": False, "mtime": 1704100800},
93
+ {"path": "cache", "is_dir": True, "mtime": 1704100000}
94
+ ])
95
+
96
+ # 获取合并后的文件列表(来自 server + 所有 tools)
97
+ files = getState("files")
98
+
99
+ # 记录自定义状态(支持嵌套 key)
100
+ setState("stage", "data_cleaning")
101
+ setState("config.threshold", 0.5)
102
+
103
+ # 获取状态
104
+ stage = getState("stage") # -> "data_cleaning"
105
+ threshold = getState("config.threshold") # -> 0.5
106
+
107
+ # 追踪文件/目录(自动检测 is_dir 和 mtime,先进先出,最多 30 条)
108
+ trackPath("/path/to/output/result.json")
109
+ trackPath("/path/to/cache")
110
+ ```
111
+
83
112
  ## 功能特性
84
113
 
85
114
  - **Python 文件解析**: 自动解析 Python 类和函数的文档字符串
@@ -1,7 +1,7 @@
1
1
  adam_community/__init__.py,sha256=vAmF9VQR6D4peppH0hnrHDmZK5cFeFPh11GIsTKUXhE,429
2
- adam_community/__version__.py,sha256=TsfOxJ6mtxlNs5o5L8PelYBmdcIU2HBCYiJjWJmW9pU,23
2
+ adam_community/__version__.py,sha256=rnKYfyIc2hXBxsPKz6c7OchEPYsBq3xHKgj-1s8ShT8,23
3
3
  adam_community/tool.py,sha256=F6jxRU3urqTfgjLIZSW-hVWyj0FpNwvY65jOODXI19w,4954
4
- adam_community/util.py,sha256=VJVXOWbnwmYAL3K0i4X_yItdmxEnC-IgyuVW74kTU4U,11664
4
+ adam_community/util.py,sha256=SDXnWLHLfJxEJ5WBnwNq_FeHNwXxGK_F36dpI_hVkNc,17766
5
5
  adam_community/cli/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
6
  adam_community/cli/build.py,sha256=fCteEXjtU2ZsXXUWa-j61gh_JVU-6xSAH_LpOP5swFc,12623
7
7
  adam_community/cli/cli.py,sha256=xlPvn0aq3TpAPjA7z9shsCqR1aHDVtZuhvtc_SNA_cQ,2811
@@ -25,8 +25,8 @@ adam_community/cli/templates/long_description.md.j2,sha256=Rj6hcuNzEL0Sp17GQVCRJ
25
25
  adam_community/cli/templates/long_description_en.md.j2,sha256=xSbahwGarXlWopZqHw7lrcv1dQuvwj2ChhZv5pJmUy4,1725
26
26
  adam_community/cli/templates/rag_python.py.j2,sha256=YJL7-WIx-Dumt7lHuUGxl3Rbaw0kpkh8hpcCJ5lz9lA,2494
27
27
  adam_community/cli/templates/toolbox_python.py.j2,sha256=EOnmsJUvQRrcO7K7c88kI42gMmcM0Z4ab46qwOJXbH8,4192
28
- adam_community-1.0.23.dist-info/METADATA,sha256=o6xvoDxorc4zgN6epTHlX8RUkk8ebPQxSMsMN_LOO5k,2202
29
- adam_community-1.0.23.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
30
- adam_community-1.0.23.dist-info/entry_points.txt,sha256=4I7yRkn7cHwPY8-fWQLeAvKjc24zUy8Z65VsZNs0Wos,56
31
- adam_community-1.0.23.dist-info/top_level.txt,sha256=MS8jbePXKZChih9kGizNVX0I1MFZFGWBMCIW_r86qhU,15
32
- adam_community-1.0.23.dist-info/RECORD,,
28
+ adam_community-1.0.24.dist-info/METADATA,sha256=sxFaC3OQBjjOuHER7x6kcuYZxgV6pwqRlevJ-rVa800,3151
29
+ adam_community-1.0.24.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
30
+ adam_community-1.0.24.dist-info/entry_points.txt,sha256=4I7yRkn7cHwPY8-fWQLeAvKjc24zUy8Z65VsZNs0Wos,56
31
+ adam_community-1.0.24.dist-info/top_level.txt,sha256=MS8jbePXKZChih9kGizNVX0I1MFZFGWBMCIW_r86qhU,15
32
+ adam_community-1.0.24.dist-info/RECORD,,