xskill 0.3.0__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.
- xskill/__init__.py +29 -0
- xskill/adapters.py +183 -0
- xskill/agent.py +506 -0
- xskill/canary.py +458 -0
- xskill/candidates.py +554 -0
- xskill/cli.py +154 -0
- xskill/config.py +130 -0
- xskill/core.py +173 -0
- xskill/download_data.py +898 -0
- xskill/entities/__init__.py +9 -0
- xskill/entities/evaluator.py +78 -0
- xskill/entities/registry.py +97 -0
- xskill/entities/skill.py +212 -0
- xskill/entities/skill_repo.py +79 -0
- xskill/entities/trajectory.py +94 -0
- xskill/frontmatter.py +121 -0
- xskill/git_lock.py +79 -0
- xskill/index.py +585 -0
- xskill/llm_client.py +271 -0
- xskill/log.py +29 -0
- xskill/migrate.py +247 -0
- xskill/process.py +329 -0
- xskill/registry.py +481 -0
- xskill/sandbox/__init__.py +14 -0
- xskill/sandbox/agent_template.py +186 -0
- xskill/sandbox/base.py +249 -0
- xskill/sandbox/registry.py +31 -0
- xskill/sandbox/swe_smith.py +320 -0
- xskill/search.py +323 -0
- xskill/server.py +1391 -0
- xskill/skill_eval.py +402 -0
- xskill/skill_manager.py +262 -0
- xskill/skill_tools.py +616 -0
- xskill/tasks.py +455 -0
- xskill/traj_meta.py +41 -0
- xskill/types.py +92 -0
- xskill/ux_score.py +175 -0
- xskill/watcher.py +342 -0
- xskill/web/dist/assets/index-CWcPd2On.js +86 -0
- xskill/web/dist/assets/index-m9mjShnV.css +1 -0
- xskill/web/dist/index.html +13 -0
- xskill-0.3.0.dist-info/METADATA +259 -0
- xskill-0.3.0.dist-info/RECORD +47 -0
- xskill-0.3.0.dist-info/WHEEL +5 -0
- xskill-0.3.0.dist-info/entry_points.txt +2 -0
- xskill-0.3.0.dist-info/licenses/LICENSE +21 -0
- xskill-0.3.0.dist-info/top_level.txt +1 -0
xskill/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""xskill — 从 AI Agent 执行轨迹自动蒸馏可复用 Skill。
|
|
2
|
+
|
|
3
|
+
公开 SDK:
|
|
4
|
+
from xskill import XSkill, Skill, Trajectory, Evaluator
|
|
5
|
+
from xskill.types import (
|
|
6
|
+
WatchDir, SkillHit, TrajectoryHit,
|
|
7
|
+
EvalScore, Candidate, UxScoreResult,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
进阶(少数场景,例如单测直接拿子系统):
|
|
11
|
+
from xskill import Registry, SkillRepo
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.3.0"
|
|
15
|
+
|
|
16
|
+
# 顶级公开面:4 个核心类
|
|
17
|
+
from xskill.core import XSkill
|
|
18
|
+
from xskill.entities.skill import Skill
|
|
19
|
+
from xskill.entities.trajectory import Trajectory
|
|
20
|
+
from xskill.entities.evaluator import Evaluator
|
|
21
|
+
|
|
22
|
+
# 进阶:子系统类(不必常用)
|
|
23
|
+
from xskill.entities.registry import Registry
|
|
24
|
+
from xskill.entities.skill_repo import SkillRepo
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"XSkill", "Skill", "Trajectory", "Evaluator",
|
|
28
|
+
"Registry", "SkillRepo",
|
|
29
|
+
]
|
xskill/adapters.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""
|
|
2
|
+
adapters.py -- Trajectory submission and format adaptation layer
|
|
3
|
+
=================================================================
|
|
4
|
+
Convert various input formats to the standard xskill format
|
|
5
|
+
(markdown + json metadata) and handle submission to the traj directory.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from xskill.config import get_traj_dir
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def generate_traj_id(traj_dir: Path = None) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Auto-generate a traj ID like ``traj_0301`` based on existing files
|
|
19
|
+
in *traj_dir*. Scans for ``traj_*.md`` and picks max + 1.
|
|
20
|
+
"""
|
|
21
|
+
traj_dir = traj_dir or get_traj_dir()
|
|
22
|
+
traj_dir.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
|
|
24
|
+
existing_ids: list[int] = []
|
|
25
|
+
for f in traj_dir.glob("traj_*.md"):
|
|
26
|
+
m = re.match(r"traj_(\d+)", f.stem)
|
|
27
|
+
if m:
|
|
28
|
+
existing_ids.append(int(m.group(1)))
|
|
29
|
+
|
|
30
|
+
next_id = max(existing_ids) + 1 if existing_ids else 1
|
|
31
|
+
return f"traj_{next_id:04d}"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def adapt_trajectory(
|
|
35
|
+
content: str,
|
|
36
|
+
format: str,
|
|
37
|
+
metadata: Optional[dict] = None,
|
|
38
|
+
) -> tuple[str, dict]:
|
|
39
|
+
"""
|
|
40
|
+
Convert various input formats to the standard xskill representation.
|
|
41
|
+
|
|
42
|
+
Supported *format* values:
|
|
43
|
+
|
|
44
|
+
- ``markdown`` -- passthrough; content is already ``traj_*.md`` format.
|
|
45
|
+
- ``json`` -- JSON object with fields like ``messages``, ``tool_calls``, etc.
|
|
46
|
+
Converted to a markdown trajectory.
|
|
47
|
+
- ``raw`` -- plain text; wrapped in a basic trajectory markdown template.
|
|
48
|
+
|
|
49
|
+
Returns ``(md_content, json_metadata)``.
|
|
50
|
+
"""
|
|
51
|
+
metadata = metadata or {}
|
|
52
|
+
|
|
53
|
+
if format == "markdown":
|
|
54
|
+
return content, metadata
|
|
55
|
+
|
|
56
|
+
if format == "json":
|
|
57
|
+
return _adapt_json(content, metadata)
|
|
58
|
+
|
|
59
|
+
if format == "raw":
|
|
60
|
+
return _adapt_raw(content, metadata)
|
|
61
|
+
|
|
62
|
+
raise ValueError(f"unsupported trajectory format: {format!r}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ------------------------------------------------------------------
|
|
66
|
+
# Internal converters
|
|
67
|
+
# ------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
def _adapt_json(content: str, metadata: dict) -> tuple[str, dict]:
|
|
70
|
+
"""Convert a JSON trajectory to markdown + metadata."""
|
|
71
|
+
data = json.loads(content)
|
|
72
|
+
|
|
73
|
+
# Merge top-level keys (except messages/tool_calls) into metadata
|
|
74
|
+
meta = dict(metadata)
|
|
75
|
+
for key in ("model", "instance_id", "repo", "task", "result", "exit_status"):
|
|
76
|
+
if key in data and key not in meta:
|
|
77
|
+
meta[key] = data[key]
|
|
78
|
+
|
|
79
|
+
# Build markdown from messages / tool_calls
|
|
80
|
+
lines: list[str] = []
|
|
81
|
+
lines.append(f"# Trajectory")
|
|
82
|
+
if meta.get("instance_id"):
|
|
83
|
+
lines.append(f"\n**instance_id**: {meta['instance_id']}")
|
|
84
|
+
if meta.get("model"):
|
|
85
|
+
lines.append(f"**model**: {meta['model']}")
|
|
86
|
+
lines.append("")
|
|
87
|
+
|
|
88
|
+
messages = data.get("messages", [])
|
|
89
|
+
tool_calls = data.get("tool_calls", [])
|
|
90
|
+
|
|
91
|
+
for msg in messages:
|
|
92
|
+
role = msg.get("role", "unknown")
|
|
93
|
+
text = msg.get("content", "")
|
|
94
|
+
lines.append(f"## {role.capitalize()}")
|
|
95
|
+
lines.append("")
|
|
96
|
+
if isinstance(text, str):
|
|
97
|
+
lines.append(text)
|
|
98
|
+
elif isinstance(text, list):
|
|
99
|
+
# multi-part content
|
|
100
|
+
for part in text:
|
|
101
|
+
if isinstance(part, dict):
|
|
102
|
+
lines.append(part.get("text", str(part)))
|
|
103
|
+
else:
|
|
104
|
+
lines.append(str(part))
|
|
105
|
+
lines.append("")
|
|
106
|
+
|
|
107
|
+
if tool_calls:
|
|
108
|
+
lines.append("## Tool Calls")
|
|
109
|
+
lines.append("")
|
|
110
|
+
for tc in tool_calls:
|
|
111
|
+
name = tc.get("name", tc.get("function", {}).get("name", "unknown"))
|
|
112
|
+
args = tc.get("arguments", tc.get("function", {}).get("arguments", ""))
|
|
113
|
+
lines.append(f"### {name}")
|
|
114
|
+
lines.append("```")
|
|
115
|
+
lines.append(args if isinstance(args, str) else json.dumps(args, ensure_ascii=False))
|
|
116
|
+
lines.append("```")
|
|
117
|
+
if tc.get("output"):
|
|
118
|
+
lines.append(f"\n**output**:\n```\n{tc['output']}\n```")
|
|
119
|
+
lines.append("")
|
|
120
|
+
|
|
121
|
+
md_content = "\n".join(lines)
|
|
122
|
+
return md_content, meta
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _adapt_raw(content: str, metadata: dict) -> tuple[str, dict]:
|
|
126
|
+
"""Wrap plain text in a basic trajectory markdown template."""
|
|
127
|
+
lines = [
|
|
128
|
+
"# Trajectory",
|
|
129
|
+
"",
|
|
130
|
+
"## Raw Content",
|
|
131
|
+
"",
|
|
132
|
+
content,
|
|
133
|
+
"",
|
|
134
|
+
]
|
|
135
|
+
md_content = "\n".join(lines)
|
|
136
|
+
return md_content, dict(metadata)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ------------------------------------------------------------------
|
|
140
|
+
# Submission
|
|
141
|
+
# ------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
def submit_trajectory(
|
|
144
|
+
content: str,
|
|
145
|
+
format: str = "markdown",
|
|
146
|
+
metadata: Optional[dict] = None,
|
|
147
|
+
traj_id: Optional[str] = None,
|
|
148
|
+
traj_dir: Optional[Path] = None,
|
|
149
|
+
) -> dict:
|
|
150
|
+
"""
|
|
151
|
+
Complete submission flow:
|
|
152
|
+
|
|
153
|
+
1. Resolve *traj_dir* (from param or ``get_traj_dir()``).
|
|
154
|
+
2. Generate *traj_id* if not provided.
|
|
155
|
+
3. Adapt the input format to standard markdown + JSON metadata.
|
|
156
|
+
4. Write ``traj_{id}.md`` and optionally ``traj_{id}.json``.
|
|
157
|
+
5. Return ``{"traj_id": ..., "path": ..., "status": "stored"}``.
|
|
158
|
+
"""
|
|
159
|
+
traj_dir = Path(traj_dir) if traj_dir else get_traj_dir()
|
|
160
|
+
traj_dir.mkdir(parents=True, exist_ok=True)
|
|
161
|
+
|
|
162
|
+
if not traj_id:
|
|
163
|
+
traj_id = generate_traj_id(traj_dir)
|
|
164
|
+
|
|
165
|
+
md_content, json_metadata = adapt_trajectory(content, format, metadata)
|
|
166
|
+
|
|
167
|
+
# Write markdown
|
|
168
|
+
md_path = traj_dir / f"{traj_id}.md"
|
|
169
|
+
md_path.write_text(md_content, encoding="utf-8")
|
|
170
|
+
|
|
171
|
+
# Write JSON metadata if non-empty
|
|
172
|
+
if json_metadata:
|
|
173
|
+
json_path = traj_dir / f"{traj_id}.json"
|
|
174
|
+
json_path.write_text(
|
|
175
|
+
json.dumps(json_metadata, ensure_ascii=False, indent=2),
|
|
176
|
+
encoding="utf-8",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
"traj_id": traj_id,
|
|
181
|
+
"path": str(md_path),
|
|
182
|
+
"status": "stored",
|
|
183
|
+
}
|