skillnet-ai 0.0.5__tar.gz → 0.0.6__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.
- {skillnet_ai-0.0.5/src/skillnet_ai.egg-info → skillnet_ai-0.0.6}/PKG-INFO +1 -1
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/pyproject.toml +1 -1
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/evaluator.py +113 -57
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6/src/skillnet_ai.egg-info}/PKG-INFO +1 -1
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/LICENSE +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/README.md +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/setup.cfg +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/__init__.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/analyzer.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/cli.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/client.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/creator.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/downloader.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/models.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/prompts.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai/searcher.py +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai.egg-info/SOURCES.txt +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai.egg-info/dependency_links.txt +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai.egg-info/entry_points.txt +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai.egg-info/requires.txt +0 -0
- {skillnet_ai-0.0.5 → skillnet_ai-0.0.6}/src/skillnet_ai.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "skillnet-ai"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.6"
|
|
8
8
|
description = "Official Python SDK for SkillNet: Create, Evaluate, and Connect AI Skills."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "SkillNet Team", email = "liangyuannnnn@gmail.com" }]
|
|
@@ -5,7 +5,7 @@ import os
|
|
|
5
5
|
import shlex
|
|
6
6
|
import subprocess
|
|
7
7
|
import time
|
|
8
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
8
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from typing import Dict, Any, List, Optional, Tuple, Callable, Iterator
|
|
11
11
|
|
|
@@ -69,6 +69,9 @@ class Skill:
|
|
|
69
69
|
normalized_url = cls._normalize_url(url)
|
|
70
70
|
if not normalized_url:
|
|
71
71
|
return None, f"Invalid GitHub URL: {url}"
|
|
72
|
+
# Derive skill name from URL if not provided
|
|
73
|
+
name = kwargs.get('name') or normalized_url.rstrip('/').split('/')[-1]
|
|
74
|
+
|
|
72
75
|
# Download to local cache (with retries in evaluator)
|
|
73
76
|
local_path = None
|
|
74
77
|
for attempt in range(max_retries):
|
|
@@ -83,8 +86,6 @@ class Skill:
|
|
|
83
86
|
time.sleep(retry_delay)
|
|
84
87
|
if not local_path:
|
|
85
88
|
return None, f"Failed to download after {max_retries} retries: {url}"
|
|
86
|
-
# Derive skill name from URL if not provided
|
|
87
|
-
name = kwargs.get('name') or normalized_url.rstrip('/').split('/')[-1]
|
|
88
89
|
|
|
89
90
|
return cls(
|
|
90
91
|
path=local_path,
|
|
@@ -835,26 +836,55 @@ class SkillEvaluator:
|
|
|
835
836
|
Returns:
|
|
836
837
|
List of evaluation results in the same order as input.
|
|
837
838
|
"""
|
|
838
|
-
results = [None] * len(skills)
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
839
|
+
results: List[Optional[Dict[str, Any]]] = [None] * len(skills)
|
|
840
|
+
|
|
841
|
+
max_workers = max(1, int(self.config.max_workers))
|
|
842
|
+
max_in_flight = max_workers * 2
|
|
843
|
+
|
|
844
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
845
|
+
it = iter(enumerate(skills))
|
|
846
|
+
in_flight: set = set()
|
|
847
|
+
future_to_idx: Dict[Any, int] = {}
|
|
848
|
+
|
|
849
|
+
def _submit_next() -> bool:
|
|
850
|
+
try:
|
|
851
|
+
idx, skill = next(it)
|
|
852
|
+
except StopIteration:
|
|
853
|
+
return False
|
|
854
|
+
fut = executor.submit(self.evaluate, skill)
|
|
855
|
+
in_flight.add(fut)
|
|
856
|
+
future_to_idx[fut] = idx
|
|
857
|
+
return True
|
|
858
|
+
|
|
859
|
+
for _ in range(min(max_in_flight, len(skills))):
|
|
860
|
+
_submit_next()
|
|
861
|
+
|
|
846
862
|
with tqdm(total=len(skills), desc="Evaluating skills") as pbar:
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
863
|
+
try:
|
|
864
|
+
while in_flight:
|
|
865
|
+
done, pending = wait(in_flight, return_when=FIRST_COMPLETED)
|
|
866
|
+
in_flight = set(pending)
|
|
867
|
+
for fut in done:
|
|
868
|
+
idx = future_to_idx.pop(fut)
|
|
869
|
+
results[idx] = fut.result()
|
|
870
|
+
pbar.update(1)
|
|
871
|
+
if len(in_flight) < max_in_flight:
|
|
872
|
+
_submit_next()
|
|
873
|
+
except KeyboardInterrupt:
|
|
874
|
+
for fut in in_flight:
|
|
875
|
+
fut.cancel()
|
|
876
|
+
executor.shutdown(wait=False, cancel_futures=True)
|
|
877
|
+
raise
|
|
878
|
+
|
|
879
|
+
return [r if r is not None else self._create_error_result("Missing result") for r in results]
|
|
853
880
|
|
|
854
881
|
def evaluate_from_url(self, url: str, **kwargs) -> Dict[str, Any]:
|
|
855
882
|
"""Convenience helper: create and evaluate a skill from a URL."""
|
|
856
883
|
skill, err = Skill.from_url(
|
|
857
|
-
url,
|
|
884
|
+
url,
|
|
885
|
+
self.downloader,
|
|
886
|
+
self.config.cache_dir,
|
|
887
|
+
**kwargs
|
|
858
888
|
)
|
|
859
889
|
if err:
|
|
860
890
|
return self._create_error_result(err)
|
|
@@ -896,9 +926,11 @@ if __name__ == '__main__':
|
|
|
896
926
|
parser.add_argument('--output', required=True, help='Output JSONL file')
|
|
897
927
|
parser.add_argument('--api-key', help='OpenAI API key')
|
|
898
928
|
parser.add_argument('--base-url', help='OpenAI API base URL')
|
|
929
|
+
parser.add_argument('--github-token', help='GitHub token for downloading private repos or avoiding rate limits')
|
|
899
930
|
parser.add_argument('--model', default='gpt-4o', help='Model name')
|
|
900
931
|
parser.add_argument('--max-workers', type=int, default=5, help='Max workers')
|
|
901
|
-
parser.add_argument('--cache-dir', default='
|
|
932
|
+
parser.add_argument('--cache-dir', default='skill_downloads', help='Cache directory')
|
|
933
|
+
parser.add_argument('--limit', type=int, default=-1, help='Limit number of input records to process (0 = all)')
|
|
902
934
|
parser.add_argument('--run-scripts', action='store_true',
|
|
903
935
|
help='Execute python scripts under scripts/')
|
|
904
936
|
parser.add_argument('--script-timeout', type=int, default=8,
|
|
@@ -917,15 +949,18 @@ if __name__ == '__main__':
|
|
|
917
949
|
def _load_records(jsonl_path: str) -> List[Dict[str, Any]]:
|
|
918
950
|
with open(jsonl_path, 'r', encoding='utf-8') as f:
|
|
919
951
|
return [json.loads(line) for line in f if line.strip()]
|
|
920
|
-
|
|
921
|
-
def
|
|
952
|
+
|
|
953
|
+
def _evaluate_records_streaming(
|
|
922
954
|
records: List[Dict[str, Any]],
|
|
923
955
|
evaluator: 'SkillEvaluator',
|
|
924
956
|
config: EvaluatorConfig,
|
|
925
|
-
) ->
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
957
|
+
) -> List[Dict[str, Any]]:
|
|
958
|
+
results: List[Optional[Dict[str, Any]]] = [None] * len(records)
|
|
959
|
+
|
|
960
|
+
max_workers = max(1, int(config.max_workers))
|
|
961
|
+
max_in_flight = max_workers * 2
|
|
962
|
+
|
|
963
|
+
def _run_one(idx: int, rec: Dict[str, Any]) -> Dict[str, Any]:
|
|
929
964
|
if 'skill_url' in rec:
|
|
930
965
|
skill, err = Skill.from_url(
|
|
931
966
|
rec['skill_url'],
|
|
@@ -933,43 +968,61 @@ if __name__ == '__main__':
|
|
|
933
968
|
config.cache_dir,
|
|
934
969
|
name=rec.get('skill_name'),
|
|
935
970
|
description=rec.get('skill_description'),
|
|
936
|
-
category=rec.get('category')
|
|
971
|
+
category=rec.get('category'),
|
|
937
972
|
)
|
|
938
|
-
|
|
973
|
+
if err:
|
|
974
|
+
return evaluator._create_error_result(err)
|
|
975
|
+
return evaluator.evaluate(skill)
|
|
976
|
+
|
|
977
|
+
if 'skill_path' in rec:
|
|
939
978
|
skill, err = Skill.from_path(
|
|
940
979
|
rec['skill_path'],
|
|
941
980
|
name=rec.get('skill_name'),
|
|
942
981
|
description=rec.get('skill_description'),
|
|
943
|
-
category=rec.get('category')
|
|
982
|
+
category=rec.get('category'),
|
|
944
983
|
)
|
|
945
|
-
|
|
946
|
-
|
|
984
|
+
if err:
|
|
985
|
+
return evaluator._create_error_result(err)
|
|
986
|
+
return evaluator.evaluate(skill)
|
|
947
987
|
|
|
948
|
-
|
|
949
|
-
errors[idx] = err
|
|
950
|
-
skills.append(None)
|
|
951
|
-
else:
|
|
952
|
-
skills.append(skill)
|
|
953
|
-
return skills, errors
|
|
988
|
+
return evaluator._create_error_result("Record must have 'skill_url' or 'skill_path'")
|
|
954
989
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
990
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
991
|
+
it = iter(enumerate(records))
|
|
992
|
+
in_flight: set = set()
|
|
993
|
+
future_to_idx: Dict[Any, int] = {}
|
|
994
|
+
|
|
995
|
+
def _submit_next() -> bool:
|
|
996
|
+
try:
|
|
997
|
+
idx, rec = next(it)
|
|
998
|
+
except StopIteration:
|
|
999
|
+
return False
|
|
1000
|
+
fut = executor.submit(_run_one, idx, rec)
|
|
1001
|
+
in_flight.add(fut)
|
|
1002
|
+
future_to_idx[fut] = idx
|
|
1003
|
+
return True
|
|
1004
|
+
|
|
1005
|
+
for _ in range(min(max_in_flight, len(records))):
|
|
1006
|
+
_submit_next()
|
|
1007
|
+
|
|
1008
|
+
with tqdm(total=len(records), desc="Downloading & evaluating") as pbar:
|
|
1009
|
+
try:
|
|
1010
|
+
while in_flight:
|
|
1011
|
+
done, pending = wait(in_flight, return_when=FIRST_COMPLETED)
|
|
1012
|
+
in_flight = set(pending)
|
|
1013
|
+
for fut in done:
|
|
1014
|
+
idx = future_to_idx.pop(fut)
|
|
1015
|
+
results[idx] = fut.result()
|
|
1016
|
+
pbar.update(1)
|
|
1017
|
+
if len(in_flight) < max_in_flight:
|
|
1018
|
+
_submit_next()
|
|
1019
|
+
except KeyboardInterrupt:
|
|
1020
|
+
for fut in in_flight:
|
|
1021
|
+
fut.cancel()
|
|
1022
|
+
executor.shutdown(wait=False, cancel_futures=True)
|
|
1023
|
+
raise
|
|
1024
|
+
|
|
1025
|
+
return [r if r is not None else evaluator._create_error_result("Missing result") for r in results]
|
|
973
1026
|
|
|
974
1027
|
def _write_outputs(records: List[Dict[str, Any]], output_jsonl_path: str) -> str:
|
|
975
1028
|
with open(output_jsonl_path, 'w', encoding='utf-8') as f:
|
|
@@ -992,12 +1045,15 @@ if __name__ == '__main__':
|
|
|
992
1045
|
max_script_runs=args.max_script_runs,
|
|
993
1046
|
script_python=args.script_python,
|
|
994
1047
|
include_script_results=args.include_script_results,
|
|
995
|
-
max_script_output_chars=args.max_script_output_chars
|
|
1048
|
+
max_script_output_chars=args.max_script_output_chars,
|
|
1049
|
+
github_token=args.github_token or os.getenv('GITHUB_TOKEN'),
|
|
996
1050
|
)
|
|
997
1051
|
evaluator = SkillEvaluator(config)
|
|
998
1052
|
records = _load_records(args.input)
|
|
999
|
-
|
|
1000
|
-
|
|
1053
|
+
if args.limit and args.limit > 0:
|
|
1054
|
+
records = records[: args.limit]
|
|
1055
|
+
|
|
1056
|
+
results = _evaluate_records_streaming(records, evaluator, config)
|
|
1001
1057
|
for rec, result in zip(records, results):
|
|
1002
1058
|
rec['evaluation'] = result
|
|
1003
1059
|
json_path = _write_outputs(records, args.output)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|