colabllm 0.1.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.
Files changed (59) hide show
  1. colabllm/__init__.py +1 -0
  2. colabllm/__main__.py +3 -0
  3. colabllm/cli.py +77 -0
  4. colabllm/control.py +52 -0
  5. colabllm/lib/avatar_sampling.py +28 -0
  6. colabllm/lib/cli_support.py +158 -0
  7. colabllm/lib/colabsvc.py +930 -0
  8. colabllm/lib/completion.py +128 -0
  9. colabllm/lib/doctor.py +73 -0
  10. colabllm/lib/frp_backend.py +164 -0
  11. colabllm/lib/frp_server.py +580 -0
  12. colabllm/lib/ocr_parameters.py +90 -0
  13. colabllm/lib/paths.py +13 -0
  14. colabllm/lib/processes.py +66 -0
  15. colabllm/lib/protocol.py +42 -0
  16. colabllm/lib/skill_bundle.py +40 -0
  17. colabllm/services/comfyui.ipynb +70 -0
  18. colabllm/services/comfyui.py +225 -0
  19. colabllm/services/indextts.ipynb +70 -0
  20. colabllm/services/indextts.py +477 -0
  21. colabllm/services/longcat-avatar.ipynb +70 -0
  22. colabllm/services/longcat-avatar.py +249 -0
  23. colabllm/services/longcat-video.ipynb +70 -0
  24. colabllm/services/longcat-video.py +26 -0
  25. colabllm/services/nemotron-transcribe.ipynb +70 -0
  26. colabllm/services/nemotron-transcribe.py +1627 -0
  27. colabllm/services/ollama.ipynb +70 -0
  28. colabllm/services/ollama.py +42 -0
  29. colabllm/services/qwen-flash.ipynb +70 -0
  30. colabllm/services/qwen-flash.py +55 -0
  31. colabllm/services/qwen3-tts.ipynb +70 -0
  32. colabllm/services/qwen3-tts.py +496 -0
  33. colabllm/services/unlimited-ocr.ipynb +70 -0
  34. colabllm/services/unlimited-ocr.py +262 -0
  35. colabllm/services/unsloth-studio.ipynb +70 -0
  36. colabllm/services/unsloth-studio.py +26 -0
  37. colabllm/utility.py +59 -0
  38. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/SKILL.md +108 -0
  39. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/comfyui.md +59 -0
  40. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/indextts.md +94 -0
  41. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/inference.md +7 -0
  42. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/longcat-avatar.md +71 -0
  43. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/longcat-video.md +58 -0
  44. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/media.md +8 -0
  45. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/nemotron-transcribe.md +90 -0
  46. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/ocr.md +5 -0
  47. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/ollama.md +57 -0
  48. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/operations.md +66 -0
  49. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/qwen-flash.md +74 -0
  50. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/qwen3-tts.md +105 -0
  51. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/runtime-common.md +65 -0
  52. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/speech.md +7 -0
  53. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/unlimited-ocr.md +80 -0
  54. colabllm-0.1.0.data/data/share/colabllm/skills/colabllm/references/unsloth-studio.md +55 -0
  55. colabllm-0.1.0.dist-info/METADATA +519 -0
  56. colabllm-0.1.0.dist-info/RECORD +59 -0
  57. colabllm-0.1.0.dist-info/WHEEL +5 -0
  58. colabllm-0.1.0.dist-info/entry_points.txt +2 -0
  59. colabllm-0.1.0.dist-info/top_level.txt +1 -0
