f3a8a38acd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
89 lines
3.7 KiB
Python
89 lines
3.7 KiB
Python
"""Run with the existing .venv; no training/MuJoCo imports or env-file discovery."""
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
from urllib.parse import urlsplit
|
|
|
|
from aiohttp import web
|
|
|
|
from .credentials import deepseek_llm, openrouter_jev
|
|
from .server import STATE, create_app
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="LeKiwi 本机模型服务(仅回环地址)")
|
|
parser.add_argument("--port", type=int, default=8768)
|
|
parser.add_argument("--state-dir", type=Path)
|
|
parser.add_argument("--website-origin", help="显式网站同源模式,不读取任何共享凭据")
|
|
parser.add_argument("--website-dev", action="store_true", help="仅回环 HTTP 开发模式")
|
|
parser.add_argument("--bind", default="127.0.0.1", choices=["127.0.0.1", "0.0.0.0"])
|
|
parser.add_argument("--trusted-proxy", action="append", default=[])
|
|
parser.add_argument("--max-sessions", type=int, default=128)
|
|
parser.add_argument("--max-inference", type=int, default=8)
|
|
parser.add_argument("--max-codex", type=int, default=2)
|
|
parser.add_argument(
|
|
"--openrouter-env",
|
|
type=Path,
|
|
help="显式只读取 OPENROUTER_API_KEY,配置 Jev;不自动发起请求",
|
|
)
|
|
parser.add_argument(
|
|
"--deepseek-env",
|
|
type=Path,
|
|
help="显式只读取 DEEPSEEK_API_KEY,配置 deepseek-flash;不自动调用",
|
|
)
|
|
parser.add_argument("--origin", action="append", help="额外允许的本机工作台 Origin")
|
|
args = parser.parse_args()
|
|
if not 1 <= args.port <= 65535:
|
|
parser.error("端口不合法")
|
|
if args.website_origin:
|
|
from .web_server import create_website_app
|
|
from .web_sessions import Limits
|
|
|
|
if args.openrouter_env or args.deepseek_env or args.origin:
|
|
parser.error("网站模式不接受共享凭据或额外 Origin")
|
|
if min(args.max_sessions, args.max_inference, args.max_codex) < 1:
|
|
parser.error("网站容量必须大于零")
|
|
app = create_website_app(
|
|
args.website_origin,
|
|
args.state_dir,
|
|
development=args.website_dev,
|
|
trusted_proxies=args.trusted_proxy,
|
|
limits=Limits(
|
|
sessions=args.max_sessions, inference=args.max_inference, codex=args.max_codex
|
|
),
|
|
)
|
|
web.run_app(app, host=args.bind, port=args.port, access_log=None, handler_cancellation=True)
|
|
return
|
|
if args.bind != "127.0.0.1" or args.website_dev or args.trusted_proxy:
|
|
parser.error("本机模式必须仅回环监听")
|
|
origins = {
|
|
"http://localhost:5173",
|
|
"http://127.0.0.1:5173",
|
|
"http://localhost:4173",
|
|
"http://127.0.0.1:4173",
|
|
}
|
|
for origin in args.origin or []:
|
|
url = urlsplit(origin)
|
|
if (
|
|
url.scheme not in ("http", "https")
|
|
or url.hostname not in ("127.0.0.1", "localhost", "::1")
|
|
or url.path
|
|
or url.query
|
|
or url.fragment
|
|
or url.username
|
|
or url.password
|
|
):
|
|
parser.error("Origin 必须是完整的本机来源,不支持通配符")
|
|
origins.add(origin)
|
|
app = create_app(args.state_dir, origins=origins, port=args.port)
|
|
if args.openrouter_env:
|
|
app[STATE].connections.values["jev"] = openrouter_jev(args.openrouter_env)
|
|
if args.deepseek_env:
|
|
app[STATE].connections.values["llm"] = deepseek_llm(args.deepseek_env)
|
|
print("服务令牌(仅当前进程有效,工作台内填写,不要保存到浏览器或 Git):", app[STATE].token)
|
|
web.run_app(app, host="127.0.0.1", port=args.port, access_log=None, handler_cancellation=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|