Skip to content

Commit

Permalink
Fix api streaming, fix AsyncClient (#2357)
Browse files Browse the repository at this point in the history
* Fix api streaming, fix AsyncClient, Improve Client class, Some providers fixes, Update models list, Fix some tests, Update model list in Airforce provid
er, Add OpenAi image generation url to api, Fix reload and debug in api arguments, Fix websearch in gui

* Fix Cloadflare and Pi and AmigoChat provider

* Fix conversation support in DDG provider, Add cloudflare bypass with nodriver

* Fix unittests without curl_cffi
  • Loading branch information
hlohaus authored Nov 16, 2024
1 parent bc79969 commit 6ce493d
Show file tree
Hide file tree
Showing 34 changed files with 1,157 additions and 1,128 deletions.
13 changes: 9 additions & 4 deletions etc/examples/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@
"provider": "",
"stream": True,
"messages": [
{"role": "assistant", "content": "What can you do? Who are you?"}
{"role": "user", "content": "What can you do? Who are you?"}
]
}
lines = requests.post(url, json=body, stream=True).iter_lines()
for line in lines:
response = requests.post(url, json=body, stream=True)
response.raise_for_status()
for line in response.iter_lines():
if line.startswith(b"data: "):
try:
print(json.loads(line[6:]).get("choices", [{"delta": {}}])[0]["delta"].get("content", ""), end="")
json_data = json.loads(line[6:])
if json_data.get("error"):
print(json_data)
break
print(json_data.get("choices", [{"delta": {}}])[0]["delta"].get("content", ""), end="")
except json.JSONDecodeError:
pass
print()
6 changes: 3 additions & 3 deletions etc/examples/image_api.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import requests
url = "http://localhost:1337/v1/images/generations"
body = {
"prompt": "heaven for dogs",
"provider": "OpenaiAccount",
"response_format": "b64_json",
"model": "dall-e",
"prompt": "hello world user",
#"response_format": "b64_json",
}
data = requests.post(url, json=body, stream=True).json()
print(data)
6 changes: 3 additions & 3 deletions etc/tool/copilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,6 @@ def main():
if not pull:
print(f"No PR number found")
exit()
if pull.get_reviews().totalCount > 0 or pull.get_issue_comments().totalCount > 0:
print(f"Has already a review")
exit()
diff = get_diff(pull.diff_url)
except Exception as e:
print(f"Error get details: {e.__class__.__name__}: {e}")
Expand All @@ -231,6 +228,9 @@ def main():
except Exception as e:
print(f"Error create review: {e}")
exit(1)
if pull.get_reviews().totalCount > 0 or pull.get_issue_comments().totalCount > 0:
pull.create_issue_comment(body=review)
return
try:
comments = analyze_code(pull, diff)
except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion etc/unittest/__main__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import unittest

from .asyncio import *
from .backend import *
from .main import *
from .model import *
from .client import *
from .client import *
from .include import *
from .integration import *

Expand Down
33 changes: 12 additions & 21 deletions etc/unittest/backend.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
from __future__ import annotations

import unittest
import asyncio
from unittest.mock import MagicMock
from .mocks import ProviderMock
import g4f
from g4f.errors import MissingRequirementsError

try:
from g4f.gui.server.backend import Backend_Api, get_error_message
from g4f.gui.server.backend import Backend_Api
has_requirements = True
except:
has_requirements = False
try:
from duckduckgo_search.exceptions import DuckDuckGoSearchException
except ImportError:
class DuckDuckGoSearchException:
pass

class TestBackendApi(unittest.TestCase):

Expand All @@ -31,28 +35,15 @@ def test_get_models(self):

def test_get_providers(self):
response = self.api.get_providers()
self.assertIsInstance(response, list)
self.assertIsInstance(response, dict)
self.assertTrue(len(response) > 0)

def test_search(self):
from g4f.gui.server.internet import search
try:
result = asyncio.run(search("Hello"))
except DuckDuckGoSearchException as e:
self.skipTest(e)
except MissingRequirementsError:
self.skipTest("search is not installed")
self.assertEqual(5, len(result))

class TestUtilityFunctions(unittest.TestCase):

def setUp(self):
if not has_requirements:
self.skipTest("gui is not installed")

def test_get_error_message(self):
g4f.debug.last_provider = ProviderMock
exception = Exception("Message")
result = get_error_message(exception)
self.assertEqual("ProviderMock: Exception: Message", result)

if __name__ == '__main__':
unittest.main()
self.assertEqual(5, len(result))
76 changes: 63 additions & 13 deletions etc/unittest/client.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
from __future__ import annotations

import unittest

from g4f.client import Client, ChatCompletion, ChatCompletionChunk
from g4f.client import Client, AsyncClient, ChatCompletion, ChatCompletionChunk
from .mocks import AsyncGeneratorProviderMock, ModelProviderMock, YieldProviderMock

DEFAULT_MESSAGES = [{'role': 'user', 'content': 'Hello'}]

class AsyncTestPassModel(unittest.IsolatedAsyncioTestCase):