colabllm/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Colab model service CLI."""
colabllm/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
colabllm/cli.py ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env python3
2
+ """Unified CLI for packaged Colab model services (Python standard library only)."""
3
+ import os
4
+ from pathlib import Path
5
+ import sys
6
+
7
+ ROOT = Path(__file__).resolve().parent
8
+ GLOBAL_COMMANDS = ('tunnel-config', 'frp-config', 'frp-status')
9
+ UTILITY_COMMANDS = ('doctor', 'completion', 'skill')
10
+
11
+
12
+ def services():
13
+ """Discover service pairs without importing or starting their implementation."""
14
+ return {p.stem: p for p in sorted((ROOT / 'services').glob('*.py'))
15
+ if p.with_suffix('.ipynb').is_file()}
16
+
17
+
18
+ def main(argv=None):
19
+ argv = list(sys.argv[1:] if argv is None else argv)
20
+ flags = []
21
+ while argv:
22
+ if argv[0] in ('--debug', '--quiet', '--no-color') or argv[0].startswith('--command-timeout='):
23
+ flags.append(argv.pop(0))
24
+ elif argv[0] == '--command-timeout':
25
+ flags.append(argv.pop(0))
26
+ if not argv:
27
+ print('--command-timeout 需要秒数', file=sys.stderr)
28
+ return 2
29
+ flags.append(argv.pop(0))
30
+ else:
31
+ break
32
+ if len(argv) > 1 and argv[0] == 'help':
33
+ argv = [*argv[1:], '--help']
34
+ available = services()
35
+ if not argv or argv[0] in ('-h', '--help', 'help'):
36
+ print('用法: colabllm <运行时> <命令> [参数...]\n'
37
+ ' colabllm <全局配置命令> [参数...]\n'
38
+ '\n'
39
+ '运行时: ' + ', '.join(available) + '\n'
40
+ '全局命令: ' + ', '.join((*GLOBAL_COMMANDS, *UTILITY_COMMANDS)) + '\n\n'
41
+ '示例:\n'
42
+ ' colabllm unlimited-ocr up\n'
43
+ ' colabllm unlimited-ocr ocr ./documents -O ./results\n'
44
+ ' colabllm qwen3-tts up -i 2\n'
45
+ ' colabllm tunnel-config\n\n'
46
+ '全局选项: --debug、--quiet、--no-color、--command-timeout SECONDS\n'
47
+ 'colabllm services 列出运行时;<运行时> --help 查看原有命令和参数。')
48
+ return 0
49
+ if argv == ['--version']:
50
+ from importlib.metadata import version
51
+ print('colabllm ' + version('colabllm'))
52
+ return 0
53
+ command, *remaining = argv
54
+ if command in ('services', '--list'):
55
+ if remaining:
56
+ print('用法: colabllm services', file=sys.stderr)
57
+ return 2
58
+ print('\n'.join(available))
59
+ return 0
60
+ if command in available:
61
+ script = available[command]
62
+ elif command in (*UTILITY_COMMANDS, '_complete'):
63
+ script = ROOT / 'utility.py'
64
+ remaining = [command, *remaining]
65
+ elif command in GLOBAL_COMMANDS:
66
+ script = ROOT / 'control.py'
67
+ remaining = [command, *remaining]
68
+ else:
69
+ print(f'未知运行时或命令: {command}\n使用 colabllm --help 查看可用命令。', file=sys.stderr)
70
+ return 2
71
+ # Replace this process: preserve signals, exit codes and the caller's cwd/local file paths.
72
+ # Keep the actual service script as argv[0], since its detached runner uses that path.
73
+ os.execv(sys.executable, [sys.executable, str(script), *flags, *remaining])
74
+
75
+
76
+ if __name__ == '__main__':
77
+ sys.exit(main())
colabllm/control.py ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python3
2
+ """Project-wide configuration shared by every Colab runtime service."""
3
+ import argparse
4
+ import sys
5
+ import json
6
+ from pathlib import Path
7
+
8
+ sys.path.insert(0, str(Path(__file__).resolve().parent / 'lib'))
9
+ import colabsvc
10
+ from colabsvc import cmd_tunnel_config, validate_domain
11
+
12
+
13
+ def build_parser():
14
+ parser = argparse.ArgumentParser(description='本项目所有运行时共享的全局配置')
15
+ commands = parser.add_subparsers(dest='command', required=True)
16
+ tunnel = commands.add_parser('tunnel-config', help='查看、设置或清除项目全局主域名')
17
+ settings = tunnel.add_mutually_exclusive_group()
18
+ settings.add_argument('--base-domain', metavar='DOMAIN', type=validate_domain,
19
+ help='所有服务共用的主域名,例如 example.com')
20
+ settings.add_argument('--clear', action='store_true', help='清除全局主域名,默认使用动态 URL')
21
+ frp=commands.add_parser('frp-config',help='配置并通过 SSH 安装 FRP 服务端;无参数时查看配置')
22
+ frp.add_argument('--server-ip',help='FRP 服务器公网 IPv4/IPv6 地址')
23
+ frp.add_argument('--ssh',help='SSH 目标,例如 root@frpip 或 ~/.ssh/config 中的别名')
24
+ frp.add_argument('--base-domain',type=validate_domain,help='项目全局主域名,DNS 由 cf CLI 管理')
25
+ frp.add_argument('--server-port',type=int,help='FRP 服务端端口,默认 7000')
26
+ frp.add_argument('--disable',action='store_true',help='关闭全局 FRP,之后启动恢复使用 Cloudflare')
27
+ commands.add_parser('frp-status',help='通过 SSH 检查 FRP/Caddy 服务和已分配路由')
28
+ return parser
29
+
30
+
31
+ def main(argv=None):
32
+ parser = build_parser()
33
+ args = parser.parse_args(argv)
34
+ if args.command=='tunnel-config':
35
+ cmd_tunnel_config(args)
36
+ elif args.command=='frp-config':
37
+ from frp_backend import configure
38
+ if args.disable and any(getattr(args,k) is not None for k in ('server_ip','ssh','base_domain','server_port')):
39
+ parser.error('--disable 不能与服务器配置参数同时使用')
40
+ result=configure(args,colabsvc.STATE_ROOT)
41
+ print(json.dumps(result,ensure_ascii=False,indent=2))
42
+ else:
43
+ from frp_backend import remote
44
+ config=colabsvc.load_tunnel_config().get('frp')
45
+ if not config:
46
+ raise ValueError('尚未配置 FRP 服务端')
47
+ print(json.dumps(remote(config,{'action':'status'}),ensure_ascii=False,indent=2))
48
+
49
+
50
+ if __name__ == '__main__':
51
+ from cli_support import run_cli
52
+ run_cli(main)
@@ -0,0 +1,28 @@
1
+ """Validated sampling controls shared by the Avatar CLI and GPU worker."""
2
+ import math
3
+
4
+
5
+ def sampling_settings(mode='distilled', steps=None, text_cfg=None, audio_cfg=None):
6
+ if mode not in ('distilled', 'standard'):
7
+ raise ValueError('sampling_mode 必须是 distilled 或 standard')
8
+ defaults = (8, 1.0, 1.0) if mode == 'distilled' else (50, 4.0, 3.0)
9
+ steps = defaults[0] if steps is None else steps
10
+ text_cfg = defaults[1] if text_cfg is None else text_cfg
11
+ audio_cfg = defaults[2] if audio_cfg is None else audio_cfg
12
+ if isinstance(steps, bool) or not isinstance(steps, (int, float)) or not math.isfinite(steps) or int(steps) != steps or not 8 <= steps <= 100:
13
+ raise ValueError('steps 必须是 8–100 的整数')
14
+ for name, value in (('text_guidance_scale', text_cfg), ('audio_guidance_scale', audio_cfg)):
15
+ if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value) or not 1 <= value <= 10:
16
+ raise ValueError(name + ' 必须是 1–10 的有限数值')
17
+ if mode == 'distilled' and (steps, text_cfg, audio_cfg) != defaults:
18
+ raise ValueError('8 步蒸馏模式固定 steps=8、text/audio CFG=1;调节 CFG 请使用 standard 模式')
19
+ return dict(sampling_mode=mode, steps=int(steps), text_guidance_scale=float(text_cfg),
20
+ audio_guidance_scale=float(audio_cfg), use_distill=mode == 'distilled')
21
+
22
+
23
+ def apply_sampling_mode(pipe, settings):
24
+ """Called under the worker lock, before generation or checkpoint resume."""
25
+ if settings['use_distill']:
26
+ pipe.dit.enable_loras(['dmd'])
27
+ else:
28
+ pipe.dit.disable_all_loras()
@@ -0,0 +1,158 @@
1
+ """Shared CLI errors, diagnostic output and authenticated HTTP transport."""
2
+ import http.client
3
+ import math
4
+ import time
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ import traceback
9
+ import urllib.error
10
+ import urllib.parse
11
+ import urllib.request
12
+
13
+ QUIET = False
14
+ NO_COLOR = False
15
+
16
+
17
+ class CliError(RuntimeError):
18
+ def __init__(self, message, code=1):
19
+ super().__init__(message)
20
+ self.code = code
21
+
22
+
23
+ class UsageError(ValueError):
24
+ code = 2
25
+
26
+
27
+ def diagnostic(message, color=None):
28
+ if QUIET:
29
+ return
30
+ if color and not NO_COLOR and not os.environ.get('NO_COLOR') and sys.stderr.isatty():
31
+ message = f'\033[{color}m{message}\033[0m'
32
+ print(message, file=sys.stderr, flush=True)
33
+
34
+
35
+ def nonnegative_seconds(value):
36
+ seconds = float(value)
37
+ if not math.isfinite(seconds) or seconds < 0:
38
+ raise ValueError('秒数必须是有限的非负数')
39
+ return seconds
40
+
41
+
42
+ def positive_seconds(value):
43
+ seconds = nonnegative_seconds(value)
44
+ if seconds == 0:
45
+ raise ValueError('秒数必须大于 0')
46
+ return seconds
47
+
48
+
49
+ def command_timeout(default=300):
50
+ value = os.environ.get('COLABLLM_COMMAND_TIMEOUT')
51
+ try:
52
+ return positive_seconds(value) if value else default
53
+ except ValueError:
54
+ raise UsageError('COLABLLM_COMMAND_TIMEOUT 必须是大于 0 的有限秒数') from None
55
+
56
+
57
+ class Deadline:
58
+ def __init__(self, timeout=0, message='等待超时;后台任务继续运行,可稍后查询'):
59
+ timeout = nonnegative_seconds(timeout)
60
+ self.end = time.monotonic() + timeout if timeout else None
61
+ self.message = message
62
+
63
+ def remaining(self, cap=None):
64
+ if self.end is None:
65
+ return cap
66
+ remaining = self.end - time.monotonic()
67
+ if remaining <= 0:
68
+ raise CliError(self.message, 124)
69
+ return min(remaining, cap) if cap is not None else remaining
70
+
71
+ def sleep(self, seconds):
72
+ time.sleep(self.remaining(seconds))
73
+
74
+
75
+ def origin(url):
76
+ parsed = urllib.parse.urlsplit(url)
77
+ if parsed.scheme not in ('http', 'https') or not parsed.hostname or parsed.username or parsed.password:
78
+ raise CliError('服务 URL 必须是无内嵌账号密码的 HTTP(S) 地址')
79
+ return (parsed.scheme.lower(), parsed.hostname.lower(), parsed.port or (443 if parsed.scheme == 'https' else 80))
80
+
81
+
82
+ def resolve_endpoint(state, url=None, token=None, *, url_env=None, token_env=None):
83
+ selected = (url or (os.environ.get(url_env) if url_env else None) or state.get('url') or '').rstrip('/')
84
+ explicit = (token or (os.environ.get(token_env) if token_env else None) or '').strip()
85
+ if not selected:
86
+ raise CliError('缺少服务地址:先启动服务,或显式提供 --url 和 API Token')
87
+ selected_origin = origin(selected)
88
+ if explicit:
89
+ return selected, explicit
90
+ saved_url, saved_token = state.get('url'), state.get('api_token')
91
+ if not saved_url or not saved_token or origin(saved_url) != selected_origin:
92
+ raise CliError('服务来源已改变或没有对应凭据;请显式提供 API Token 参数或对应环境变量')
93
+ return selected, saved_token
94
+
95
+
96
+ class SameOriginRedirect(urllib.request.HTTPRedirectHandler):
97
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
98
+ if req.has_header('Authorization') and origin(req.full_url) != origin(newurl):
99
+ raise CliError('拒绝向其他站点转发认证信息;请检查服务 URL 或重定向配置')
100
+ return super().redirect_request(req, fp, code, msg, headers, newurl)
101
+
102
+
103
+ def safe_urlopen(request, *args, **kwargs):
104
+ return urllib.request.build_opener(SameOriginRedirect()).open(request, *args, **kwargs)
105
+
106
+
107
+ def configure_output():
108
+ global QUIET, NO_COLOR
109
+ args = []
110
+ literal = False
111
+ debug = False
112
+ argv = iter(sys.argv[1:])
113
+ for arg in argv:
114
+ if arg == '--':
115
+ literal = True
116
+ if not literal and arg in ('--debug', '--quiet', '--no-color'):
117
+ debug |= arg == '--debug'
118
+ QUIET |= arg == '--quiet'
119
+ NO_COLOR |= arg == '--no-color'
120
+ elif not literal and (arg == '--command-timeout' or arg.startswith('--command-timeout=')):
121
+ value = next(argv, None) if arg == '--command-timeout' else arg.split('=', 1)[1]
122
+ try:
123
+ os.environ['COLABLLM_COMMAND_TIMEOUT'] = str(positive_seconds(value))
124
+ except (ValueError, TypeError):
125
+ raise UsageError('--command-timeout 需要大于 0 的有限秒数') from None
126
+ else:
127
+ args.append(arg)
128
+ sys.argv[1:] = args
129
+ return debug
130
+
131
+
132
+ def run_cli(action):
133
+ """Handle expected user-facing failures consistently, including early parsing."""
134
+ debug = False
135
+ try:
136
+ debug = configure_output()
137
+ code = action()
138
+ if isinstance(code, int) and code:
139
+ raise SystemExit(code)
140
+ except BrokenPipeError:
141
+ # Avoid a second flush failure at interpreter shutdown.
142
+ with open(os.devnull, 'w') as sink:
143
+ os.dup2(sink.fileno(), sys.stdout.fileno())
144
+ raise SystemExit(141) from None
145
+ except KeyboardInterrupt:
146
+ diagnostic('已中断当前命令;可用 status/job 查看服务或任务状态。')
147
+ raise SystemExit(130) from None
148
+ except (OSError, ValueError, RuntimeError, http.client.HTTPException, subprocess.SubprocessError) as error:
149
+ if debug:
150
+ traceback.print_exc()
151
+ else:
152
+ message = str(error)
153
+ if isinstance(error, urllib.error.HTTPError):
154
+ message = f'HTTP {error.code}: {error.reason}'
155
+ # Errors remain visible in quiet mode.
156
+ print(f'colabllm: {message}', file=sys.stderr)
157
+ code = error.code if isinstance(error, (CliError, UsageError)) else (124 if isinstance(error, (TimeoutError, subprocess.TimeoutExpired)) else 1)
158
+ raise SystemExit(code) from None