feat: expand module workflows, dynamic config, notifications and console API

This commit is contained in:
2026-09-04 09:49:37 +08:00
parent 8df5266abb
commit b124d757b0
309 changed files with 89358 additions and 6232 deletions
@@ -0,0 +1,183 @@
from __future__ import annotations
import json
import pytest
from gyxx_flow.modules.product_commerce.direct_llm import (
DirectLLMConfig,
DirectLLMConfigurationError,
DirectLLMResponseError,
DirectLLMTransientError,
chat_completion,
load_direct_llm_config,
)
def _config() -> DirectLLMConfig:
return DirectLLMConfig(
endpoint="https://api.minimaxi.com/v1/chat/completions",
credential="test-only-secret",
title_model="MiniMax-M3",
vision_model="MiniMax-M3",
)
def _response(content: str) -> bytes:
return json.dumps(
{
"choices": [{"message": {"content": content}}],
"usage": {"prompt_tokens": 3, "completion_tokens": 4},
}
).encode()
def test_direct_chat_uses_provider_endpoint_and_model_without_echoing_secret() -> None:
observed: dict[str, object] = {}
def transport(url, headers, body, timeout):
payload = json.loads(body)
observed.update(
url=url,
model=payload["model"],
messages=payload["messages"],
reasoning_split=payload["reasoning_split"],
timeout=timeout,
authorization=headers["Authorization"],
)
return _response('{"ok":true}')
content, usage = chat_completion(
[{"role": "user", "content": "hello"}],
model="MiniMax-M3",
timeout=17,
transport=transport,
config=_config(),
)
assert content == '{"ok":true}'
assert usage == {"prompt_tokens": 3, "completion_tokens": 4}
assert observed["url"] == "https://api.minimaxi.com/v1/chat/completions"
assert observed["model"] == "MiniMax-M3"
assert observed["reasoning_split"] is True
assert observed["timeout"] == 17.0
assert observed["authorization"] == "Bearer test-only-secret"
def test_direct_chat_can_disable_minimax_thinking_for_structured_video_tasks() -> None:
observed: dict[str, object] = {}
def transport(_url, _headers, body, _timeout):
observed.update(json.loads(body))
return _response("ok")
chat_completion(
[{"role": "user", "content": "return one token"}],
thinking_mode="disabled",
transport=transport,
config=_config(),
)
assert observed["thinking"] == {"type": "disabled"}
def test_direct_chat_rejects_unknown_thinking_mode() -> None:
with pytest.raises(DirectLLMConfigurationError, match="thinking mode"):
chat_completion(
[{"role": "user", "content": "hello"}],
thinking_mode="sometimes",
transport=lambda *_args: _response("unused"),
config=_config(),
)
def test_config_reads_shared_minimax_environment(monkeypatch) -> None:
monkeypatch.delenv("GYXX_DIRECT_LLM_BASE_URL", raising=False)
monkeypatch.delenv("GYXX_DIRECT_LLM_API_KEY", raising=False)
monkeypatch.delenv("GYXX_DIRECT_LLM_MODEL", raising=False)
monkeypatch.setenv("STYLE_ANALYSIS_LLM_BASE_URL", "https://api.minimaxi.com/v1")
monkeypatch.setenv("STYLE_ANALYSIS_LLM_API_KEY", "test-only-secret")
monkeypatch.setenv("STYLE_ANALYSIS_LLM_MODEL", "MiniMax-M3")
config = load_direct_llm_config()
assert config.endpoint == "https://api.minimaxi.com/v1/chat/completions"
assert config.title_model == "MiniMax-M3"
assert config.vision_model == "MiniMax-M3"
assert "test-only-secret" not in repr(config)
def test_config_rejects_legacy_local_gateway(monkeypatch) -> None:
monkeypatch.setenv(
"GYXX_DIRECT_LLM_BASE_URL",
"http://127.0.0.1:8642/v1",
)
monkeypatch.setenv("GYXX_DIRECT_LLM_API_KEY", "test-only-secret")
with pytest.raises(DirectLLMConfigurationError, match="local gateway"):
load_direct_llm_config()
def test_invalid_response_is_fail_closed() -> None:
with pytest.raises(DirectLLMResponseError, match="invalid JSON"):
chat_completion(
[{"role": "user", "content": "hello"}],
transport=lambda *_args: b"not-json",
config=_config(),
)
def test_direct_chat_retries_transient_provider_overload(monkeypatch) -> None:
calls = 0
def transport(_url, _headers, _body, _timeout):
nonlocal calls
calls += 1
if calls < 3:
raise DirectLLMTransientError(
"direct model request failed with HTTP 529"
)
return _response("recovered")
delays: list[float] = []
monkeypatch.setattr(
"gyxx_flow.modules.product_commerce.direct_llm.time.sleep",
delays.append,
)
content, _usage = chat_completion(
[{"role": "user", "content": "hello"}],
transport=transport,
config=_config(),
)
assert content == "recovered"
assert calls == 3
assert delays == [1.0, 3.0]
def test_direct_chat_preserves_transient_error_after_bounded_retries(
monkeypatch,
) -> None:
calls = 0
def transport(_url, _headers, _body, _timeout):
nonlocal calls
calls += 1
raise DirectLLMTransientError(
"direct model request failed with HTTP 529"
)
monkeypatch.setattr(
"gyxx_flow.modules.product_commerce.direct_llm.time.sleep",
lambda _seconds: None,
)
with pytest.raises(DirectLLMTransientError, match="HTTP 529"):
chat_completion(
[{"role": "user", "content": "hello"}],
transport=transport,
config=_config(),
)
assert calls == 3