refactor(api): rename dify_graph to graphon (#34095)

This commit is contained in:
99
2026-03-25 21:58:56 +08:00
committed by GitHub
parent 7e9d00a5a6
commit 52e7492cbc
898 changed files with 2687 additions and 2687 deletions

View File

@ -0,0 +1,231 @@
import dataclasses
import datetime
from collections import deque
from decimal import Decimal
from enum import Enum
from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
from pathlib import Path, PurePath
from re import compile
from typing import Any
from unittest.mock import MagicMock
from uuid import UUID
import pytest
from pydantic import BaseModel, ConfigDict
from pydantic.networks import AnyUrl, NameEmail
from pydantic.types import SecretBytes, SecretStr
from pydantic_core import Url
from pydantic_extra_types.color import Color
from graphon.model_runtime.utils.encoders import (
_model_dump,
decimal_encoder,
generate_encoders_by_class_tuples,
isoformat,
jsonable_encoder,
)
class MockEnum(Enum):
A = "a"
B = "b"
class MockPydanticModel(BaseModel):
model_config = ConfigDict(populate_by_name=True)
name: str
age: int
@dataclasses.dataclass
class MockDataclass:
name: str
value: Any
class MockWithDict:
def __init__(self, data):
self.data = data
def __iter__(self):
return iter(self.data.items())
class MockWithVars:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
class TestEncoders:
def test_model_dump(self):
model = MockPydanticModel(name="test", age=20)
result = _model_dump(model)
assert result == {"name": "test", "age": 20}
def test_isoformat(self):
d = datetime.date(2023, 1, 1)
assert isoformat(d) == "2023-01-01"
t = datetime.time(12, 0, 0)
assert isoformat(t) == "12:00:00"
def test_decimal_encoder(self):
assert decimal_encoder(Decimal("1.0")) == 1.0
assert decimal_encoder(Decimal(1)) == 1
assert decimal_encoder(Decimal("1.5")) == 1.5
assert decimal_encoder(Decimal(0)) == 0
assert decimal_encoder(Decimal(-1)) == -1
def test_generate_encoders_by_class_tuples(self):
type_map = {int: str, float: str, str: int}
result = generate_encoders_by_class_tuples(type_map)
assert result[str] == (int, float)
assert result[int] == (str,)
def test_jsonable_encoder_basic_types(self):
assert jsonable_encoder("string") == "string"
assert jsonable_encoder(123) == 123
assert jsonable_encoder(1.23) == 1.23
assert jsonable_encoder(None) is None
def test_jsonable_encoder_pydantic(self):
model = MockPydanticModel(name="test", age=20)
assert jsonable_encoder(model) == {"name": "test", "age": 20}
def test_jsonable_encoder_pydantic_root(self):
# Manually create a mock that behaves like a model with __root__
# because Pydantic v2 handles root differently, but the code checks for "__root__"
model = MagicMock(spec=BaseModel)
# _model_dump(obj, mode="json", ...) -> model.model_dump(mode="json", ...)
model.model_dump.return_value = {"__root__": [1, 2, 3]}
assert jsonable_encoder(model) == [1, 2, 3]
def test_jsonable_encoder_dataclass(self):
obj = MockDataclass(name="test", value=1)
assert jsonable_encoder(obj) == {"name": "test", "value": 1}
# Test dataclass type (should not be treated as instance)
# It should fall back to vars() or dict() or at least not crash
with pytest.raises(ValueError):
jsonable_encoder(MockDataclass)
def test_jsonable_encoder_enum(self):
assert jsonable_encoder(MockEnum.A) == "a"
def test_jsonable_encoder_path(self):
assert jsonable_encoder(Path("/tmp/test")) == "/tmp/test"
assert jsonable_encoder(PurePath("/tmp/test")) == "/tmp/test"
def test_jsonable_encoder_decimal(self):
# In jsonable_encoder, Decimal is formatted as string via format(obj, "f")
assert jsonable_encoder(Decimal("1.23")) == "1.23"
assert jsonable_encoder(Decimal("1.000")) == "1.000"
def test_jsonable_encoder_dict(self):
d = {"a": 1, "b": [2, 3], "_private": "hidden"}
assert jsonable_encoder(d) == {"a": 1, "b": [2, 3], "_private": "hidden"}
assert jsonable_encoder(d, excluded_key_prefixes=("_",)) == {"a": 1, "b": [2, 3]}
d_with_none = {"a": 1, "b": None}
assert jsonable_encoder(d_with_none, exclude_none=True) == {"a": 1}
assert jsonable_encoder(d_with_none, exclude_none=False) == {"a": 1, "b": None}
def test_jsonable_encoder_collections(self):
assert jsonable_encoder([1, 2]) == [1, 2]
assert jsonable_encoder((1, 2)) == [1, 2]
assert jsonable_encoder({1, 2}) == [1, 2]
assert jsonable_encoder(frozenset([1, 2])) == [1, 2]
assert jsonable_encoder(deque([1, 2])) == [1, 2]
def gen():
yield 1
yield 2
assert jsonable_encoder(gen()) == [1, 2]
def test_jsonable_encoder_custom_encoder(self):
custom = {int: lambda x: str(x + 1)}
assert jsonable_encoder(1, custom_encoder=custom) == "2"
# Test subclass matching for custom encoder
class SubInt(int):
pass
assert jsonable_encoder(SubInt(1), custom_encoder=custom) == "2"
def test_jsonable_encoder_special_types(self):
# These hit ENCODERS_BY_TYPE or encoders_by_class_tuples
assert jsonable_encoder(b"bytes") == "bytes"
assert jsonable_encoder(Color("red")) == "red"
dt = datetime.datetime(2023, 1, 1, 12, 0, 0)
assert jsonable_encoder(dt) == dt.isoformat()
date = datetime.date(2023, 1, 1)
assert jsonable_encoder(date) == date.isoformat()
time = datetime.time(12, 0, 0)
assert jsonable_encoder(time) == time.isoformat()
td = datetime.timedelta(seconds=60)
assert jsonable_encoder(td) == 60.0
assert jsonable_encoder(IPv4Address("127.0.0.1")) == "127.0.0.1"
assert jsonable_encoder(IPv4Interface("127.0.0.1/24")) == "127.0.0.1/24"
assert jsonable_encoder(IPv4Network("127.0.0.0/24")) == "127.0.0.0/24"
assert jsonable_encoder(IPv6Address("::1")) == "::1"
assert jsonable_encoder(IPv6Interface("::1/128")) == "::1/128"
assert jsonable_encoder(IPv6Network("::/128")) == "::/128"
assert jsonable_encoder(NameEmail(name="test", email="test@example.com")) == "test <test@example.com>"
assert jsonable_encoder(compile("abc")) == "abc"
# Secret types
# Check what they actually return in this environment
res_bytes = jsonable_encoder(SecretBytes(b"secret"))
assert "**********" in res_bytes
res_str = jsonable_encoder(SecretStr("secret"))
assert res_str == "**********"
u = UUID("12345678-1234-5678-1234-567812345678")
assert jsonable_encoder(u) == str(u)
url = AnyUrl("https://example.com")
assert jsonable_encoder(url) == "https://example.com/"
purl = Url("https://example.com")
assert jsonable_encoder(purl) == "https://example.com/"
def test_jsonable_encoder_fallback(self):
# dict(obj) success
obj_dict = MockWithDict({"a": 1})
assert jsonable_encoder(obj_dict) == {"a": 1}
# vars(obj) success
obj_vars = MockWithVars(x=10, y=20)
assert jsonable_encoder(obj_vars) == {"x": 10, "y": 20}
# error fallback
class ReallyUnserializable:
__slots__ = ["__weakref__"] # No __dict__
def __iter__(self):
raise TypeError("not iterable")
with pytest.raises(ValueError) as exc:
jsonable_encoder(ReallyUnserializable())
assert "not iterable" in str(exc.value)
def test_jsonable_encoder_nested(self):
data = {
"model": MockPydanticModel(name="test", age=20),
"list": [Decimal("1.1"), {MockEnum.A: Path("/tmp")}],
"set": {1, 2},
}
expected = {
"model": {"name": "test", "age": 20},
"list": ["1.1", {"a": "/tmp"}],
"set": [1, 2],
}
assert jsonable_encoder(data) == expected