From 20ad08021a32b23fe8fc085664f8a70f6b512e33 Mon Sep 17 00:00:00 2001 From: Heiner Lohaus Date: Sat, 7 Dec 2024 20:07:11 +0100 Subject: [PATCH] Fix deprecated construct method, fix unittests --- etc/unittest/backend.py | 2 +- g4f/client/__init__.py | 20 ++++++++--------- g4f/client/stubs.py | 49 +++++++++++++++++++++++------------------ 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/etc/unittest/backend.py b/etc/unittest/backend.py index a90bf2537c0..75ab6b47013 100644 --- a/etc/unittest/backend.py +++ b/etc/unittest/backend.py @@ -35,7 +35,7 @@ def test_get_models(self): def test_get_providers(self): response = self.api.get_providers() - self.assertIsInstance(response, dict) + self.assertIsInstance(response, list) self.assertTrue(len(response) > 0) def test_search(self): diff --git a/g4f/client/__init__.py b/g4f/client/__init__.py index d95618f195b..52349e7210c 100644 --- a/g4f/client/__init__.py +++ b/g4f/client/__init__.py @@ -74,7 +74,7 @@ def iter_response( finish_reason = "stop" if stream: - yield ChatCompletionChunk.construct(chunk, None, completion_id, int(time.time())) + yield ChatCompletionChunk.model_construct(chunk, None, completion_id, int(time.time())) if finish_reason is not None: break @@ -84,12 +84,12 @@ def iter_response( finish_reason = "stop" if finish_reason is None else finish_reason if stream: - yield ChatCompletionChunk.construct(None, finish_reason, completion_id, int(time.time())) + yield ChatCompletionChunk.model_construct(None, finish_reason, completion_id, int(time.time())) else: if response_format is not None and "type" in response_format: if response_format["type"] == "json_object": content = filter_json(content) - yield ChatCompletion.construct(content, finish_reason, completion_id, int(time.time())) + yield ChatCompletion.model_construct(content, finish_reason, completion_id, int(time.time())) # Synchronous iter_append_model_and_provider function def iter_append_model_and_provider(response: ChatCompletionResponseType) -> ChatCompletionResponseType: @@ -138,7 +138,7 @@ async def async_iter_response( finish_reason = "stop" if stream: - yield ChatCompletionChunk.construct(chunk, None, completion_id, int(time.time())) + yield ChatCompletionChunk.model_construct(chunk, None, completion_id, int(time.time())) if finish_reason is not None: break @@ -146,12 +146,12 @@ async def async_iter_response( finish_reason = "stop" if finish_reason is None else finish_reason if stream: - yield ChatCompletionChunk.construct(None, finish_reason, completion_id, int(time.time())) + yield ChatCompletionChunk.model_construct(None, finish_reason, completion_id, int(time.time())) else: if response_format is not None and "type" in response_format: if response_format["type"] == "json_object": content = filter_json(content) - yield ChatCompletion.construct(content, finish_reason, completion_id, int(time.time())) + yield ChatCompletion.model_construct(content, finish_reason, completion_id, int(time.time())) finally: await safe_aclose(response) @@ -422,7 +422,7 @@ async def _process_image_response( last_provider = get_last_provider(True) if response_format == "url": # Return original URLs without saving locally - images = [Image.construct(url=image, revised_prompt=response.alt) for image in response.get_list()] + images = [Image.model_construct(url=image, revised_prompt=response.alt) for image in response.get_list()] else: # Save locally for None (default) case images = await copy_images(response.get_list(), response.get("cookies"), proxy) @@ -430,11 +430,11 @@ async def _process_image_response( async def process_image_item(image_file: str) -> Image: with open(os.path.join(images_dir, os.path.basename(image_file)), "rb") as file: image_data = base64.b64encode(file.read()).decode() - return Image.construct(b64_json=image_data, revised_prompt=response.alt) + return Image.model_construct(b64_json=image_data, revised_prompt=response.alt) images = await asyncio.gather(*[process_image_item(image) for image in images]) else: - images = [Image.construct(url=f"/images/{os.path.basename(image)}", revised_prompt=response.alt) for image in images] - return ImagesResponse.construct( + images = [Image.model_construct(url=f"/images/{os.path.basename(image)}", revised_prompt=response.alt) for image in images] + return ImagesResponse.model_construct( created=int(time.time()), data=images, model=last_provider.get("model") if model is None else model, diff --git a/g4f/client/stubs.py b/g4f/client/stubs.py index 414651dea3a..575327690ed 100644 --- a/g4f/client/stubs.py +++ b/g4f/client/stubs.py @@ -10,7 +10,7 @@ except ImportError: class BaseModel(): @classmethod - def construct(cls, **data): + def model_construct(cls, **data): new = cls() for key, value in data.items(): setattr(new, key, value) @@ -19,6 +19,13 @@ class Field(): def __init__(self, **config): pass +class BaseModel(BaseModel): + @classmethod + def model_construct(cls, **data): + if hasattr(super(), "model_construct"): + return super().model_construct(**data) + return cls.construct(**data) + class ChatCompletionChunk(BaseModel): id: str object: str @@ -28,21 +35,21 @@ class ChatCompletionChunk(BaseModel): choices: List[ChatCompletionDeltaChoice] @classmethod - def construct( + def model_construct( cls, content: str, finish_reason: str, completion_id: str = None, created: int = None ): - return super().construct( + return super().model_construct( id=f"chatcmpl-{completion_id}" if completion_id else None, object="chat.completion.cunk", created=created, model=None, provider=None, - choices=[ChatCompletionDeltaChoice.construct( - ChatCompletionDelta.construct(content), + choices=[ChatCompletionDeltaChoice.model_construct( + ChatCompletionDelta.model_construct(content), finish_reason )] ) @@ -52,8 +59,8 @@ class ChatCompletionMessage(BaseModel): content: str @classmethod - def construct(cls, content: str): - return super().construct(role="assistant", content=content) + def model_construct(cls, content: str): + return super().model_construct(role="assistant", content=content) class ChatCompletionChoice(BaseModel): index: int @@ -61,8 +68,8 @@ class ChatCompletionChoice(BaseModel): finish_reason: str @classmethod - def construct(cls, message: ChatCompletionMessage, finish_reason: str): - return super().construct(index=0, message=message, finish_reason=finish_reason) + def model_construct(cls, message: ChatCompletionMessage, finish_reason: str): + return super().model_construct(index=0, message=message, finish_reason=finish_reason) class ChatCompletion(BaseModel): id: str @@ -78,21 +85,21 @@ class ChatCompletion(BaseModel): }]) @classmethod - def construct( + def model_construct( cls, content: str, finish_reason: str, completion_id: str = None, created: int = None ): - return super().construct( + return super().model_construct( id=f"chatcmpl-{completion_id}" if completion_id else None, object="chat.completion", created=created, model=None, provider=None, - choices=[ChatCompletionChoice.construct( - ChatCompletionMessage.construct(content), + choices=[ChatCompletionChoice.model_construct( + ChatCompletionMessage.model_construct(content), finish_reason )], usage={ @@ -107,8 +114,8 @@ class ChatCompletionDelta(BaseModel): content: str @classmethod - def construct(cls, content: Optional[str]): - return super().construct(role="assistant", content=content) + def model_construct(cls, content: Optional[str]): + return super().model_construct(role="assistant", content=content) class ChatCompletionDeltaChoice(BaseModel): index: int @@ -116,8 +123,8 @@ class ChatCompletionDeltaChoice(BaseModel): finish_reason: Optional[str] @classmethod - def construct(cls, delta: ChatCompletionDelta, finish_reason: Optional[str]): - return super().construct(index=0, delta=delta, finish_reason=finish_reason) + def model_construct(cls, delta: ChatCompletionDelta, finish_reason: Optional[str]): + return super().model_construct(index=0, delta=delta, finish_reason=finish_reason) class Image(BaseModel): url: Optional[str] @@ -125,8 +132,8 @@ class Image(BaseModel): revised_prompt: Optional[str] @classmethod - def construct(cls, url: str = None, b64_json: str = None, revised_prompt: str = None): - return super().construct(**filter_none( + def model_construct(cls, url: str = None, b64_json: str = None, revised_prompt: str = None): + return super().model_construct(**filter_none( url=url, b64_json=b64_json, revised_prompt=revised_prompt @@ -139,10 +146,10 @@ class ImagesResponse(BaseModel): created: int @classmethod - def construct(cls, data: List[Image], created: int = None, model: str = None, provider: str = None): + def model_construct(cls, data: List[Image], created: int = None, model: str = None, provider: str = None): if created is None: created = int(time()) - return super().construct( + return super().model_construct( data=data, model=model, provider=provider,