unionllm 0.2.0__py2.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.
- unionllm/__init__.py +1 -0
- unionllm/exceptions.py +146 -0
- unionllm/main.py +142 -0
- unionllm/models.py +28 -0
- unionllm/providers/__init__.py +0 -0
- unionllm/providers/baichuan.py +143 -0
- unionllm/providers/base_provider.py +286 -0
- unionllm/providers/coze.py +273 -0
- unionllm/providers/coze_back.py +269 -0
- unionllm/providers/deepseek.py +83 -0
- unionllm/providers/dify.py +265 -0
- unionllm/providers/doubao.py +158 -0
- unionllm/providers/fastgpt.py +183 -0
- unionllm/providers/gemini.py +444 -0
- unionllm/providers/lingyi.py +81 -0
- unionllm/providers/litellm.py +82 -0
- unionllm/providers/minimax.py +182 -0
- unionllm/providers/moonshot.py +81 -0
- unionllm/providers/qwen.py +269 -0
- unionllm/providers/stepfun.py +80 -0
- unionllm/providers/tiangong.py +160 -0
- unionllm/providers/wenxin.py +218 -0
- unionllm/providers/xai.py +82 -0
- unionllm/providers/xunfei.py +256 -0
- unionllm/providers/xunfei_http.py +85 -0
- unionllm/providers/zhipu.py +76 -0
- unionllm/utils.py +517 -0
- unionllm-0.2.0.dist-info/LICENSE +3 -0
- unionllm-0.2.0.dist-info/METADATA +29 -0
- unionllm-0.2.0.dist-info/RECORD +31 -0
- unionllm-0.2.0.dist-info/WHEEL +4 -0
unionllm/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main import UnionLLM, unionchat
|
unionllm/exceptions.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
class UnionLLMError(Exception):
|
|
2
|
+
"""Base exception class for UnionLLM."""
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
class ProviderError(UnionLLMError):
|
|
6
|
+
"""Exception raised when there is an issue with the model provider."""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class APICallError(UnionLLMError):
|
|
10
|
+
"""Exception raised when an API call fails."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
from openai import (
|
|
14
|
+
AuthenticationError,
|
|
15
|
+
BadRequestError,
|
|
16
|
+
RateLimitError,
|
|
17
|
+
APIStatusError,
|
|
18
|
+
OpenAIError,
|
|
19
|
+
APIError,
|
|
20
|
+
APITimeoutError,
|
|
21
|
+
APIConnectionError,
|
|
22
|
+
)
|
|
23
|
+
import httpx
|
|
24
|
+
|
|
25
|
+
class AuthenticationError(AuthenticationError): # type: ignore
|
|
26
|
+
def __init__(self, message, llm_provider, model, response: httpx.Response):
|
|
27
|
+
self.status_code = 401
|
|
28
|
+
self.message = message
|
|
29
|
+
self.llm_provider = llm_provider
|
|
30
|
+
self.model = model
|
|
31
|
+
super().__init__(
|
|
32
|
+
self.message,
|
|
33
|
+
response=response,
|
|
34
|
+
body=None
|
|
35
|
+
) # Call the base class constructor with the parameters it needs
|
|
36
|
+
|
|
37
|
+
class BadRequestError(BadRequestError): # type: ignore
|
|
38
|
+
def __init__(self, message, model, llm_provider, response: httpx.Response):
|
|
39
|
+
self.status_code = 400
|
|
40
|
+
self.message = message
|
|
41
|
+
self.model = model
|
|
42
|
+
self.llm_provider = llm_provider
|
|
43
|
+
super().__init__(
|
|
44
|
+
self.message,
|
|
45
|
+
response=response,
|
|
46
|
+
body=None
|
|
47
|
+
) # Call the base class constructor with the parameters it needs
|
|
48
|
+
|
|
49
|
+
class Timeout(APITimeoutError): # type: ignore
|
|
50
|
+
def __init__(self, message, model, llm_provider, request: httpx.Request):
|
|
51
|
+
self.status_code = 408
|
|
52
|
+
self.message = message
|
|
53
|
+
self.model = model
|
|
54
|
+
self.llm_provider = llm_provider
|
|
55
|
+
super().__init__(
|
|
56
|
+
request=request
|
|
57
|
+
) # Call the base class constructor with the parameters it needs
|
|
58
|
+
|
|
59
|
+
class RateLimitError(RateLimitError): # type: ignore
|
|
60
|
+
def __init__(self, message, llm_provider, model, response: httpx.Response):
|
|
61
|
+
self.status_code = 429
|
|
62
|
+
self.message = message
|
|
63
|
+
self.llm_provider = llm_provider
|
|
64
|
+
self.modle = model
|
|
65
|
+
super().__init__(
|
|
66
|
+
self.message,
|
|
67
|
+
response=response,
|
|
68
|
+
body=None
|
|
69
|
+
) # Call the base class constructor with the parameters it needs
|
|
70
|
+
|
|
71
|
+
# sub class of rate limit error - meant to give more granularity for error handling context window exceeded errors
|
|
72
|
+
class ContextWindowExceededError(BadRequestError): # type: ignore
|
|
73
|
+
def __init__(self, message, model, llm_provider, response: httpx.Response):
|
|
74
|
+
self.status_code = 400
|
|
75
|
+
self.message = message
|
|
76
|
+
self.model = model
|
|
77
|
+
self.llm_provider = llm_provider
|
|
78
|
+
super().__init__(
|
|
79
|
+
message=self.message,
|
|
80
|
+
model=self.model, # type: ignore
|
|
81
|
+
llm_provider=self.llm_provider, # type: ignore
|
|
82
|
+
response=response
|
|
83
|
+
) # Call the base class constructor with the parameters it needs
|
|
84
|
+
|
|
85
|
+
class ServiceUnavailableError(APIStatusError): # type: ignore
|
|
86
|
+
def __init__(self, message, llm_provider, model):
|
|
87
|
+
self.status_code = 503
|
|
88
|
+
self.message = message
|
|
89
|
+
self.llm_provider = llm_provider
|
|
90
|
+
self.model = model
|
|
91
|
+
super().__init__(
|
|
92
|
+
self.message
|
|
93
|
+
) # Call the base class constructor with the parameters it needs
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# raise this when the API returns an invalid response object - https://github.com/openai/openai-python/blob/1be14ee34a0f8e42d3f9aa5451aa4cb161f1781f/openai/api_requestor.py#L401
|
|
97
|
+
class APIError(APIError): # type: ignore
|
|
98
|
+
def __init__(self, status_code, message, llm_provider, model, request: httpx.Request):
|
|
99
|
+
self.status_code = status_code
|
|
100
|
+
self.message = message
|
|
101
|
+
self.llm_provider = llm_provider
|
|
102
|
+
self.model = model
|
|
103
|
+
super().__init__(
|
|
104
|
+
self.message,
|
|
105
|
+
request=request # type: ignore
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# raised if an invalid request (not get, delete, put, post) is made
|
|
109
|
+
class APIConnectionError(APIConnectionError): # type: ignore
|
|
110
|
+
def __init__(self, message, llm_provider, model):
|
|
111
|
+
self.message = message
|
|
112
|
+
self.llm_provider = llm_provider
|
|
113
|
+
self.model = model
|
|
114
|
+
super().__init__(
|
|
115
|
+
self.message
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
class OpenAIError(OpenAIError): # type: ignore
|
|
119
|
+
def __init__(self, original_exception):
|
|
120
|
+
self.status_code = original_exception.http_status
|
|
121
|
+
super().__init__(
|
|
122
|
+
http_body=original_exception.http_body,
|
|
123
|
+
http_status=original_exception.http_status,
|
|
124
|
+
json_body=original_exception.json_body,
|
|
125
|
+
headers=original_exception.headers,
|
|
126
|
+
code=original_exception.code,
|
|
127
|
+
)
|
|
128
|
+
self.llm_provider = "openai"
|
|
129
|
+
|
|
130
|
+
class BudgetExceededError(Exception):
|
|
131
|
+
def __init__(self, current_cost, max_budget):
|
|
132
|
+
self.current_cost = current_cost
|
|
133
|
+
self.max_budget = max_budget
|
|
134
|
+
message = f"Budget has been exceeded! Current cost: {current_cost}, Max budget: {max_budget}"
|
|
135
|
+
super().__init__(message)
|
|
136
|
+
|
|
137
|
+
## DEPRECATED ##
|
|
138
|
+
class InvalidRequestError(BadRequestError): # type: ignore
|
|
139
|
+
def __init__(self, message, model, llm_provider):
|
|
140
|
+
self.status_code = 400
|
|
141
|
+
self.message = message
|
|
142
|
+
self.model = model
|
|
143
|
+
self.llm_provider = llm_provider
|
|
144
|
+
super().__init__(
|
|
145
|
+
self.message, f"{self.model}"
|
|
146
|
+
) # Call the base class constructor with the parameters it needs
|
unionllm/main.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import asyncio
|
|
3
|
+
import os
|
|
4
|
+
from functools import partial
|
|
5
|
+
|
|
6
|
+
from typing import Any, List, Optional
|
|
7
|
+
from .providers import zhipu, moonshot, xai, minimax, qwen, tiangong, baichuan, wenxin, xunfei, xunfei_http, dify, fastgpt, coze, litellm, lingyi, stepfun, doubao, deepseek, gemini
|
|
8
|
+
from .exceptions import ProviderError
|
|
9
|
+
# from litellm import completion as litellm_completion
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class UnionLLM:
|
|
14
|
+
def __init__(self, provider: Optional[str] = None, **kwargs):
|
|
15
|
+
self.provider = provider.lower() if provider else None
|
|
16
|
+
self.litellm_call_type = None
|
|
17
|
+
|
|
18
|
+
if self.provider == "zhipuai":
|
|
19
|
+
self.provider_instance = zhipu.ZhipuAIProvider(**kwargs)
|
|
20
|
+
elif self.provider == "moonshot":
|
|
21
|
+
self.provider_instance = moonshot.MoonshotAIProvider(**kwargs)
|
|
22
|
+
elif self.provider == "minimax":
|
|
23
|
+
self.provider_instance = minimax.MinimaxAIProvider(**kwargs)
|
|
24
|
+
elif self.provider == "qwen":
|
|
25
|
+
# 根据model判断是否需要使用dashscope的api, 否则使用openai的兼容api
|
|
26
|
+
model = kwargs.get("model")
|
|
27
|
+
# 预留特殊模型通过litellm调用
|
|
28
|
+
if model is not None and model not in ["qwen-special-model"]:
|
|
29
|
+
self.provider_instance = qwen.QwenAIProvider(**kwargs)
|
|
30
|
+
else:
|
|
31
|
+
# 使用DASHSCOPE_API_KEY作为OPENAI_API_KEY
|
|
32
|
+
os.environ["OPENAI_API_KEY"] = os.environ.get("DASHSCOPE_API_KEY")
|
|
33
|
+
self.provider_instance = litellm.LiteLLMProvider(**kwargs)
|
|
34
|
+
self.litellm_call_type = 3
|
|
35
|
+
elif self.provider == "tiangong":
|
|
36
|
+
self.provider_instance = tiangong.TianGongAIProvider(**kwargs)
|
|
37
|
+
elif self.provider == "baichuan":
|
|
38
|
+
self.provider_instance = baichuan.BaiChuanAIProvider(**kwargs)
|
|
39
|
+
elif self.provider == "wenxin":
|
|
40
|
+
self.provider_instance = wenxin.WenXinAIProvider(**kwargs)
|
|
41
|
+
elif self.provider == "xunfei":
|
|
42
|
+
self.provider_instance = xunfei.XunfeiAIProvider(**kwargs)
|
|
43
|
+
elif self.provider == "xunfei_http":
|
|
44
|
+
self.provider_instance = xunfei_http.XunfeiHTTPProvider(**kwargs)
|
|
45
|
+
# elif self.provider == "xai":
|
|
46
|
+
# self.provider_instance = xai.XAIHTTPProvider(**kwargs)
|
|
47
|
+
elif self.provider == "dify":
|
|
48
|
+
self.provider_instance = dify.DifyAIProvider(**kwargs)
|
|
49
|
+
elif self.provider == "fastgpt":
|
|
50
|
+
self.provider_instance = fastgpt.FastGPTProvider(**kwargs)
|
|
51
|
+
elif self.provider == "coze":
|
|
52
|
+
self.provider_instance = coze.CozeAIProvider(**kwargs)
|
|
53
|
+
elif self.provider == "lingyi":
|
|
54
|
+
self.provider_instance = lingyi.LingyiAIProvider(**kwargs)
|
|
55
|
+
elif self.provider == "stepfun":
|
|
56
|
+
self.provider_instance = stepfun.StepfunAIProvider(**kwargs)
|
|
57
|
+
elif self.provider == "doubao":
|
|
58
|
+
self.provider_instance = doubao.DouBaoAIProvider(**kwargs)
|
|
59
|
+
elif self.provider == "deepseek":
|
|
60
|
+
self.provider_instance = deepseek.DeepSeekAIProvider(**kwargs)
|
|
61
|
+
# elif self.provider == "gemini":
|
|
62
|
+
# self.provider_instance = gemini.GeminiAIProvider(**kwargs)
|
|
63
|
+
elif self.provider == "gemini" and kwargs.get("multimodal") and kwargs['multimodal'] == True:
|
|
64
|
+
self.provider_instance = gemini.GeminiAIProvider(**kwargs)
|
|
65
|
+
elif self.provider == "gemini" and kwargs.get("fileinput") and kwargs['fileinput'] == True:
|
|
66
|
+
self.provider_instance = gemini.GeminiAIProvider(**kwargs)
|
|
67
|
+
elif self.provider == "gemini" and kwargs.get("videoinput") and kwargs['videoinput'] == True:
|
|
68
|
+
self.provider_instance = gemini.GeminiAIProvider(**kwargs)
|
|
69
|
+
|
|
70
|
+
elif self.provider:
|
|
71
|
+
if_litellm_support, support_type = self.check_litellm_providers(provider=self.provider)
|
|
72
|
+
if if_litellm_support:
|
|
73
|
+
self.provider_instance = litellm.LiteLLMProvider(**kwargs)
|
|
74
|
+
if support_type == 1:
|
|
75
|
+
self.litellm_call_type = 1
|
|
76
|
+
elif support_type == 2:
|
|
77
|
+
self.litellm_call_type = 2
|
|
78
|
+
elif support_type == 3:
|
|
79
|
+
self.litellm_call_type = 3
|
|
80
|
+
else:
|
|
81
|
+
raise ProviderError(f"Provider '{self.provider}' is not supported.")
|
|
82
|
+
else:
|
|
83
|
+
self.provider_instance = litellm.LiteLLMProvider(**kwargs)
|
|
84
|
+
|
|
85
|
+
def completion(self, model: str, messages: List[str], **kwargs) -> Any:
|
|
86
|
+
if not self.provider_instance:
|
|
87
|
+
raise ProviderError(f"Provider '{self.provider}' is not initialized.")
|
|
88
|
+
if self.litellm_call_type:
|
|
89
|
+
if self.litellm_call_type == 1:
|
|
90
|
+
# Jugde whether the model starts with self.provider, if not, add it
|
|
91
|
+
if not model.startswith(self.provider+"/"):
|
|
92
|
+
model = f"{self.provider}/{model}"
|
|
93
|
+
return self.provider_instance.completion(model, messages, **kwargs)
|
|
94
|
+
elif self.litellm_call_type == 2:
|
|
95
|
+
return self.provider_instance.completion(model, messages, **kwargs)
|
|
96
|
+
elif self.litellm_call_type == 3:
|
|
97
|
+
# append OpenAI as provider name
|
|
98
|
+
if self.provider == 'xai':
|
|
99
|
+
kwargs['api_base'] = "https://api.x.ai/v1"
|
|
100
|
+
if self.provider == 'qwen':
|
|
101
|
+
kwargs['api_base'] = "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
102
|
+
model = f"openai/{model}"
|
|
103
|
+
return self.provider_instance.completion(model, messages, **kwargs)
|
|
104
|
+
else:
|
|
105
|
+
return self.provider_instance.completion(model, messages, **kwargs)
|
|
106
|
+
|
|
107
|
+
async def acompletion(self, model: str, messages: List[str], **kwargs) -> Any:
|
|
108
|
+
loop = asyncio.get_event_loop()
|
|
109
|
+
if not self.provider_instance:
|
|
110
|
+
raise ProviderError(f"Provider '{self.provider}' is not initialized.")
|
|
111
|
+
|
|
112
|
+
if self.litellm_call_type:
|
|
113
|
+
if self.litellm_call_type == 1:
|
|
114
|
+
if not model.startswith(self.provider + "/"):
|
|
115
|
+
model = f"{self.provider}/{model}"
|
|
116
|
+
func = partial(self.provider_instance.completion, model, messages, **kwargs)
|
|
117
|
+
return await loop.run_in_executor(None, func)
|
|
118
|
+
elif self.litellm_call_type == 2:
|
|
119
|
+
func = partial(self.provider_instance.completion, model, messages, **kwargs)
|
|
120
|
+
return await loop.run_in_executor(None, func)
|
|
121
|
+
else:
|
|
122
|
+
func = partial(self.provider_instance.completion, model, messages, **kwargs)
|
|
123
|
+
return await loop.run_in_executor(None, func)
|
|
124
|
+
|
|
125
|
+
def check_litellm_providers(self, provider: str) -> bool:
|
|
126
|
+
# Judge whether the provider is supported by LiteLLM, and if provider name should be added to the model name
|
|
127
|
+
if provider in ['azure', 'azure_ai', 'anthropic', 'deepseek', 'sagemaker', 'bedrock', 'vertex_ai', 'vertex_ai_beta', 'palm', 'gemini', 'mistral', 'cloudflare', 'huggingface', 'replicate', 'together_ai', 'openrouter', 'baseten', 'nlp_cloud', 'petals', 'ollama', 'perplexity', 'groq', 'anyscale', 'watsonx', 'voyage', 'xinference']:
|
|
128
|
+
# provider name should be added to the model name
|
|
129
|
+
return True, 1
|
|
130
|
+
elif provider in ['openai', 'cohere', 'ai21', 'deepinfra', 'ai21', 'alpha_alpha']:
|
|
131
|
+
# provider name should not be added to the model name
|
|
132
|
+
return True, 2
|
|
133
|
+
elif provider in ['xai', 'qwen']:
|
|
134
|
+
# append OpenAI as procall as OpenAI campatible API
|
|
135
|
+
return True, 3
|
|
136
|
+
else:
|
|
137
|
+
return False, 0
|
|
138
|
+
|
|
139
|
+
# This is the new function to simplify calling
|
|
140
|
+
def unionchat(model: str, messages: List[dict], **kwargs) -> Any:
|
|
141
|
+
client = UnionLLM(**kwargs)
|
|
142
|
+
return client.completion(model=model, messages=messages, **kwargs)
|
unionllm/models.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
class ResponseModel:
|
|
4
|
+
def __init__(self, prompt_tokens: List[str], completion_tokens: List[str], total_tokens: int, total_attempts: int, raw_response: Optional[dict] = None, usage: Optional[dict] = None):
|
|
5
|
+
self.prompt_tokens = prompt_tokens
|
|
6
|
+
self.completion_tokens = completion_tokens
|
|
7
|
+
self.total_tokens = total_tokens
|
|
8
|
+
self.total_attempts = total_attempts
|
|
9
|
+
self.raw_response = raw_response
|
|
10
|
+
self.usage = usage
|
|
11
|
+
|
|
12
|
+
def to_dict(self) -> dict:
|
|
13
|
+
return {
|
|
14
|
+
"prompt_tokens": self.prompt_tokens,
|
|
15
|
+
"completion_tokens": self.completion_tokens,
|
|
16
|
+
"total_tokens": self.total_tokens,
|
|
17
|
+
"total_attempts": self.total_attempts,
|
|
18
|
+
"raw_response": self.raw_response,
|
|
19
|
+
"usage": self.usage
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def get_completions(self) -> list[str]:
|
|
23
|
+
completions = []
|
|
24
|
+
if self.raw_response and "choices" in self.raw_response:
|
|
25
|
+
for choice in self.raw_response["choices"]:
|
|
26
|
+
if "message" in choice and "content" in choice["message"]:
|
|
27
|
+
completions.append(choice["message"]["content"])
|
|
28
|
+
return completions
|
|
File without changes
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from .base_provider import BaseProvider
|
|
3
|
+
from unionllm.utils import ModelResponse, Message, Choices, Usage, Delta, StreamingChoices
|
|
4
|
+
import requests
|
|
5
|
+
import logging, os
|
|
6
|
+
|
|
7
|
+
class BaiChuanOpenAIError(Exception):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
status_code,
|
|
11
|
+
message,
|
|
12
|
+
):
|
|
13
|
+
self.status_code = status_code
|
|
14
|
+
self.message = message
|
|
15
|
+
super().__init__(self.message)
|
|
16
|
+
|
|
17
|
+
class BaiChuanAIProvider(BaseProvider):
|
|
18
|
+
def __init__(self, **model_kwargs):
|
|
19
|
+
# Get BAICHUAN_API_KEY from environment variables
|
|
20
|
+
_env_api_key = os.environ.get("BAICHUAN_API_KEY")
|
|
21
|
+
self.api_key = model_kwargs.get("api_key") if model_kwargs.get("api_key") else _env_api_key
|
|
22
|
+
if not self.api_key:
|
|
23
|
+
raise BaiChuanOpenAIError(
|
|
24
|
+
status_code=422, message=f"Missing API key"
|
|
25
|
+
)
|
|
26
|
+
self.endpoint_url = "https://api.baichuan-ai.com/v1/chat/completions"
|
|
27
|
+
|
|
28
|
+
def pre_processing(self, **kwargs):
|
|
29
|
+
supported_params = [
|
|
30
|
+
"model", "messages", "max_tokens", "temperature", "top_p", "n",
|
|
31
|
+
"logprobs", "stream", "stop", "presence_penalty", "frequency_penalty",
|
|
32
|
+
"best_of", "logit_bias", "tools", "tool_choice"
|
|
33
|
+
]
|
|
34
|
+
for key in list(kwargs.keys()):
|
|
35
|
+
if key not in supported_params:
|
|
36
|
+
kwargs.pop(key)
|
|
37
|
+
return kwargs
|
|
38
|
+
|
|
39
|
+
def post_stream_processing_wrapper(self, model, messages, **new_kwargs):
|
|
40
|
+
payload = json.dumps({"model": model, "messages": messages, **new_kwargs})
|
|
41
|
+
headers = {
|
|
42
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
}
|
|
45
|
+
response = requests.post(self.endpoint_url, headers=headers, data=payload)
|
|
46
|
+
for line in response.iter_lines():
|
|
47
|
+
if line:
|
|
48
|
+
new_line = line.decode("utf-8").replace("data: ", "")
|
|
49
|
+
if new_line == "[DONE]":
|
|
50
|
+
break
|
|
51
|
+
data = json.loads(new_line)
|
|
52
|
+
chunk_choices = []
|
|
53
|
+
for choice in data["choices"]:
|
|
54
|
+
chunk_delta = Delta()
|
|
55
|
+
delta = choice.get("delta")
|
|
56
|
+
if delta:
|
|
57
|
+
if "role" in choice['delta']:
|
|
58
|
+
chunk_delta.role = choice['delta']["role"]
|
|
59
|
+
if "content" in choice['delta']:
|
|
60
|
+
chunk_delta.content = choice['delta']["content"]
|
|
61
|
+
chunk_choices.append(StreamingChoices(index=choice['index'], delta=chunk_delta))
|
|
62
|
+
|
|
63
|
+
if "usage" in data:
|
|
64
|
+
chunk_usage = Usage()
|
|
65
|
+
if "prompt_tokens" in data["usage"]:
|
|
66
|
+
chunk_usage.prompt_tokens = data["usage"]["prompt_tokens"]
|
|
67
|
+
if "completion_tokens" in data["usage"]:
|
|
68
|
+
chunk_usage.completion_tokens = data["usage"]["completion_tokens"]
|
|
69
|
+
if "total_tokens" in data["usage"]:
|
|
70
|
+
chunk_usage.total_tokens = data["usage"]["total_tokens"]
|
|
71
|
+
|
|
72
|
+
chunk_response = ModelResponse(
|
|
73
|
+
id=data["id"],
|
|
74
|
+
choices=chunk_choices,
|
|
75
|
+
created=data["created"],
|
|
76
|
+
model=model,
|
|
77
|
+
usage=chunk_usage if "usage" in data else None,
|
|
78
|
+
stream=True
|
|
79
|
+
)
|
|
80
|
+
yield chunk_response
|
|
81
|
+
|
|
82
|
+
def create_model_response_wrapper(self, result, model):
|
|
83
|
+
response_dict = result.json()
|
|
84
|
+
choices = []
|
|
85
|
+
for choice in response_dict["choices"]:
|
|
86
|
+
message = Message(
|
|
87
|
+
content=choice["message"]["content"], role=choice["message"]["role"]
|
|
88
|
+
)
|
|
89
|
+
choices.append(
|
|
90
|
+
Choices(
|
|
91
|
+
message=message,
|
|
92
|
+
index=choice["index"],
|
|
93
|
+
finish_reason=choice["finish_reason"],
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
usage = Usage(
|
|
97
|
+
prompt_tokens=response_dict["usage"]["prompt_tokens"],
|
|
98
|
+
completion_tokens=response_dict["usage"]["completion_tokens"],
|
|
99
|
+
total_tokens=response_dict["usage"]["total_tokens"],
|
|
100
|
+
)
|
|
101
|
+
response = ModelResponse(
|
|
102
|
+
id=response_dict["id"],
|
|
103
|
+
choices=choices,
|
|
104
|
+
created=response_dict["created"],
|
|
105
|
+
model=model,
|
|
106
|
+
usage=usage,
|
|
107
|
+
)
|
|
108
|
+
return response
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def completion(self, model: str, messages: list, **kwargs):
|
|
112
|
+
try:
|
|
113
|
+
if model is None or messages is None:
|
|
114
|
+
raise BaiChuanOpenAIError(
|
|
115
|
+
status_code=422, message=f"Missing model or messages"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
message_check_result = self.check_prompt("baichuan", model, messages)
|
|
119
|
+
if message_check_result['pass_check']:
|
|
120
|
+
messages = message_check_result['messages']
|
|
121
|
+
else:
|
|
122
|
+
raise BaiChuanOpenAIError(
|
|
123
|
+
status_code=422, message=message_check_result['reason']
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
new_kwargs = self.pre_processing(**kwargs)
|
|
127
|
+
stream = kwargs.get("stream", False)
|
|
128
|
+
|
|
129
|
+
if stream:
|
|
130
|
+
return self.post_stream_processing_wrapper(model, messages, **new_kwargs)
|
|
131
|
+
else:
|
|
132
|
+
payload = json.dumps({"model": model, "messages": messages, **new_kwargs})
|
|
133
|
+
headers = {
|
|
134
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
135
|
+
"Content-Type": "application/json",
|
|
136
|
+
}
|
|
137
|
+
result = requests.post(self.endpoint_url, headers=headers, data=payload)
|
|
138
|
+
return self.create_model_response_wrapper(result, model=model)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
if hasattr(e, "status_code"):
|
|
141
|
+
raise BaiChuanOpenAIError(status_code=e.status_code, message=str(e))
|
|
142
|
+
else:
|
|
143
|
+
raise BaiChuanOpenAIError(status_code=500, message=str(e))
|