返回提交历史
Modified
.gitignore
+2
-1
Modified
docker/Dockerfile
+1
-1
Deleted
g4f.dev
+0
-1
Added
g4f/Provider/Kimi.py
+104
-0
Modified
g4f/Provider/__init__.py
+1
-0
Modified
g4f/Provider/needs_auth/LMArenaBeta.py
+25
-2
Deleted
g4f/Provider/needs_auth/Sora.py
+0
-0
Modified
g4f/tools/run_tools.py
+1
-0
XFEstudio/gpt4free
Add Kimi provider, add vision support to LMArenaBeta
8892b00a
代码差异
8 个文件
+134
-5
@@ -36,4 +36,5 @@ projects/windows/
36
36
37
37
*.bak
38
38
*.backup
39
.env
39
.env
40
g4f.dev/
@@ -19,7 +19,7 @@ RUN if [ "$G4F_VERSION" = "" ] ; then \
19
19
RUN apt-get -qqy update \
20
20
&& apt-get -qqy upgrade \
21
21
&& apt-get -qyy autoremove \
22
&& apt-get -qqy install python3 python-is-python3 pip ffmpeg flac \
22
&& apt-get -qqy install python3 python-is-python3 pip ffmpeg flac libavcodec-extra \
23
23
&& apt-get -qyy remove openjdk-11-jre-headless \
24
24
&& apt-get -qyy autoremove \
25
25
&& apt-get -qyy clean \
@@ -1 +0,0 @@
1
Subproject commit b3a9831dd9b10e90f17bcf6524ff48863ac8112d
@@ -0,0 +1,104 @@
1
from __future__ import annotations
2
3
import random
4
from typing import AsyncIterator
5
6
from .base_provider import AsyncAuthedProvider, ProviderModelMixin
7
from ..providers.helper import get_last_user_message
8
from ..requests import StreamSession, see_stream
9
from ..providers.response import AuthResult, TitleGeneration, JsonConversation, FinishReason
10
from ..typing import AsyncResult, Messages
11
12
class Kimi(AsyncAuthedProvider, ProviderModelMixin):
13
url = "https://www.kimi.com"
14
working = True
15
active_by_default = True
16
default_model = "kimi-k2"
17
models = [default_model]
18
19
@classmethod
20
async def on_auth_async(cls, proxy: str = None, **kwargs) -> AsyncIterator:
21
device_id = str(random.randint(1000000000000000, 9999999999999999))
22
async with StreamSession(proxy=proxy, impersonate="chrome") as session:
23
async with session.post(
24
"https://www.kimi.com/api/device/register",
25
json={},
26
headers={
27
"x-msh-device-id": device_id,
28
"x-msh-platform": "web",
29
"x-traffic-id": device_id
30
}
31
) as response:
32
if response.status != 200:
33
raise Exception("Failed to register device")
34
data = await response.json()
35
if not data.get("access_token"):
36
raise Exception("No access token received")
37
yield AuthResult(
38
api_key=data.get("access_token"),
39
device_id=device_id,
40
)
41
42
@classmethod
43
async def create_authed(
44
cls,
45
model: str,
46
messages: Messages,
47
auth_result: AuthResult,
48
proxy: str = None,
49
conversation: JsonConversation = None,
50
web_search: bool = False,
51
**kwargs
52
) -> AsyncResult:
53
pass
54
async with StreamSession(
55
proxy=proxy,
56
impersonate="chrome",
57
headers={
58
"Authorization": f"Bearer {auth_result.api_key}",
59
}
60
) as session:
61
if conversation is None:
62
async with session.post("https://www.kimi.com/api/chat", json={
63
"name":"未命名会话",
64
"born_from":"home",
65
"kimiplus_id":"kimi",
66
"is_example":False,
67
"source":"web",
68
"tags":[]
69
}) as response:
70
if response.status != 200:
71
raise Exception("Failed to create chat")
72
chat_data = await response.json()
73
conversation = JsonConversation(chat_id=chat_data.get("id"))
74
data = {
75
"kimiplus_id": "kimi",
76
"extend": {"sidebar": True},
77
"model": model,
78
"use_search": web_search,
79
"messages": [
80
{
81
"role": "user",
82
"content": get_last_user_message(messages)
83
}
84
],
85
"refs": [],
86
"history": [],
87
"scene_labels": [],
88
"use_semantic_memory": False,
89
"use_deep_research": False
90
}
91
async with session.post(
92
f"https://www.kimi.com/api/chat/{conversation.chat_id}/completion/stream",
93
json=data
94
) as response:
95
if response.status != 200:
96
raise Exception("Failed to start chat completion")
97
async for line in see_stream(response):
98
if line.get("event") == "cmpl":
99
yield line.get("text")
100
elif line.get("event") == "rename":
101
yield TitleGeneration(line.get("text"))
102
elif line.get("event") == "all_done":
103
yield FinishReason("stop")
104
break
@@ -41,6 +41,7 @@ from .DeepInfraChat import DeepInfraChat
41
41
from .DuckDuckGo import DuckDuckGo
42
42
from .Free2GPT import Free2GPT
43
43
from .ImageLabs import ImageLabs
44
from .Kimi import Kimi
44
45
from .LambdaChat import LambdaChat
45
46
from .LegacyLMArena import LegacyLMArena
46
47
from .OIVSCodeSer2 import OIVSCodeSer2
@@ -4,12 +4,14 @@ import time
4
4
import uuid
5
5
import json
6
6
import asyncio
7
import os
7
8
8
from ...typing import AsyncResult, Messages
9
from ...typing import AsyncResult, Messages, MediaListType
9
10
from ...requests import StreamSession, get_args_from_nodriver, raise_for_status, merge_cookies
10
11
from ...requests import DEFAULT_HEADERS, has_nodriver
11
12
from ...errors import ModelNotFoundError
12
13
from ...providers.response import FinishReason, Usage, JsonConversation, ImageResponse
14
from ...tools.media import merge_media
13
15
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin,AuthFileMixin
14
16
from ..helper import get_last_user_message
15
17
from ... import debug
@@ -114,6 +116,7 @@ models = [
114
116
]
115
117
text_models = {model["publicName"]: model["id"] for model in models if "text" in model["capabilities"]["outputCapabilities"]}
116
118
image_models = {model["publicName"]: model["id"] for model in models if "image" in model["capabilities"]["outputCapabilities"]}
119
vision_models = [model["publicName"] for model in models if "image" in model["capabilities"]["inputCapabilities"]]
117
120
118
121
class LMArenaBeta(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
119
122
label = "LMArena (New)"
@@ -124,6 +127,7 @@ class LMArenaBeta(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
124
127
default_model = list(text_models.keys())[0]
125
128
models = list(text_models) + list(image_models)
126
129
image_models = list(image_models)
130
vision_models = vision_models
127
131
128
132
@classmethod
129
133
async def create_async_generator(
@@ -131,6 +135,7 @@ class LMArenaBeta(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
131
135
model: str,
132
136
messages: Messages,
133
137
conversation: JsonConversation = None,
138
media: MediaListType = None,
134
139
proxy: str = None,
135
140
timeout: int = None,
136
141
**kwargs
@@ -181,7 +186,15 @@ class LMArenaBeta(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
181
186
"id": userMessageId,
182
187
"role": "user",
183
188
"content": prompt,
184
"experimental_attachments": [],
189
"experimental_attachments": [
190
{
191
"name": name or os.path.basename(url),
192
"contentType": get_content_type(url),
193
"url": url
194
}
195
for url, name in list(merge_media(media, messages))
196
if url.startswith("https://")
197
],
185
198
"parentMessageIds": [] if conversation is None else conversation.message_ids,
186
199
"participantPosition": "a",
187
200
"modelId": None,
@@ -233,3 +246,13 @@ class LMArenaBeta(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
233
246
# Save the args to cache file
234
247
with cache_file.open("w") as f:
235
248
json.dump(args, f)
249
250
def get_content_type(url: str) -> str:
251
if url.endswith(".webp"):
252
return "image/webp"
253
elif url.endswith(".png"):
254
return "image/png"
255
elif url.endswith(".jpg") or url.endswith(".jpeg"):
256
return "image/jpeg"
257
else:
258
return "application/octet-stream"
此文件没有可显示的逐行差异。
@@ -129,6 +129,7 @@ class AuthManager:
129
129
"GeminiPro": "Gemini",
130
130
"PollinationsAI": "Pollinations",
131
131
"OpenaiAPI": "Openai",
132
"PuterJS": "Puter",
132
133
}
133
134
134
135
@classmethod