Skip to content
Merged
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
9 changes: 8 additions & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@ Studio 的持续集成和持续部署工作流。
- 手动触发 (workflow_dispatch)

**测试矩阵**:
- Python 3.10, 3.11
<!-- - Python 3.10, 3.11 -->
- Python 3.11
- 单元测试 + 集成测试

**测试步骤**:

#### Job: test
运行快速单元测试和集成测试(不需要 LLM 引擎)

安装依赖使用:

```bash
pip install -e ".[dev,full]"
```

```bash
# 单元测试
pytest tests/unit/ -v -m "not slow"
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/ci-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ jobs:

strategy:
matrix:
python-version: ['3.10', '3.11']
# python-version: ['3.10', '3.11'] # deprecated: 3.10 support removed
python-version: ['3.11']

env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
Expand All @@ -76,7 +77,7 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
pip install -e ".[dev,full]"

- name: Run unit tests
if: ${{ github.event.inputs.test_type == 'unit' || github.event.inputs.test_type == 'all' || github.event.inputs.test_type == '' }}
Expand Down Expand Up @@ -127,7 +128,7 @@ jobs:
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -e ".[dev]"
pip install -e ".[dev,full]"

- name: Run LLM 集成测试 (CPU backend)
run: |
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ Source: `https://pypi.org/pypi/isage-studio/json` (checked on 2026-02-14, UTC).

### Added
- CI/CD workflow for automated testing (`.github/workflows/ci-test.yml`)
- Unit tests on Python 3.10 and 3.11
<!-- - Unit tests on Python 3.10 and 3.11 -->
- Unit tests on Python 3.11
- Integration tests with CPU backend
- E2E tests for LLM integration
- Code quality checks (Ruff, Mypy)
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ cd sage-studio

2. **Install dependencies**:
```bash
pip install -e .
pip install -e ".[dev,full]"
```

3. **Install SAGE core dependencies** (if needed):
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ Studio 采用**前后端分离**架构,直接接入 SAGE 核心引擎:

### Environment Requirements

- **Python**: 3.10+ (必需)
<!-- - **Python**: 3.10+ (必需) -->
- **Python**: 3.11+ (必需)
- **Node.js**: 18+ (推荐 LTS)
- **SAGE**: 完整安装 (包括 kernel, middleware, libs)

Expand Down Expand Up @@ -97,7 +98,7 @@ pip install isage # 自动包含 isage-studio

# 方式 2: 开发模式安装
cd sage-studio
pip install -e ".[dev]"
pip install -e ".[dev,full]"

# 安装前端依赖
cd src/sage/studio/frontend
Expand Down Expand Up @@ -525,7 +526,8 @@ React 18.2 + TypeScript 5.2
#### 后端

```
FastAPI + Python 3.10+
<!-- FastAPI + Python 3.10+ -->
FastAPI + Python 3.11+
├── Pydantic 2.0 # 数据验证
├── Uvicorn # ASGI 服务器
├── sage-kernel # Environment, DataStream API
Expand Down Expand Up @@ -870,7 +872,8 @@ sage studio start --dev

```bash
# 检查 Python 版本
python --version # 需要 3.10+
# python --version # 需要 3.10+ [deprecated]
python --version # 需要 3.11+

