skillnet-ai 0.0.4__py3-none-any.whl → 0.0.6__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.
skillnet_ai/evaluator.py CHANGED
@@ -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
- with ThreadPoolExecutor(max_workers=self.config.max_workers) as executor:
841
- future_to_idx = {
842
- executor.submit(self.evaluate, skill): idx
843
- for idx, skill in enumerate(skills)
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
- for future in as_completed(future_to_idx):
848
- idx = future_to_idx[future]
849
- results[idx] = future.result()
850
- pbar.update(1)
851
-
852
- return results
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, self.downloader, self.config.cache_dir, **kwargs
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='./evaluate_cache_dir', help='Cache directory')
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 _build_skills(
952
+
953
+ def _evaluate_records_streaming(
922
954
  records: List[Dict[str, Any]],
923
955
  evaluator: 'SkillEvaluator',
924
956
  config: EvaluatorConfig,
925
- ) -> Tuple[List[Optional[Skill]], Dict[int, str]]:
926
- skills: List[Optional[Skill]] = []
927
- errors: Dict[int, str] = {}
928
- for idx, rec in enumerate(records):
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
- elif 'skill_path' in rec:
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
- else:
946
- raise ValueError("Record must have 'skill_url' or 'skill_path'")
984
+ if err:
985
+ return evaluator._create_error_result(err)
986
+ return evaluator.evaluate(skill)
947
987
 
948
- if err:
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
- def _evaluate_records(
956
- records: List[Dict[str, Any]],
957
- evaluator: 'SkillEvaluator',
958
- skills: List[Optional[Skill]],
959
- errors: Dict[int, str],
960
- ) -> List[Dict[str, Any]]:
961
- skills_to_eval = [(idx, s) for idx, s in enumerate(skills) if s is not None]
962
- idx_to_result: Dict[int, Dict[str, Any]] = {}
963
- if skills_to_eval:
964
- indices, valid_skills = zip(*skills_to_eval)
965
- batch_results = evaluator.evaluate_batch(list(valid_skills))
966
- idx_to_result = dict(zip(indices, batch_results))
967
- return [
968
- evaluator._create_error_result(errors[idx])
969
- if idx in errors
970
- else idx_to_result[idx]
971
- for idx in range(len(records))
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
- skills, download_errors = _build_skills(records, evaluator, config)
1000
- results = _evaluate_records(records, evaluator, skills, download_errors)
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skillnet-ai
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: Official Python SDK for SkillNet: Create, Evaluate, and Connect AI Skills.
5
5
  Author-email: SkillNet Team <liangyuannnnn@gmail.com>
6
6
  License: MIT
@@ -9,26 +9,26 @@ Project-URL: Source, https://github.com/zjunlp/SkillNet
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.8
12
+ Requires-Python: >=3.9
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: requests>=2.25.0
16
- Requires-Dist: openai<2.0,>=1.0
17
- Requires-Dist: pydantic>=2.0.0
18
- Requires-Dist: tqdm>=4.0.0
19
- Requires-Dist: typer>=0.9.0
20
- Requires-Dist: rich>=13.0.0
21
- Requires-Dist: PyPDF2>=3.0.0
22
- Requires-Dist: pycryptodome>=3.20.0
23
- Requires-Dist: python-docx>=0.8.11
24
- Requires-Dist: python-pptx>=0.6.21
15
+ Requires-Dist: requests==2.32.5
16
+ Requires-Dist: openai==1.109.1
17
+ Requires-Dist: pydantic==2.12.5
18
+ Requires-Dist: tqdm==4.67.1
19
+ Requires-Dist: typer==0.21.1
20
+ Requires-Dist: rich==13.9.4
21
+ Requires-Dist: PyPDF2==3.0.1
22
+ Requires-Dist: pycryptodome==3.23.0
23
+ Requires-Dist: python-docx==1.2.0
24
+ Requires-Dist: python-pptx==1.0.2
25
25
  Dynamic: license-file
26
26
 
27
27
  # skillnet-ai
28
28
 
29
29
  [![PyPI version](https://badge.fury.io/py/skillnet-ai.svg)](https://badge.fury.io/py/skillnet-ai)
30
30
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
- [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
31
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
32
32
 
33
33
  **skillnet-ai** is the official Python Toolkit for interacting with the SkillNet platform. It allows AI Agents to **Create**, **Evaluate** and **Organize** AI skills at scale. It functions seamlessly as both a powerful Python Library and a feature-rich Command Line Interface (CLI).
34
34
 
@@ -265,12 +265,25 @@ skillnet download <private_url> --token <your_github_token>
265
265
 
266
266
  Generate structured Skill Packages from various sources using LLMs.
267
267
 
268
+ **Linux/macOS:**
269
+
268
270
  ```bash
269
271
  # Requirement: Ensure API_KEY is set in your environment variables.
270
272
  export API_KEY=sk-xxxxx
271
- export BASE_URL= xxxxxx # Optional custom LLM base URL
273
+ export BASE_URL=xxxxxx # Optional custom LLM base URL
274
+ ```
275
+
276
+ **Windows PowerShell:**
277
+
278
+ ```powershell
279
+ # Requirement: Ensure API_KEY is set in your environment variables.
280
+ $env:API_KEY = "sk-xxxxx"
281
+ $env:BASE_URL = "xxxxxx" # Optional custom LLM base URL
282
+ ```
272
283
 
284
+ **Usage Examples:**
273
285
 
286
+ ```bash
274
287
  # From a trajectory file
275
288
  skillnet create ./logs/trajectory.txt -d ./generated_skills
276
289
 
@@ -290,11 +303,26 @@ skillnet create --office report.pdf --model gpt-4o
290
303
  ### 4. Evaluate Skills (`evaluate`)
291
304
  Generate a comprehensive quality report (Safety, Completeness, Executability, Modifiability, Cost Awareness) for a skill.
292
305
 
306
+ **Linux/macOS:**
307
+
293
308
  ```bash
294
309
  # Requirement: Ensure API_KEY is set in your environment variables.
295
310
  export API_KEY=sk-xxxxx
296
- export BASE_URL= xxxxxx # Optional custom LLM base URL
311
+ export BASE_URL=xxxxxx # Optional custom LLM base URL
312
+ ```
297
313
 
314
+ **Windows PowerShell:**
315
+
316
+ ```powershell
317
+ # Requirement: Ensure API_KEY is set in your environment variables.
318
+ $env:API_KEY = "sk-xxxxx"
319
+ $env:BASE_URL = "xxxxxx" # Optional custom LLM base URL
320
+ ```
321
+
322
+ **Usage Examples:**
323
+
324
+ ```bash
325
+ # Requirement: Ensure API_KEY is set in your environment variables.
298
326
  # Evaluate a remote skill via GitHub URL
299
327
  skillnet evaluate https://github.com/anthropics/skills/tree/main/skills/algorithmic-art
300
328
 
@@ -308,12 +336,25 @@ skillnet evaluate ./my_skills/tool --category "Development" --model gpt-4o
308
336
  ### 5. Analyze Relationships (`analyze`)
309
337
  Scan a directory of skills to analyze their connections using AI.
310
338
 
339
+ **Linux/macOS:**
311
340
 
312
341
  ```bash
313
342
  # Requirement: Ensure API_KEY is set in your environment variables.
314
343
  export API_KEY=sk-xxxxx
315
- export BASE_URL= xxxxxx # Optional custom LLM base URL
344
+ export BASE_URL=xxxxxx # Optional custom LLM base URL
345
+ ```
346
+
347
+ **Windows PowerShell:**
316
348
 
349
+ ```powershell
350
+ # Requirement: Ensure API_KEY is set in your environment variables.
351
+ $env:API_KEY = "sk-xxxxx"
352
+ $env:BASE_URL = "xxxxxx" # Optional custom LLM base URL
353
+ ```
354
+
355
+ **Usage Examples:**
356
+
357
+ ```bash
317
358
  # Analyze a directory containing multiple skill folders
318
359
  skillnet analyze ./my_agent_skills
319
360
 
@@ -4,13 +4,13 @@ skillnet_ai/cli.py,sha256=g_geqiDJU3-JKHTKzfN0U_Mx3_Ye38Dk2VAv3pqEOwo,24443
4
4
  skillnet_ai/client.py,sha256=1vKuBUQMCSAybYddsuXlkix3sGW1KD-gqeFuTcVuzHM,11491
5
5
  skillnet_ai/creator.py,sha256=srJn_sV7mDWwTb1AG9QrXf2fDeA5pGBUCDXIYxkdQk0,39130
6
6
  skillnet_ai/downloader.py,sha256=egP7b1ovL4pbNuAqn8y17-6YseyPpJzWlJAohH9XeWE,6358
7
- skillnet_ai/evaluator.py,sha256=_x_9ijlF_R-doK2kiyIpRfVSdwQ2rly7seDXGmGNwpE,36446
7
+ skillnet_ai/evaluator.py,sha256=cB3-2YDxUIrT9Wq5eSAPnHxVziSN03gqOGIWcn15n0w,38876
8
8
  skillnet_ai/models.py,sha256=2af5PnXhkzhRKUUc02KMr5U77KbhlTyxSlJETmWdmZY,1162
9
9
  skillnet_ai/prompts.py,sha256=afxSxV9b_UPhTCQdahgCAnQoRVwMNweOmT3r9aVwxY0,40377
10
10
  skillnet_ai/searcher.py,sha256=MvinkWR8omSCMXKSdZTSBBsifxQ13ep4hpnd4ql2w7U,3170
11
- skillnet_ai-0.0.4.dist-info/licenses/LICENSE,sha256=N5w66QPiGia1KA-Drt_75rmKLsyVt5GJUu-j6Gxpihs,1063
12
- skillnet_ai-0.0.4.dist-info/METADATA,sha256=UKNgnLUWsxy_OFfbx2gvybsckdi7EasUuQemSw-iKVc,12771
13
- skillnet_ai-0.0.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
14
- skillnet_ai-0.0.4.dist-info/entry_points.txt,sha256=8JDzqrJcxks90e4ifiivf2KNeGMGLW-f7nRNi_f1bQQ,49
15
- skillnet_ai-0.0.4.dist-info/top_level.txt,sha256=0gHowx1tlCRYxEbdoQcH4nPoMZGWUAUX6wjSNv3VUoI,12
16
- skillnet_ai-0.0.4.dist-info/RECORD,,
11
+ skillnet_ai-0.0.6.dist-info/licenses/LICENSE,sha256=N5w66QPiGia1KA-Drt_75rmKLsyVt5GJUu-j6Gxpihs,1063
12
+ skillnet_ai-0.0.6.dist-info/METADATA,sha256=AQxxcvQiXa8C4GJm9IWVqU8nJx9blzQnkREm_Y7tIgA,13575
13
+ skillnet_ai-0.0.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
14
+ skillnet_ai-0.0.6.dist-info/entry_points.txt,sha256=8JDzqrJcxks90e4ifiivf2KNeGMGLW-f7nRNi_f1bQQ,49
15
+ skillnet_ai-0.0.6.dist-info/top_level.txt,sha256=0gHowx1tlCRYxEbdoQcH4nPoMZGWUAUX6wjSNv3VUoI,12
16
+ skillnet_ai-0.0.6.dist-info/RECORD,,