Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions cookbook/client/server/megatron/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ set -u

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RUN_SCRIPT="$SCRIPT_DIR/run.sh"
TWINKLE_WORK_DIR="${TWINKLE_WORK_DIR:-/dashscope/caches/application/twinkle}"
TEMP_DIR="${TWINKLE_TEMP_DIR:-/dashscope/caches/application/ray_logs}"
export MODELSCOPE_CACHE="${MODELSCOPE_CACHE:-/dashscope/caches/application/.cache}"
TWINKLE_RUNTIME_DIR="${TWINKLE_RUNTIME_DIR:-/twinkle/runtime}"
TWINKLE_WORK_DIR="${TWINKLE_WORK_DIR:-$TWINKLE_RUNTIME_DIR/work}"
TEMP_DIR="${TWINKLE_TEMP_DIR:-$TWINKLE_RUNTIME_DIR/ray_logs}"
export MODELSCOPE_CACHE="${MODELSCOPE_CACHE:-$TWINKLE_RUNTIME_DIR/.cache}"
export FLA_TILELANG="${FLA_TILELANG:-0}"
LOG_FILE="$TWINKLE_WORK_DIR/run.log"
TWINKLE_HEALTH_URL="${TWINKLE_HEALTH_URL:-http://127.0.0.1:9000/api/v1/healthz}"
TWINKLE_DEEP_HEALTH_URL="${TWINKLE_DEEP_HEALTH_URL:-http://127.0.0.1:9000/api/v1/twinkle/healthz/deep}"
TWINKLE_DASHSERVING_ADAPTER="${TWINKLE_DASHSERVING_ADAPTER:-0}"
if [ "$TWINKLE_DASHSERVING_ADAPTER" = "1" ]; then
TWINKLE_HEALTH_URL="${TWINKLE_HEALTH_URL:-http://127.0.0.1:${PORT:-9000}/health}"
else
TWINKLE_HEALTH_URL="${TWINKLE_HEALTH_URL:-http://127.0.0.1:8000/api/v1/healthz}"
fi
TWINKLE_DEEP_HEALTH_URL="${TWINKLE_DEEP_HEALTH_URL:-http://127.0.0.1:8000/api/v1/twinkle/healthz/deep}"
TWINKLE_WATCHDOG_INTERVAL_SECONDS="${TWINKLE_WATCHDOG_INTERVAL_SECONDS:-10}"
TWINKLE_WATCHDOG_FAILURE_THRESHOLD="${TWINKLE_WATCHDOG_FAILURE_THRESHOLD:-3}"
TWINKLE_RAY_GRACE_SECONDS="${TWINKLE_RAY_GRACE_SECONDS:-30}"
Expand Down Expand Up @@ -67,6 +73,14 @@ validate_entrypoint_config() {
require_non_negative_int "TWINKLE_DEEP_HEALTH_GRACE_SECONDS" "$TWINKLE_DEEP_HEALTH_GRACE_SECONDS"
require_non_negative_int "TWINKLE_ENTRYPOINT_RESTART_BACKOFF_SECONDS" "$RESTART_BACKOFF_SECONDS"

case "$TWINKLE_DASHSERVING_ADAPTER" in
0|1) ;;
*)
print_error "TWINKLE_DASHSERVING_ADAPTER 只能是 0 或 1,当前值: $TWINKLE_DASHSERVING_ADAPTER"
exit 1
;;
esac

require_command timeout
require_command ray
require_command tail
Expand Down
58 changes: 47 additions & 11 deletions cookbook/client/server/megatron/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# ============================================
# Twinkle Megatron 服务启动脚本
# ============================================
# 功能:启动 Ray 集群(支持多 GPU/CPU 节点)、LGTM 观测栈和 Twinkle 服务器
# 功能:启动 Ray 集群(支持多 GPU/CPU 节点)、LGTM 观测栈和 Twinkle 服务器。
# 设置 TWINKLE_DASHSERVING_ADAPTER=1 时,同时启动 ASI Native HTTP Adapter。
#
# 用法:./run.sh [选项]
#
Expand All @@ -12,14 +13,15 @@
# --head NODE Head 节点 GPU 设备列表,逗号分隔 (默认: 0,1,2,3)
# --gpu-workers LIST GPU Worker 列表,分号分隔多个节点 (默认: 4,5,6,7)
# --cpu-workers N CPU Worker 数量 (默认: 1)
# --temp-dir DIR Ray 临时目录 (默认: /dashscope/caches/application/ray_logs)
# --save-dir DIR Twinkle 模型保存目录 (默认: /dashscope/caches/application/save)
# --temp-dir DIR Ray 临时目录 (默认: /twinkle/runtime/ray_logs)
# --save-dir DIR Twinkle 模型保存目录 (默认: /twinkle/runtime/save)
# --server-config FILE Twinkle 服务器配置文件路径 (默认: /twinkle/cookbook/client/server/megatron/server_config.yaml)
# --help 显示帮助信息
#
# 环境变量:
# MODELSCOPE_CACHE 默认 /dashscope/caches/application/.cache
# TWINKLE_WORK_DIR 默认 /dashscope/caches/application/twinkle
# MODELSCOPE_CACHE 默认 /twinkle/runtime/.cache
# TWINKLE_WORK_DIR 默认 /twinkle/runtime/work
# TWINKLE_DASHSERVING_ADAPTER 设为 1 时启动端口 9000 的 Adapter
# TWINKLE_RUN_EXISTING_ACTION 已有 run.sh 进程运行时的行为:exit 或 restart(默认 exit)
# TWINKLE_RUN_RESTART_TIMEOUT_SECONDS --restart 等待已有实例接收请求秒数(默认 120)
#
Expand Down Expand Up @@ -61,12 +63,13 @@ RAY_PORT=6379
RAY_ADDRESS="127.0.0.1:$RAY_PORT"