# 检查 SAGE 安装
pip list | grep isage
Expand Down
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ name = "isage-studio"
dynamic = ["version"]
description = "SAGE Studio - Visual workflow builder and LLM playground for SAGE AI pipelines"
readme = "README.md"
requires-python = ">=3.10"
# requires-python = ">=3.10" # deprecated: use 3.11+
requires-python = ">=3.11"
authors = [{ name = "IntelliStream Team", email = "shuhao_zhang@hust.edu.cn" }]
keywords = [
"sage",
Expand Down Expand Up @@ -143,7 +144,8 @@ version = { attr = "sage.studio._version.__version__" }
# Development tools configuration
[tool.black]
line-length = 100
target-version = ["py310", "py311", "py312"]
# target-version = ["py310", "py311", "py312"] # deprecated: py310 removed
target-version = ["py311", "py312"]
include = '\.pyi?$'

[tool.isort]
Expand Down Expand Up @@ -187,7 +189,8 @@ filterwarnings = [

[tool.ruff]
line-length = 100
target-version = "py310"
# target-version = "py310" # deprecated: use py311
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "UP"]
Expand Down
23 changes: 16 additions & 7 deletions quickstart.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
# quickstart.sh — sage-studio dev environment setup
#
# Usage:
# ./quickstart.sh # dev mode (default): hooks + .[dev] (includes [full])
# ./quickstart.sh # dev mode (default): hooks + .[dev,full]
# ./quickstart.sh --full # optional backends only: .[full]
# ./quickstart.sh --standard # core deps only: no extras
# ./quickstart.sh --yes # non-interactive (assume yes)
# ./quickstart.sh --doctor # diagnose environment issues
#
# Install matrix:
# (default / --dev) pip install -e .[dev] ← includes [full] via self-ref
# (default / --dev) pip install -e .[dev,full]
# --full pip install -e .[full]
# --standard pip install -e .
#
Expand All @@ -29,15 +29,15 @@ BOLD='\033[1m'
NC='\033[0m'

# ─── Arguments ────────────────────────────────────────────────────────────────
EXTRAS="[dev]" # default — dev includes [full] via pyproject self-reference
EXTRAS="[dev,full]" # default — install both dev and full extras
DOCTOR=false
YES=false
for arg in "$@"; do
case "$arg" in
--doctor) DOCTOR=true ;;
--standard) EXTRAS="" ;;
--full) EXTRAS="[full]" ;;
--dev) EXTRAS="[dev]" ;;
--dev) EXTRAS="[dev,full]" ;;
--yes|-y) YES=true ;;
esac
done
Expand Down Expand Up @@ -83,10 +83,19 @@ fi
echo -e "${YELLOW}${BOLD}Step 1/3: Checking Python environment${NC}"
PYTHON_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>/dev/null || echo "unknown")
echo -e " Python version: ${CYAN}${PYTHON_VERSION}${NC}"
if python3 -c "import sys; exit(0 if sys.version_info >= (3,10) else 1)" 2>/dev/null; then
echo -e " ${GREEN}✓ Python ≥ 3.10${NC}"
# DEPRECATED: Python 3.10 support
# if python3 -c "import sys; exit(0 if sys.version_info >= (3,10) else 1)" 2>/dev/null; then
# echo -e " ${GREEN}✓ Python ≥ 3.10${NC}"
# else
# echo -e " ${RED}✗ Python 3.10+ required (found ${PYTHON_VERSION})${NC}"
# exit 1
# fi

