refactor: split submit_sft_via_cml.sh into submit_sft.sh + submit_cml_eval.sh

- submit_sft.sh: pure SFT submission, prints JobID and exits (no embedded watcher)
- submit_cml_eval.sh: pure CML eval, reads workflow_id/version from config.yaml
- jupyter_runtime write_text: rewrite to use Contents API instead of terminal websocket
- SKILL.md: restrict trigger to explicit UI click only
- program.md: update §5.2 docs to reflect two-script workflow

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
hupenglong1
2026-05-25 17:21:42 +08:00
parent d2c221e2da
commit a3ab74b506
8 changed files with 328 additions and 186 deletions
+96 -26
View File
@@ -796,34 +796,104 @@ else:
max_output_chars: int,
) -> str:
target = self.resolve_workspace_path(path)
if newline_at_end and content and not content.endswith('\n'):
content += '\n'
mode = 'a' if append else 'w'
code = r'''
from pathlib import Path
target = Path(PATH)
target.parent.mkdir(parents=True, exist_ok=True)
content = CONTENT
with target.open(MODE, encoding="utf-8") as fh:
fh.write(content)
print(f"{'appended' if MODE == 'a' else 'wrote'} {target} ({len(content)} chars)")
'''
script = (
code.replace('PATH', python_literal(target))
.replace('CONTENT', python_literal(content))
.replace('MODE', python_literal(mode))
api_path = api_path_for_absolute_path(target)
if not api_path:
raise JupyterRuntimeError(f'Invalid remote path: {target}')
parent_api = '/'.join(api_path.split('/')[:-1])
if parent_api:
self._ensure_remote_dir(parent_api)
final_content = content
if append:
final_content = self._read_remote_text_for_append(api_path) + content
if newline_at_end and final_content and not final_content.endswith('\n'):
final_content += '\n'
encoded = base64.b64encode(final_content.encode('utf-8')).decode('ascii')
url = f'{self.binding.base_url}/api/contents/{quote(api_path, safe="/")}'
response = self._session.put(
url,
json={
'type': 'file',
'format': 'base64',
'content': encoded,
},
headers={
'X-XSRFToken': self._xsrf_token,
'Content-Type': 'application/json',
},
timeout=60,
)
result = self.run_python(
code=script,
script_path=None,
args=[],
stdin=None,
timeout_seconds=20.0,
max_output_chars=max_output_chars,
if response.status_code >= 400:
raise JupyterRuntimeError(
f'Unable to write remote file: HTTP {response.status_code} {response.text[:500]}'
)
verb = 'appended' if append else 'wrote'
return f'{verb} {target} ({len(final_content)} chars)'
def _ensure_remote_dir(self, api_path: str) -> None:
parts = [segment for segment in api_path.split('/') if segment]
cumulative = ''
for segment in parts:
cumulative = f'{cumulative}/{segment}' if cumulative else segment
url = f'{self.binding.base_url}/api/contents/{quote(cumulative, safe="/")}'
resp = self._session.get(
url,
params={'content': 0},
headers={'X-XSRFToken': self._xsrf_token},
timeout=30,
)
if resp.status_code == 200:
kind = resp.json().get('type')
if kind == 'directory':
continue
raise JupyterRuntimeError(
f'Cannot create directory at {cumulative}: path exists as {kind}'
)
if resp.status_code != 404:
raise JupyterRuntimeError(
f'Unable to inspect remote path {cumulative}: HTTP {resp.status_code} {resp.text[:300]}'
)
create = self._session.put(
url,
json={'type': 'directory'},
headers={
'X-XSRFToken': self._xsrf_token,
'Content-Type': 'application/json',
},
timeout=30,
)
if create.status_code >= 400:
raise JupyterRuntimeError(
f'Unable to create remote directory {cumulative}: HTTP {create.status_code} {create.text[:300]}'
)
def _read_remote_text_for_append(self, api_path: str) -> str:
url = f'{self.binding.base_url}/api/contents/{quote(api_path, safe="/")}'
resp = self._session.get(
url,
params={'content': 1},
headers={'X-XSRFToken': self._xsrf_token},
timeout=60,
)
if result.exit_code != 0:
raise JupyterRuntimeError(result.stdout.strip() or 'remote write_file failed')
return result.stdout.strip()
if resp.status_code == 404:
return ''
if resp.status_code >= 400:
raise JupyterRuntimeError(
f'Unable to read existing remote file for append: HTTP {resp.status_code} {resp.text[:500]}'
)
payload = resp.json()
if payload.get('type') != 'file':
raise JupyterRuntimeError(
f'Cannot append: remote path {api_path} is not a file (type={payload.get("type")})'
)
existing = payload.get('content', '')
if not isinstance(existing, str):
return ''
if payload.get('format') == 'base64':
return base64.b64decode(existing).decode('utf-8', errors='replace')
return existing
def resolve_workspace_path(self, raw_path: str) -> str:
value = raw_path.strip() or '.'