# --- 路径配置 ---
export MODELSCOPE_CACHE="${MODELSCOPE_CACHE:-/dashscope/caches/application/.cache}"
TWINKLE_WORK_DIR="${TWINKLE_WORK_DIR:-/dashscope/caches/application/twinkle}"
DEFAULT_TEMP_DIR="/dashscope/caches/application/ray_logs"
TWINKLE_RUNTIME_DIR="${TWINKLE_RUNTIME_DIR:-/twinkle/runtime}"
export MODELSCOPE_CACHE="${MODELSCOPE_CACHE:-$TWINKLE_RUNTIME_DIR/.cache}"
TWINKLE_WORK_DIR="${TWINKLE_WORK_DIR:-$TWINKLE_RUNTIME_DIR/work}"
DEFAULT_TEMP_DIR="${TWINKLE_TEMP_DIR:-$TWINKLE_RUNTIME_DIR/ray_logs}"
LOG_FILE="run.log"
REDIS_LOG_FILE="/twinkle/redis.log"
DEFAULT_SAVE_DIR="/dashscope/caches/application/save"
DEFAULT_SAVE_DIR="${TWINKLE_SAVE_DIR:-$TWINKLE_RUNTIME_DIR/save}"
DEFAULT_SERVER_CONFIG_FILE="/twinkle/cookbook/client/server/megatron/server_config.yaml"

# --- LGTM 版本配置(与 grafana/otel-lgtm:0.28.0 保持一致) ---
Expand All @@ -87,8 +90,10 @@ TWINKLE_RUN_RESTART_REQUEST_FILE="${TWINKLE_RUN_RESTART_REQUEST_FILE:-/tmp/twink
TWINKLE_RUN_EXISTING_ACTION="${TWINKLE_RUN_EXISTING_ACTION:-exit}"
TWINKLE_RUN_RESTART_TIMEOUT_SECONDS="${TWINKLE_RUN_RESTART_TIMEOUT_SECONDS:-120}"
SERVER_PID=""
ADAPTER_PID=""
TAIL_PID=""
RESTART_REQUESTED_BY_SIGNAL=0
TWINKLE_DASHSERVING_ADAPTER="${TWINKLE_DASHSERVING_ADAPTER:-0}"

# ============================================
# 参数解析(支持 --key=value 或 --key value 格式)
Expand Down Expand Up @@ -117,8 +122,9 @@ print_usage() {
--help, -h 显示帮助信息

环境变量:
MODELSCOPE_CACHE 默认: /dashscope/caches/application/.cache
TWINKLE_WORK_DIR 默认: /dashscope/caches/application/twinkle
MODELSCOPE_CACHE 默认: /twinkle/runtime/.cache
TWINKLE_WORK_DIR 默认: /twinkle/runtime/work
TWINKLE_DASHSERVING_ADAPTER 设为 1 时启动端口 9000 的 Adapter
TWINKLE_RUN_EXISTING_ACTION 已有 run.sh 进程运行时的行为:exit 或 restart (默认: exit)
TWINKLE_RUN_RESTART_TIMEOUT_SECONDS --restart 等待已有实例接收请求秒数 (默认: 120)

Expand Down Expand Up @@ -397,6 +403,13 @@ validate_runtime_config() {

require_positive_int "TWINKLE_RUN_RESTART_TIMEOUT_SECONDS" "$TWINKLE_RUN_RESTART_TIMEOUT_SECONDS"
require_non_negative_int "CPU_WORKER_COUNT" "$CPU_WORKER_COUNT"
case "$TWINKLE_DASHSERVING_ADAPTER" in
0|1) ;;
*)
print_error "TWINKLE_DASHSERVING_ADAPTER 只能是 0 或 1,当前值: $TWINKLE_DASHSERVING_ADAPTER"
exit 1
;;
esac
}

