bump version to 0.2.7
This commit is contained in:
+4
-2
@@ -6,6 +6,7 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
"Programming Language :: Python :: 3.13",
|
"Programming Language :: Python :: 3.13",
|
||||||
|
"Programming Language :: Python :: 3.14",
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
||||||
@@ -20,7 +21,7 @@ license = { text = "MIT" }
|
|||||||
name = "pyflowx"
|
name = "pyflowx"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
version = "0.2.6"
|
version = "0.2.7"
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
autofmt = "pyflowx.cli.autofmt:main"
|
autofmt = "pyflowx.cli.autofmt:main"
|
||||||
@@ -65,6 +66,7 @@ dev = [
|
|||||||
"tox-uv>=1.13.1",
|
"tox-uv>=1.13.1",
|
||||||
"tox>=4.25.0",
|
"tox>=4.25.0",
|
||||||
]
|
]
|
||||||
|
llm = ["sglang[all]==0.5.10rc0; python_version >= '3.10'"]
|
||||||
office = [
|
office = [
|
||||||
"pillow>=10.4.0",
|
"pillow>=10.4.0",
|
||||||
"pymupdf>=1.24.11",
|
"pymupdf>=1.24.11",
|
||||||
@@ -90,7 +92,7 @@ packages = ["src/pyflowx"]
|
|||||||
pyflowx = { workspace = true }
|
pyflowx = { workspace = true }
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = ["pyflowx[dev,office]"]
|
dev = ["pyflowx[dev,office,llm]"]
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
branch = true
|
branch = true
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ from .task import (
|
|||||||
task_template,
|
task_template,
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = "0.3.0"
|
__version__ = "0.3.1"
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"IS_LINUX",
|
"IS_LINUX",
|
||||||
|
|||||||
@@ -4,19 +4,23 @@ import argparse
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pyflowx as px
|
import pyflowx as px
|
||||||
from pyflowx.conditions import BuiltinConditions
|
from pyflowx.conditions import BuiltinConditions, Constants
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Run a local model using SGLang.")
|
parser = argparse.ArgumentParser(description="启动 SGLang 服务")
|
||||||
parser.add_argument("name", help="Model name.")
|
parser.add_argument("--model", default="~/.models/Qwen2.5-Coder-32B-Instruct-AWQ", help="模型路径")
|
||||||
parser.add_argument("--dir", default=None, help="Directory of model.")
|
parser.add_argument("--port", type=int, default=8000, help="服务端口")
|
||||||
|
parser.add_argument("--ctx-len", type=int, default=28672, help="最大上下文长度")
|
||||||
|
parser.add_argument("--mem", type=float, default=0.75, help="显存占比 (0-1)")
|
||||||
|
parser.add_argument("--host", default="0.0.0.0", help="主机地址")
|
||||||
|
parser.add_argument("--log-level", default="info", help="日志级别")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if not args.name:
|
if not args.model:
|
||||||
parser.error("name is required")
|
parser.error("model is required")
|
||||||
|
|
||||||
model_dir = Path(args.dir) if args.dir else Path.home() / ".models" / args.name.split("/")[-1]
|
model_dir = Path(args.model).expanduser()
|
||||||
if not model_dir.exists():
|
if not model_dir.exists():
|
||||||
parser.error(f"Model directory {model_dir} does not exist.")
|
parser.error(f"Model directory {model_dir} does not exist.")
|
||||||
|
|
||||||
@@ -34,22 +38,26 @@ def main():
|
|||||||
px.TaskSpec(
|
px.TaskSpec(
|
||||||
name="run",
|
name="run",
|
||||||
cmd=[
|
cmd=[
|
||||||
"uvx",
|
"python" if Constants.IS_WINDOWS else "python3",
|
||||||
"sglang",
|
"-m",
|
||||||
"serve",
|
"sglang.launch_server",
|
||||||
"--model-path",
|
"--model-path",
|
||||||
str(model_dir),
|
str(model_dir),
|
||||||
"--host",
|
"--host",
|
||||||
"0.0.0.0",
|
str(args.host),
|
||||||
"--port",
|
"--port",
|
||||||
"8000",
|
"8000",
|
||||||
"--mem-fraction-static",
|
"--mem-fraction-static",
|
||||||
"0.88",
|
str(args.mem),
|
||||||
"--context-length",
|
"--context-length",
|
||||||
"32768",
|
"32768",
|
||||||
|
"--tool-call-parser",
|
||||||
|
"qwen",
|
||||||
|
"--log-level",
|
||||||
|
str(args.log_level),
|
||||||
],
|
],
|
||||||
verbose=True,
|
verbose=True,
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|
||||||
px.run(graph, verbose=True)
|
px.run(graph, strategy="sequential", verbose=True)
|
||||||
|
|||||||
@@ -134,7 +134,9 @@ def _evaluate_conditions(spec: TaskSpec[Any], context: Mapping[str, Any]) -> str
|
|||||||
failed_conditions.append(getattr(condition, "__name__", None) or "匿名条件")
|
failed_conditions.append(getattr(condition, "__name__", None) or "匿名条件")
|
||||||
|
|
||||||
if failed_conditions:
|
if failed_conditions:
|
||||||
return f"条件不满足: {', '.join(failed_conditions)}"
|
if len(failed_conditions) <= 2:
|
||||||
|
return f"条件不满足: {', '.join(failed_conditions)}"
|
||||||
|
return f"条件不满足: {', '.join(failed_conditions[:2])} 等{len(failed_conditions)}个条件"
|
||||||
|
|
||||||
if spec.skip_if_missing and not spec._is_cmd_available():
|
if spec.skip_if_missing and not spec._is_cmd_available():
|
||||||
cmd_name = spec.cmd[0] if isinstance(spec.cmd, list) and spec.cmd else "unknown"
|
cmd_name = spec.cmd[0] if isinstance(spec.cmd, list) and spec.cmd else "unknown"
|
||||||
|
|||||||
+33
-35
@@ -35,8 +35,6 @@ from typing import (
|
|||||||
Iterator,
|
Iterator,
|
||||||
List,
|
List,
|
||||||
Mapping,
|
Mapping,
|
||||||
Optional,
|
|
||||||
Tuple,
|
|
||||||
Union,
|
Union,
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
@@ -107,7 +105,7 @@ class RetryPolicy:
|
|||||||
delay: float = 0.0
|
delay: float = 0.0
|
||||||
backoff: float = 1.0
|
backoff: float = 1.0
|
||||||
jitter: float = 0.0
|
jitter: float = 0.0
|
||||||
retry_on: Tuple[type[BaseException], ...] = (Exception,)
|
retry_on: tuple[type[BaseException], ...] = (Exception,)
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
if self.max_attempts < 1:
|
if self.max_attempts < 1:
|
||||||
@@ -151,9 +149,9 @@ class TaskHooks:
|
|||||||
钩子异常不会影响任务状态,仅记录日志。
|
钩子异常不会影响任务状态,仅记录日志。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pre_run: Optional[Callable[[TaskSpec[Any]], None]] = None
|
pre_run: Callable[[TaskSpec[Any]], None] | None = None
|
||||||
post_run: Optional[Callable[[TaskSpec[Any], Any], None]] = None
|
post_run: Callable[[TaskSpec[Any], Any], None] | None = None
|
||||||
on_failure: Optional[Callable[[TaskSpec[Any], BaseException], None]] = None
|
on_failure: Callable[[TaskSpec[Any], BaseException], None] | None = None
|
||||||
|
|
||||||
|
|
||||||
class TaskStatus(Enum):
|
class TaskStatus(Enum):
|
||||||
@@ -248,27 +246,27 @@ class TaskSpec(Generic[T]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
fn: Optional[TaskFn[T]] = None
|
fn: TaskFn[T] | None = None
|
||||||
cmd: Optional[TaskCmd] = None
|
cmd: TaskCmd | None = None
|
||||||
depends_on: Tuple[str, ...] = ()
|
depends_on: tuple[str, ...] = ()
|
||||||
soft_depends_on: Tuple[str, ...] = ()
|
soft_depends_on: tuple[str, ...] = ()
|
||||||
defaults: Mapping[str, Any] = field(default_factory=dict)
|
defaults: Mapping[str, Any] = field(default_factory=dict)
|
||||||
args: Tuple[Any, ...] = ()
|
args: tuple[Any, ...] = ()
|
||||||
kwargs: Mapping[str, Any] = field(default_factory=dict)
|
kwargs: Mapping[str, Any] = field(default_factory=dict)
|
||||||
retry: RetryPolicy = field(default_factory=RetryPolicy)
|
retry: RetryPolicy = field(default_factory=RetryPolicy)
|
||||||
timeout: Optional[float] = None
|
timeout: float | None = None
|
||||||
tags: Tuple[str, ...] = ()
|
tags: tuple[str, ...] = ()
|
||||||
conditions: Tuple[Condition, ...] = ()
|
conditions: tuple[Condition, ...] = ()
|
||||||
cwd: Optional[Path] = None
|
cwd: Path | None = None
|
||||||
env: Optional[Mapping[str, str]] = None
|
env: Mapping[str, str] | None = None
|
||||||
verbose: bool = False
|
verbose: bool = False
|
||||||
skip_if_missing: bool = False
|
skip_if_missing: bool = False
|
||||||
allow_upstream_skip: bool = False
|
allow_upstream_skip: bool = False
|
||||||
strategy: Optional[str] = None
|
strategy: str | None = None
|
||||||
priority: int = 0
|
priority: int = 0
|
||||||
concurrency_key: Optional[str] = None
|
concurrency_key: str | None = None
|
||||||
continue_on_error: bool = False
|
continue_on_error: bool = False
|
||||||
cache_key: Optional[CacheKeyFn] = None
|
cache_key: CacheKeyFn | None = None
|
||||||
hooks: TaskHooks = field(default_factory=TaskHooks)
|
hooks: TaskHooks = field(default_factory=TaskHooks)
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
@@ -310,7 +308,7 @@ class TaskSpec(Generic[T]):
|
|||||||
_run.__name__ = spec.name
|
_run.__name__ = spec.name
|
||||||
return _run # type: ignore[return-value]
|
return _run # type: ignore[return-value]
|
||||||
|
|
||||||
def should_execute(self, context: Context) -> Tuple[bool, Optional[str]]:
|
def should_execute(self, context: Context) -> tuple[bool, str | None]:
|
||||||
"""检查任务是否应执行。
|
"""检查任务是否应执行。
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@@ -367,12 +365,12 @@ class TaskSpec(Generic[T]):
|
|||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def _env_and_cwd(
|
def _env_and_cwd(
|
||||||
env: Optional[Mapping[str, str]],
|
env: Mapping[str, str] | None,
|
||||||
cwd: Optional[Path],
|
cwd: Path | None,
|
||||||
) -> Iterator[None]:
|
) -> Iterator[None]:
|
||||||
"""临时设置环境变量与工作目录。"""
|
"""临时设置环境变量与工作目录。"""
|
||||||
saved_env: dict[str, str] = {}
|
saved_env: dict[str, str] = {}
|
||||||
saved_cwd: Optional[str] = None
|
saved_cwd: str | None = None
|
||||||
if env:
|
if env:
|
||||||
for k, v in env.items():
|
for k, v in env.items():
|
||||||
if k in os.environ:
|
if k in os.environ:
|
||||||
@@ -431,7 +429,7 @@ def _run_command(spec: TaskSpec[Any]) -> Any: # noqa: PLR0912
|
|||||||
print(f"[verbose] 工作目录: {cwd}", flush=True)
|
print(f"[verbose] 工作目录: {cwd}", flush=True)
|
||||||
|
|
||||||
# 合并环境变量
|
# 合并环境变量
|
||||||
run_env: Optional[dict[str, str]] = None
|
run_env: dict[str, str] | None = None
|
||||||
if env_override:
|
if env_override:
|
||||||
run_env = dict(os.environ)
|
run_env = dict(os.environ)
|
||||||
run_env.update(env_override)
|
run_env.update(env_override)
|
||||||
@@ -470,8 +468,8 @@ def _run_command(spec: TaskSpec[Any]) -> Any: # noqa: PLR0912
|
|||||||
# 任务模板:批量生成相似 TaskSpec 的工厂
|
# 任务模板:批量生成相似 TaskSpec 的工厂
|
||||||
# ---------------------------------------------------------------------- #
|
# ---------------------------------------------------------------------- #
|
||||||
def task_template(
|
def task_template(
|
||||||
fn: Optional[TaskFn[Any]] = None,
|
fn: TaskFn[Any] | None = None,
|
||||||
cmd: Optional[TaskCmd] = None,
|
cmd: TaskCmd | None = None,
|
||||||
**defaults: Any,
|
**defaults: Any,
|
||||||
) -> Callable[..., TaskSpec[Any]]:
|
) -> Callable[..., TaskSpec[Any]]:
|
||||||
"""创建任务模板工厂。
|
"""创建任务模板工厂。
|
||||||
@@ -505,15 +503,15 @@ class TaskResult(Generic[T]):
|
|||||||
|
|
||||||
spec: TaskSpec[T]
|
spec: TaskSpec[T]
|
||||||
status: TaskStatus = TaskStatus.PENDING
|
status: TaskStatus = TaskStatus.PENDING
|
||||||
value: Optional[T] = None
|
value: T | None = None
|
||||||
error: Optional[BaseException] = None
|
error: BaseException | None = None
|
||||||
attempts: int = 0
|
attempts: int = 0
|
||||||
started_at: Optional[datetime] = None
|
started_at: datetime | None = None
|
||||||
finished_at: Optional[datetime] = None
|
finished_at: datetime | None = None
|
||||||
reason: Optional[str] = None # 跳过原因
|
reason: str | None = None # 跳过原因
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def duration(self) -> Optional[float]:
|
def duration(self) -> float | None:
|
||||||
"""从开始到结束的耗时(秒),未开始/未结束则为 ``None``。"""
|
"""从开始到结束的耗时(秒),未开始/未结束则为 ``None``。"""
|
||||||
if self.started_at is None or self.finished_at is None:
|
if self.started_at is None or self.finished_at is None:
|
||||||
return None
|
return None
|
||||||
@@ -527,6 +525,6 @@ class TaskEvent:
|
|||||||
task: str
|
task: str
|
||||||
status: TaskStatus
|
status: TaskStatus
|
||||||
attempts: int = 0
|
attempts: int = 0
|
||||||
error: Optional[str] = None
|
error: str | None = None
|
||||||
duration: Optional[float] = None
|
duration: float | None = None
|
||||||
reason: Optional[str] = None
|
reason: str | None = None
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ class TestPdfInfo:
|
|||||||
class TestPdfOcr:
|
class TestPdfOcr:
|
||||||
"""Test pdf_ocr function."""
|
"""Test pdf_ocr function."""
|
||||||
|
|
||||||
|
@pytest.mark.slow
|
||||||
def test_pdf_ocr_file(self, tmp_path: Path) -> None:
|
def test_pdf_ocr_file(self, tmp_path: Path) -> None:
|
||||||
"""Should OCR PDF."""
|
"""Should OCR PDF."""
|
||||||
pytest.importorskip("fitz")
|
pytest.importorskip("fitz")
|
||||||
|
|||||||
@@ -529,6 +529,7 @@ class TestDependencyDrivenScheduling:
|
|||||||
class TestConcurrencyLimits:
|
class TestConcurrencyLimits:
|
||||||
"""测试并发限制:相同 concurrency_key 的任务串行执行。"""
|
"""测试并发限制:相同 concurrency_key 的任务串行执行。"""
|
||||||
|
|
||||||
|
@pytest.mark.slow
|
||||||
def test_concurrency_key_serializes_tasks(self) -> None:
|
def test_concurrency_key_serializes_tasks(self) -> None:
|
||||||
"""相同 key 的任务不应并发执行。"""
|
"""相同 key 的任务不应并发执行。"""
|
||||||
running: list[int] = []
|
running: list[int] = []
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ def test_skip_if_missing_with_fn_not_checked():
|
|||||||
assert spec.should_execute({})[0] is True
|
assert spec.should_execute({})[0] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.slow
|
||||||
def test_skip_if_missing_with_empty_cmd_list():
|
def test_skip_if_missing_with_empty_cmd_list():
|
||||||
"""skip_if_missing=True 时,空命令列表应返回 True(不检查)."""
|
"""skip_if_missing=True 时,空命令列表应返回 True(不检查)."""
|
||||||
spec = TaskSpec("test", cmd=[""], skip_if_missing=True)
|
spec = TaskSpec("test", cmd=[""], skip_if_missing=True)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tox]
|
[tox]
|
||||||
isolated_build = true
|
isolated_build = true
|
||||||
envlist = py38, py39, py310, py311, py312, py313
|
envlist = py38, py39, py310, py311, py312, py313, py314
|
||||||
min_version = 4.0
|
min_version = 4.0
|
||||||
requires = tox-uv
|
requires = tox-uv
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
|
|||||||
Reference in New Issue
Block a user