# ACTIVE: Python 3.11+ required
if python3 -c "import sys; exit(0 if sys.version_info >= (3,11) else 1)" 2>/dev/null; then
echo -e " ${GREEN}✓ Python ≥ 3.11${NC}"
else
echo -e " ${RED}✗ Python 3.10+ required (found ${PYTHON_VERSION})${NC}"
echo -e " ${RED}✗ Python 3.11+ required (found ${PYTHON_VERSION})${NC}"
exit 1
fi
echo ""
Expand Down
2 changes: 1 addition & 1 deletion src/sage/studio/_version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Version information for sage-studio package."""

# 独立硬编码版本
__version__ = "0.2.4.37"
__version__ = "0.2.4.39"
__author__ = "IntelliStream Team"
__email__ = "shuhao_zhang@hust.edu.cn"
4 changes: 2 additions & 2 deletions src/sage/studio/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from __future__ import annotations

from datetime import datetime, timezone
from datetime import UTC, datetime

from fastapi import APIRouter
from pydantic import BaseModel
Expand All @@ -26,7 +26,7 @@ class TokenResponse(BaseModel):


def _now_iso() -> str:
return datetime.now(timezone.utc).isoformat()
return datetime.now(UTC).isoformat()


def build_auth_router() -> APIRouter:
Expand Down
8 changes: 4 additions & 4 deletions src/sage/studio/api/canvas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import json
from datetime import datetime, timezone
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -144,8 +144,8 @@ async def publish_canvas_graph_route(
"metadata": req.metadata,
"params_schema": {},
"dataset_requirements": {},
"created_at": datetime.now(timezone.utc).isoformat(),
"updated_at": datetime.now(timezone.utc).isoformat(),
"created_at": datetime.now(UTC).isoformat(),
"updated_at": datetime.now(UTC).isoformat(),
},
compiled_payload=compiled_payload,
)
Expand Down Expand Up @@ -184,7 +184,7 @@ async def export_flow(flow_id: str):

export_data = {
"version": "1.0.0",
"exportTime": str(datetime.now(timezone.utc)),
"exportTime": str(datetime.now(UTC)),
"flowId": flow_id,
"flow": flow_data,
}
Expand Down
14 changes: 7 additions & 7 deletions src/sage/studio/contracts/models.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
from __future__ import annotations

from datetime import datetime, timezone
from enum import Enum
from datetime import UTC, datetime
from enum import StrEnum
from typing import Any

from pydantic import BaseModel, ConfigDict, Field

CONTRACT_SCHEMA_VERSION = "v1"


class RunKind(str, Enum):
class RunKind(StrEnum):
CHAT = "chat"
EXPERIMENT = "experiment"
SWARM = "swarm"


class StageEventState(str, Enum):
class StageEventState(StrEnum):
CREATED = "created"
QUEUED = "queued"
RUNNING = "running"
Expand All @@ -24,7 +24,7 @@ class StageEventState(str, Enum):
CANCELLED = "cancelled"


class ArtifactKind(str, Enum):
class ArtifactKind(StrEnum):
INPUT = "input"
OUTPUT = "output"
LOG = "log"
Expand All @@ -39,7 +39,7 @@ class RunRef(BaseModel):
request_id: str
workspace_id: str
kind: RunKind
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))


class StageEvent(BaseModel):
Expand All @@ -50,7 +50,7 @@ class StageEvent(BaseModel):
request_id: str
stage: str
state: StageEventState
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC))
message: str | None = None
metrics: dict[str, Any] | None = None

Expand Down
10 changes: 5 additions & 5 deletions src/sage/studio/runtime/endpoints/contracts.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from datetime import UTC, datetime
from enum import StrEnum


class EndpointProvider(str, Enum):
class EndpointProvider(StrEnum):
ALIBABA_DASHSCOPE = "alibaba_dashscope"
OPENAI = "openai"
ANTHROPIC = "anthropic"
Expand Down Expand Up @@ -39,8 +39,8 @@ class ManagedEndpoint:
is_default: bool = False
extra_headers: tuple[tuple[str, str], ...] = ()
api_key: str | None = None
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
updated_at: datetime = field(default_factory=lambda: datetime.now(UTC))


@dataclass(slots=True, frozen=True)
Expand Down
8 changes: 4 additions & 4 deletions src/sage/studio/runtime/endpoints/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import threading
from dataclasses import replace
from datetime import datetime, timezone
from datetime import UTC, datetime

from sage.studio.runtime.endpoints.contracts import (
PROVIDER_PRESETS,
Expand Down Expand Up @@ -59,7 +59,7 @@ def update_endpoint(self, endpoint_id: str, command: EndpointUpdate) -> ManagedE
if current is None:
raise KeyError(endpoint_id)

now = datetime.now(timezone.utc)
now = datetime.now(UTC)
updated = replace(
current,
display_name=command.display_name
Expand Down Expand Up @@ -92,7 +92,7 @@ def set_enabled(self, endpoint_id: str, enabled: bool) -> ManagedEndpoint:
current = self._records.get(endpoint_id)
if current is None:
raise KeyError(endpoint_id)
updated = replace(current, enabled=enabled, updated_at=datetime.now(timezone.utc))
updated = replace(current, enabled=enabled, updated_at=datetime.now(UTC))
self._records[endpoint_id] = updated
return updated

Expand Down Expand Up @@ -140,7 +140,7 @@ def reset(self) -> None:
self._records.clear()

def _set_default_locked(self, endpoint_id: str) -> None:
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
for record_id, record in list(self._records.items()):
self._records[record_id] = replace(
record,
Expand Down
Loading
Loading