require_command() {
Expand Down Expand Up @@ -493,8 +506,10 @@ stop_pid() {

cleanup_existing_runtime() {
stop_pid "$TAIL_PID" "日志 tail"
stop_pid "$ADAPTER_PID" "DashServing Adapter"
stop_pid "$SERVER_PID" "Twinkle Server"
TAIL_PID=""
ADAPTER_PID=""
SERVER_PID=""

print_info "停止已有的 Twinkle Server..."
Expand Down Expand Up @@ -742,6 +757,20 @@ start_twinkle_server() {
start_log_tail
}

start_dashserving_adapter() {
if [ "$TWINKLE_DASHSERVING_ADAPTER" != "1" ]; then
return
fi

print_header "启动 DashServing Adapter"
export PORT="${PORT:-9000}"
export TWINKLE_INTERNAL_URL="${TWINKLE_INTERNAL_URL:-http://127.0.0.1:8000}"
export TWINKLE_DS_TIMEOUT_SECONDS="${TWINKLE_DS_TIMEOUT_SECONDS:-600}"
nohup python -m twinkle.server.dashserving >> "$LOG_FILE" 2>&1 &
ADAPTER_PID=$!
print_success "DashServing Adapter 已启动 (PID: $ADAPTER_PID, port: $PORT)"
}

wait_runtime() {
print_info "Twinkle runtime 已启动,等待 server 进程..."
while true; do
Expand All @@ -754,6 +783,12 @@ wait_runtime() {
return 1
fi

if [ "$TWINKLE_DASHSERVING_ADAPTER" = "1" ] \
&& ! kill -0 "$ADAPTER_PID" 2>/dev/null; then
print_error "DashServing Adapter 进程已退出 (PID: $ADAPTER_PID)"
return 1
fi

if ! kill -0 "$TAIL_PID" 2>/dev/null; then
print_warning "日志 tail 进程已退出,重新启动..."
start_log_tail
Expand All @@ -772,6 +807,7 @@ run_service_once() {
start_ray_cluster
start_lgtm
start_twinkle_server
start_dashserving_adapter
wait_runtime
}

Expand Down
2 changes: 1 addition & 1 deletion cookbook/client/server/megatron/server_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ proxy_location: EveryNode
# HTTP listener settings
http_options:
host: 0.0.0.0 # Listen on all network interfaces
port: 9000 # Port number for the server
port: 8000 # Private runtime port; ASI Adapter owns public port 9000

# Telemetry: push traces/metrics/logs to LGTM's OTel Collector via OTLP
telemetry:
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ dependencies = [
[project.scripts]
twinkle-server = "twinkle.server.cli:main"
twinkle-auto = "twinkle_client.auto:main"
twinkle-dashserving-adapter = "twinkle.server.dashserving.__main__:main"
twinkle-service-mesh-gateway = "twinkle.server.mesh_gateway.__main__:main"

[project.optional-dependencies]
megatron = ["megatron-core>=0.12.0", "transformer-engine[pytorch]", "mcore_bridge"]
Expand All @@ -39,13 +41,15 @@ client = [
"tinker==0.16.1",
]
server = [
"httpx>=0.25.0",
"redis>=5.0",
"psutil>=5.9.0",
"pynvml>=11.0.0",
"opentelemetry-api",
"opentelemetry-sdk",
"opentelemetry-exporter-otlp",
"opentelemetry-instrumentation-logging",
"uvicorn>=0.24.0",
]
test = [
"hypothesis>=6.0",
Expand Down
31 changes: 31 additions & 0 deletions src/twinkle/server/common/tunnel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
"""HTTP request and response payloads carried between service boundaries."""
from __future__ import annotations

from pydantic import BaseModel, Field, field_validator
from typing import Any


class TunnelRequest(BaseModel):
"""One Twinkle or Tinker HTTP request addressed to the local runtime."""

method: str = Field(min_length=1)
path: str
query: dict[str, str] = Field(default_factory=dict)
headers: dict[str, str] = Field(default_factory=dict)
body: Any = None

@field_validator('path')
@classmethod
def validate_path(cls, path: str) -> str:
if not path.startswith('/api/v1/'):
raise ValueError('path must start with /api/v1/')
return path


class TunnelResponse(BaseModel):
"""The original runtime HTTP response, serializable inside DashScope output."""

status_code: int = Field(ge=100, le=599)
headers: dict[str, str] = Field(default_factory=dict)
body: Any = None
6 changes: 6 additions & 0 deletions src/twinkle/server/dashserving/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
"""DashServing Native HTTP adapter for the Twinkle server."""

from .app import create_dashserving_app

__all__ = ['create_dashserving_app']
24 changes: 24 additions & 0 deletions src/twinkle/server/dashserving/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
"""Standalone launcher for the Twinkle DashServing adapter."""
from __future__ import annotations

import os


def main() -> None:
import uvicorn

from .app import create_dashserving_app

upstream_url = os.getenv('TWINKLE_INTERNAL_URL', 'http://127.0.0.1:8000')
port = int(os.getenv('PORT', '9000'))
timeout_seconds = float(os.getenv('TWINKLE_DS_TIMEOUT_SECONDS', '600'))
app = create_dashserving_app(
upstream_url=upstream_url,
timeout_seconds=timeout_seconds,
)
uvicorn.run(app, host='0.0.0.0', port=port)


if __name__ == '__main__':
main()
Loading
Loading