async def test_response(self):
client = Client(provider=AsyncGeneratorProviderMock)
response = await client.chat.completions.async_create(DEFAULT_MESSAGES, "")
client = AsyncClient(provider=AsyncGeneratorProviderMock)
response = await client.chat.completions.create(DEFAULT_MESSAGES, "")
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("Mock", response.choices[0].message.content)

async def test_pass_model(self):
client = Client(provider=ModelProviderMock)
response = await client.chat.completions.async_create(DEFAULT_MESSAGES, "Hello")
client = AsyncClient(provider=ModelProviderMock)
response = await client.chat.completions.create(DEFAULT_MESSAGES, "Hello")
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("Hello", response.choices[0].message.content)

async def test_max_tokens(self):
client = Client(provider=YieldProviderMock)
client = AsyncClient(provider=YieldProviderMock)
messages = [{'role': 'user', 'content': chunk} for chunk in ["How ", "are ", "you", "?"]]
response = await client.chat.completions.async_create(messages, "Hello", max_tokens=1)
response = await client.chat.completions.create(messages, "Hello", max_tokens=1)
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("How ", response.choices[0].message.content)
response = await client.chat.completions.async_create(messages, "Hello", max_tokens=2)
response = await client.chat.completions.create(messages, "Hello", max_tokens=2)
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("How are ", response.choices[0].message.content)

async def test_max_stream(self):
client = Client(provider=YieldProviderMock)
client = AsyncClient(provider=YieldProviderMock)
messages = [{'role': 'user', 'content': chunk} for chunk in ["How ", "are ", "you", "?"]]
response = await client.chat.completions.async_create(messages, "Hello", stream=True)
response = client.chat.completions.create(messages, "Hello", stream=True)
async for chunk in response:
chunk: ChatCompletionChunk = chunk
self.assertIsInstance(chunk, ChatCompletionChunk)
if chunk.choices[0].delta.content is not None:
self.assertIsInstance(chunk.choices[0].delta.content, str)
messages = [{'role': 'user', 'content': chunk} for chunk in ["You ", "You ", "Other", "?"]]
response = await client.chat.completions.async_create(messages, "Hello", stream=True, max_tokens=2)
response = client.chat.completions.create(messages, "Hello", stream=True, max_tokens=2)
response_list = []
async for chunk in response:
response_list.append(chunk)
Expand All @@ -48,11 +51,58 @@ async def test_max_stream(self):
self.assertEqual(chunk.choices[0].delta.content, "You ")

async def test_stop(self):
client = AsyncClient(provider=YieldProviderMock)
messages = [{'role': 'user', 'content': chunk} for chunk in ["How ", "are ", "you", "?"]]
response = await client.chat.completions.create(messages, "Hello", stop=["and"])
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("How are you?", response.choices[0].message.content)

class TestPassModel(unittest.TestCase):

def test_response(self):
client = Client(provider=AsyncGeneratorProviderMock)
response = client.chat.completions.create(DEFAULT_MESSAGES, "")
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("Mock", response.choices[0].message.content)

def test_pass_model(self):
client = Client(provider=ModelProviderMock)
response = client.chat.completions.create(DEFAULT_MESSAGES, "Hello")
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("Hello", response.choices[0].message.content)

def test_max_tokens(self):
client = Client(provider=YieldProviderMock)
messages = [{'role': 'user', 'content': chunk} for chunk in ["How ", "are ", "you", "?"]]
response = client.chat.completions.create(messages, "Hello", max_tokens=1)
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("How ", response.choices[0].message.content)
response = client.chat.completions.create(messages, "Hello", max_tokens=2)
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("How are ", response.choices[0].message.content)

def test_max_stream(self):
client = Client(provider=YieldProviderMock)
messages = [{'role': 'user', 'content': chunk} for chunk in ["How ", "are ", "you", "?"]]
response = client.chat.completions.create(messages, "Hello", stream=True)
for chunk in response:
self.assertIsInstance(chunk, ChatCompletionChunk)
if chunk.choices[0].delta.content is not None:
self.assertIsInstance(chunk.choices[0].delta.content, str)
messages = [{'role': 'user', 'content': chunk} for chunk in ["You ", "You ", "Other", "?"]]
response = client.chat.completions.create(messages, "Hello", stream=True, max_tokens=2)
response_list = list(response)
self.assertEqual(len(response_list), 3)
for chunk in response_list:
if chunk.choices[0].delta.content is not None:
self.assertEqual(chunk.choices[0].delta.content, "You ")

def test_stop(self):
client = Client(provider=YieldProviderMock)
messages = [{'role': 'user', 'content': chunk} for chunk in ["How ", "are ", "you", "?"]]
response = await client.chat.completions.async_create(messages, "Hello", stop=["and"])
response = client.chat.completions.create(messages, "Hello", stop=["and"])
self.assertIsInstance(response, ChatCompletion)
self.assertEqual("How are you?", response.choices[0].message.content)

if __name__ == '__main__':
unittest.main()
unittest.main()
Loading

0 comments on commit 6ce493d

Please sign in to comment.