vectorvein 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ # @Author: Bi Ying
2
+ # @Date: 2024-07-26 14:48:55
3
+ from ..types.enums import BackendType
4
+ from ..types.defaults import OPENAI_DEFAULT_MODEL
5
+ from .openai_compatible_client import OpenAICompatibleChatClient, AsyncOpenAICompatibleChatClient
6
+
7
+
8
+ class OpenAIChatClient(OpenAICompatibleChatClient):
9
+ DEFAULT_MODEL = OPENAI_DEFAULT_MODEL
10
+ BACKEND_NAME = BackendType.OpenAI
11
+
12
+
13
+ class AsyncOpenAIChatClient(AsyncOpenAICompatibleChatClient):
14
+ DEFAULT_MODEL = OPENAI_DEFAULT_MODEL
15
+ BACKEND_NAME = BackendType.OpenAI
@@ -0,0 +1,291 @@
1
+ # @Author: Bi Ying
2
+ # @Date: 2024-07-26 14:48:55
3
+ import json
4
+ import random
5
+ from typing import Union, AsyncGenerator
6
+
7
+ from openai._types import NotGiven, NOT_GIVEN
8
+ from openai._streaming import Stream, AsyncStream
9
+ from openai.types.chat import ChatCompletion, ChatCompletionChunk
10
+ from openai import OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI
11
+
12
+ from .base_client import BaseChatClient, BaseAsyncChatClient
13
+ from .utils import (
14
+ tool_use_re,
15
+ cutoff_messages,
16
+ extract_tool_calls,
17
+ generate_tool_use_system_prompt,
18
+ )
19
+ from ..settings import settings
20
+ from ..types import defaults as defs
21
+ from ..types.enums import ContextLengthControlType, BackendType
22
+
23
+
24
+ class OpenAICompatibleChatClient(BaseChatClient):
25
+ DEFAULT_MODEL: str = ""
26
+ BACKEND_NAME: BackendType
27
+
28
+ def __init__(
29
+ self,
30
+ model: str = "",
31
+ stream: bool = True,
32
+ temperature: float = 0.7,
33
+ context_length_control: ContextLengthControlType = defs.CONTEXT_LENGTH_CONTROL,
34
+ random_endpoint: bool = True,
35
+ endpoint_id: str = "",
36
+ **kwargs,
37
+ ):
38
+ super().__init__(
39
+ model,
40
+ stream,
41
+ temperature,
42
+ context_length_control,
43
+ random_endpoint,
44
+ endpoint_id,
45
+ **kwargs,
46
+ )
47
+
48
+ def create_completion(
49
+ self,
50
+ messages: list = list,
51
+ model: str | None = None,
52
+ stream: bool | None = None,
53
+ temperature: float | None = None,
54
+ max_tokens: int = 2000,
55
+ tools: list | NotGiven = NOT_GIVEN,
56
+ tool_choice: str | NotGiven = NOT_GIVEN,
57
+ ):
58
+ if model is not None:
59
+ self.model = model
60
+ if stream is not None:
61
+ self.stream = stream
62
+ if temperature is not None:
63
+ self.temperature = temperature
64
+
65
+ self.model_setting = self.backend_settings.models[self.model]
66
+
67
+ if self.random_endpoint:
68
+ self.random_endpoint = True
69
+ self.endpoint_id = random.choice(self.backend_settings.models[self.model].endpoints)
70
+ self.endpoint = settings.get_endpoint(self.endpoint_id)
71
+
72
+ if self.endpoint.is_azure:
73
+ self._client = AzureOpenAI(
74
+ azure_endpoint=self.endpoint.api_base,
75
+ api_key=self.endpoint.api_key,
76
+ api_version="2024-05-01-preview",
77
+ )
78
+ else:
79
+ self._client = OpenAI(
80
+ api_key=self.endpoint.api_key,
81
+ base_url=self.endpoint.api_base,
82
+ )
83
+
84
+ if self.context_length_control == ContextLengthControlType.Latest:
85
+ messages = cutoff_messages(
86
+ messages,
87
+ max_count=self.model_setting.context_length,
88
+ backend=self.BACKEND_NAME,
89
+ model=self.model_setting.id,
90
+ )
91
+
92
+ if tools:
93
+ if self.model_setting.function_call_available:
94
+ tools_params = dict(tools=tools, tool_choice=tool_choice)
95
+ else:
96
+ tools_str = json.dumps(tools, ensure_ascii=False, indent=None)
97
+ additional_system_prompt = generate_tool_use_system_prompt(tools=tools_str)
98
+ if messages and messages[0].get("role") == "system":
99
+ messages[0]["content"] += "\n\n" + additional_system_prompt
100
+ else:
101
+ messages.insert(0, {"role": "system", "content": additional_system_prompt})
102
+ tools_params = {}
103
+ else:
104
+ tools_params = {}
105
+
106
+ response: ChatCompletion | Stream[ChatCompletionChunk] = self._client.chat.completions.create(
107
+ model=self.model_setting.id,
108
+ messages=messages,
109
+ stream=self.stream,
110
+ temperature=self.temperature,
111
+ max_tokens=max_tokens,
112
+ **tools_params,
113
+ )
114
+
115
+ if self.stream:
116
+
117
+ def generator():
118
+ full_content = ""
119
+ result = {}
120
+ for chunk in response:
121
+ if len(chunk.choices) == 0:
122
+ continue
123
+ if self.model_setting.function_call_available:
124
+ yield chunk.choices[0].delta.model_dump()
125
+ else:
126
+ message = chunk.choices[0].delta.model_dump()
127
+ full_content += message["content"] if message["content"] else ""
128
+ if tools:
129
+ tool_call_data = extract_tool_calls(full_content)
130
+ if tool_call_data:
131
+ message["tool_calls"] = tool_call_data["tool_calls"]
132
+ if full_content in ("<", "<|", "<|▶", "<|▶|") or full_content.startswith("<|▶|>"):
133
+ message["content"] = ""
134
+ result = message
135
+ continue
136
+ yield message
137
+ if result:
138
+ yield result
139
+
140
+ return generator()
141
+ else:
142
+ result = {
143
+ "content": response.choices[0].message.content,
144
+ "usage": response.usage.model_dump(),
145
+ }
146
+ if tools:
147
+ if self.model_setting.function_call_available and response.choices[0].message.tool_calls:
148
+ result["tool_calls"] = [
149
+ tool_call.model_dump() for tool_call in response.choices[0].message.tool_calls
150
+ ]
151
+ else:
152
+ tool_call_data = extract_tool_calls(result["content"])
153
+ if tool_call_data:
154
+ result["tool_calls"] = tool_call_data["tool_calls"]
155
+ result["content"] = tool_use_re.sub("", result["content"])
156
+ return result
157
+
158
+
159
+ class AsyncOpenAICompatibleChatClient(BaseAsyncChatClient):
160
+ DEFAULT_MODEL: str = ""
161
+ BACKEND_NAME: BackendType
162
+
163
+ def __init__(
164
+ self,
165
+ model: str = "",
166
+ stream: bool = True,
167
+ temperature: float = 0.7,
168
+ context_length_control: ContextLengthControlType = defs.CONTEXT_LENGTH_CONTROL,
169
+ random_endpoint: bool = True,
170
+ endpoint_id: str = "",
171
+ **kwargs,
172
+ ):
173
+ super().__init__(
174
+ model,
175
+ stream,
176
+ temperature,
177
+ context_length_control,
178
+ random_endpoint,
179
+ endpoint_id,
180
+ **kwargs,
181
+ )
182
+
183
+ async def create_completion(
184
+ self,
185
+ messages: list = list,
186
+ model: str | None = None,
187
+ stream: bool | None = None,
188
+ temperature: float | None = None,
189
+ max_tokens: int = 2000,
190
+ tools: list | NotGiven = NOT_GIVEN,
191
+ tool_choice: str | NotGiven = NOT_GIVEN,
192
+ ) -> Union[AsyncGenerator[str, None], str]:
193
+ if model is not None:
194
+ self.model = model
195
+ if stream is not None:
196
+ self.stream = stream
197
+ if temperature is not None:
198
+ self.temperature = temperature
199
+
200
+ self.model_setting = self.backend_settings.models[self.model]
201
+
202
+ if self.random_endpoint:
203
+ self.random_endpoint = True
204
+ self.endpoint_id = random.choice(self.backend_settings.models[self.model].endpoints)
205
+ self.endpoint = settings.get_endpoint(self.endpoint_id)
206
+
207
+ if self.endpoint.is_azure:
208
+ self._client = AsyncAzureOpenAI(
209
+ azure_endpoint=self.endpoint.api_base,
210
+ api_key=self.endpoint.api_key,
211
+ api_version="2024-05-01-preview",
212
+ )
213
+ else:
214
+ self._client = AsyncOpenAI(
215
+ api_key=self.endpoint.api_key,
216
+ base_url=self.endpoint.api_base,
217
+ )
218
+
219
+ if self.context_length_control == ContextLengthControlType.Latest:
220
+ messages = cutoff_messages(
221
+ messages,
222
+ max_count=self.model_setting.context_length,
223
+ backend=self.BACKEND_NAME,
224
+ model=self.model_setting.id,
225
+ )
226
+
227
+ if tools:
228
+ if self.model_setting.function_call_available:
229
+ tools_params = dict(tools=tools, tool_choice=tool_choice)
230
+ else:
231
+ tools_str = json.dumps(tools, ensure_ascii=False, indent=None)
232
+ additional_system_prompt = generate_tool_use_system_prompt(tools=tools_str)
233
+ if messages and messages[0].get("role") == "system":
234
+ messages[0]["content"] += "\n\n" + additional_system_prompt
235
+ else:
236
+ messages.insert(0, {"role": "system", "content": additional_system_prompt})
237
+ tools_params = {}
238
+ else:
239
+ tools_params = {}
240
+
241
+ response: ChatCompletion | AsyncStream[ChatCompletionChunk] = await self._client.chat.completions.create(
242
+ model=self.model_setting.id,
243
+ messages=messages,
244
+ stream=self.stream,
245
+ temperature=self.temperature,
246
+ max_tokens=max_tokens,
247
+ **tools_params,
248
+ )
249
+
250
+ if self.stream:
251
+
252
+ async def generator():
253
+ full_content = ""
254
+ result = {}
255
+ async for chunk in response:
256
+ if len(chunk.choices) == 0:
257
+ continue
258
+ if self.model_setting.function_call_available:
259
+ yield chunk.choices[0].delta.model_dump()
260
+ else:
261
+ message = chunk.choices[0].delta.model_dump()
262
+ full_content += message["content"] if message["content"] else ""
263
+ if tools:
264
+ tool_call_data = extract_tool_calls(full_content)
265
+ if tool_call_data:
266
+ message["tool_calls"] = tool_call_data["tool_calls"]
267
+ if full_content in ("<", "<|", "<|▶", "<|▶|") or full_content.startswith("<|▶|>"):
268
+ message["content"] = ""
269
+ result = message
270
+ continue
271
+ yield message
272
+ if result:
273
+ yield result
274
+
275
+ return generator()
276
+ else:
277
+ result = {
278
+ "content": response.choices[0].message.content,
279
+ "usage": response.usage.model_dump(),
280
+ }
281
+ if tools:
282
+ if self.model_setting.function_call_available and response.choices[0].message.tool_calls:
283
+ result["tool_calls"] = [
284
+ tool_call.model_dump() for tool_call in response.choices[0].message.tool_calls
285
+ ]
286
+ else:
287
+ tool_call_data = extract_tool_calls(result["content"])
288
+ if tool_call_data:
289
+ result["tool_calls"] = tool_call_data["tool_calls"]
290
+ result["content"] = tool_use_re.sub("", result["content"])
291
+ return result
@@ -0,0 +1,15 @@
1
+ # @Author: Bi Ying
2
+ # @Date: 2024-07-26 14:48:55
3
+ from ..types.enums import BackendType
4
+ from ..types.defaults import QWEN_DEFAULT_MODEL
5
+ from .openai_compatible_client import OpenAICompatibleChatClient, AsyncOpenAICompatibleChatClient
6
+
7
+
8
+ class QwenChatClient(OpenAICompatibleChatClient):
9
+ DEFAULT_MODEL = QWEN_DEFAULT_MODEL
10
+ BACKEND_NAME = BackendType.Qwen
11
+
12
+
13
+ class AsyncQwenChatClient(AsyncOpenAICompatibleChatClient):
14
+ DEFAULT_MODEL = QWEN_DEFAULT_MODEL
15
+ BACKEND_NAME = BackendType.Qwen