From bbc16577518f93c8304366a9f57fa727786fa4a0 Mon Sep 17 00:00:00 2001
From: ZiTao-Li <135263265+ZiTao-Li@users.noreply.github.com>
Date: Wed, 22 May 2024 17:53:39 +0800
Subject: [PATCH] Add post API for embedding model and fix embedding model
wrappers (#186)
* add post model for embedding mdoel
* fix bugs in other embedding models
* update documents
* update as comment
* update documents
* make ollama consistent
* fix typo
* fix typo in markdown files
* merge
---
.../en/source/tutorial/203-model.md | 91 +++++++++++++++----
.../zh_CN/source/tutorial/203-model.md | 87 ++++++++++++++----
.../postapi_model_config_template.json | 32 +++++++
src/agentscope/models/dashscope_model.py | 16 +---
src/agentscope/models/ollama_model.py | 2 +-
src/agentscope/models/openai_model.py | 14 +--
src/agentscope/models/post_model.py | 60 ++++++++++++
7 files changed, 244 insertions(+), 58 deletions(-)
diff --git a/docs/sphinx_doc/en/source/tutorial/203-model.md b/docs/sphinx_doc/en/source/tutorial/203-model.md
index d6e153d0f..d5565b617 100644
--- a/docs/sphinx_doc/en/source/tutorial/203-model.md
+++ b/docs/sphinx_doc/en/source/tutorial/203-model.md
@@ -90,7 +90,9 @@ In the current AgentScope, the supported `model_type` types, the corresponding
| | Generation | [`OllamaGenerationWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/ollama_model.py) | `"ollama_generate"` | llama2, ... |
| LiteLLM API | Chat | [`LiteLLMChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/litellm_model.py) | `"litellm_chat"` | - |
| Post Request based API | - | [`PostAPIModelWrapperBase`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `"post_api"` | - |
-| | Chat | [`PostAPIChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `"post_api_chat"` | meta-llama/Meta-Llama-3-8B-Instruct, ... |
+| | Chat | [`PostAPIChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `"post_api_chat"` | meta-llama/Meta-Llama-3-8B-Instruct, ... |
+| | Image Synthesis | [`PostAPIDALLEWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `post_api_dall_e` | - | |
+| | Embedding | [`PostAPIEmbeddingWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `post_api_embedding` | - |
#### Detailed Parameters
@@ -417,15 +419,33 @@ Here we provide example configurations for different model wrappers.
+#### LiteLLM Chat API
+
+
+LiteLLM Chat API (agentscope.models.LiteLLMChatModelWrapper
)
+
+```python
+{
+ "config_name": "lite_llm_openai_chat_gpt-3.5-turbo",
+ "model_type": "litellm_chat",
+ "model_name": "gpt-3.5-turbo" # You should note that for different models, you should set the corresponding environment variables, such as OPENAI_API_KEY, etc. You may refer to https://docs.litellm.ai/docs/ for this.
+},
+```
+
+
+
+
+
#### Post Request API
-Post request API (agentscope.models.PostAPIModelWrapperBase
)
+Post Request Chat API (agentscope.models.PostAPIChatWrapper
)
```python
{
- "config_name": "my_postapiwrapper_config",
- "model_type": "post_api",
+ "config_name": "my_postapichatwrapper_config",
+ "model_type": "post_api_chat",
# Required parameters
"api_url": "https://xxx.xxx",
@@ -437,41 +457,69 @@ Here we provide example configurations for different model wrappers.
"messages_key": "messages",
}
```
+> ⚠️ The Post Request Chat model wrapper (`PostAPIChatWrapper`) has the following properties:
+> 1) The `.format()` function makes sure the input messages become a list of dicts.
+> 2) The `._parse_response()` function assumes the generated text will be in `response["data"]["response"]["choices"][0]["message"]["content"]`
-
-
-#### LiteLLM Chat API
-LiteLLM Chat API (agentscope.models.LiteLLMChatModelWrapper
)
+Post Request Image Synthesis API (agentscope.models.PostAPIDALLEWrapper
)
```python
{
- "config_name": "lite_llm_openai_chat_gpt-3.5-turbo",
- "model_type": "litellm_chat",
- "model_name": "gpt-3.5-turbo" # You should note that for different models, you should set the corresponding environment variables, such as OPENAI_API_KEY, etc. You may refer to https://docs.litellm.ai/docs/ for this.
-},
+ "config_name": "my_postapiwrapper_config",
+ "model_type": "post_api_dall_e",
+
+ # Required parameters
+ "api_url": "https://xxx.xxx",
+ "headers": {
+ # e.g. "Authorization": "Bearer xxx",
+ },
+
+ # Optional parameters
+ "messages_key": "messages",
+}
```
+> ⚠️ The Post Request Image Synthesis model wrapper (`PostAPIDALLEWrapper`) has the following properties:
+> 1) The `._parse_response()` function assumes the generated image will be presented as urls in `response["data"]["response"]["data"][i]["url"]`
-
+
+Post Request Embedding API (agentscope.models.PostAPIEmbeddingWrapper
)
+
+```python
+{
+ "config_name": "my_postapiwrapper_config",
+ "model_type": "post_api_embedding",
+
+ # Required parameters
+ "api_url": "https://xxx.xxx",
+ "headers": {
+ # e.g. "Authorization": "Bearer xxx",
+ },
+
+ # Optional parameters
+ "messages_key": "messages",
+}
+```
+> ⚠️ The Post Request Embedding model wrapper (`PostAPIEmbeddingWrapper`) has the following properties:
+> 1) The `._parse_response()` function assumes the generated embeddings will be in `response["data"]["response"]["data"][i]["embedding"]`
+
+
-#### Post Request Chat API
-Post request Chat API (agentscope.models.PostAPIChatModelWrapper
)
+Post Request API (agentscope.models.PostAPIModelWrapperBase
)
```python
{
- "config_name": "my_postapichatwrapper_config",
- "model_type": "post_api_chat",
+ "config_name": "my_postapiwrapper_config",
+ "model_type": "post_api",
# Required parameters
"api_url": "https://xxx.xxx",
@@ -483,9 +531,14 @@ com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py">agentsc
"messages_key": "messages",
}
```
+> ⚠️ Post Request model wrapper (`PostAPIModelWrapperBase`) returns raw HTTP responses from the API in ModelResponse, and the `.format()` is not implemented. It is recommended to use `Post Request Chat API` when running examples with chats.
+> `PostAPIModelWrapperBase` can be used when
+> 1) only the raw HTTP response is wanted and `.format()` is not called;
+> 2) Or, the developers want to overwrite the `.format()` and/or `._parse_response()` functions.
+
## Build Model Service from Scratch
diff --git a/docs/sphinx_doc/zh_CN/source/tutorial/203-model.md b/docs/sphinx_doc/zh_CN/source/tutorial/203-model.md
index 7b912cbf2..78dc90bad 100644
--- a/docs/sphinx_doc/zh_CN/source/tutorial/203-model.md
+++ b/docs/sphinx_doc/zh_CN/source/tutorial/203-model.md
@@ -94,7 +94,7 @@ API如下:
| API | Task | Model Wrapper | `model_type` | Some Supported Models |
|------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------|-------------------------------|--------------------------------------------------|
-| OpenAI API | Chat | [`OpenAIChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/openai_model.py) | `"openai_chat"` | gpt-4, gpt-3.5-turbo, ... |
+| OpenAI API | Chat | [`OpenAIChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/openai_model.py) | `"openai_chat"` | gpt-4, gpt-3.5-turbo, ... |
| | Embedding | [`OpenAIEmbeddingWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/openai_model.py) | `"openai_embedding"` | text-embedding-ada-002, ... |
| | DALL·E | [`OpenAIDALLEWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/openai_model.py) | `"openai_dall_e"` | dall-e-2, dall-e-3 |
| DashScope API | Chat | [`DashScopeChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/dashscope_model.py) | `"dashscope_chat"` | qwen-plus, qwen-max, ... |
@@ -103,14 +103,16 @@ API如下:
| | Multimodal | [`DashScopeMultiModalWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/dashscope_model.py) | `"dashscope_multimodal"` | qwen-vl-plus, qwen-vl-max, qwen-audio-turbo, ... |
| Gemini API | Chat | [`GeminiChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/gemini_model.py) | `"gemini_chat"` | gemini-pro, ... |
| | Embedding | [`GeminiEmbeddingWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/gemini_model.py) | `"gemini_embedding"` | models/embedding-001, ... |
-| ZhipuAI API | Chat | [`ZhipuAIChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/zhipu_model.py) | `"zhipuai_chat"` | glm-4, ... |
-| | Embedding | [`ZhipuAIEmbeddingWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/zhipu_model.py) | `"zhipuai_embedding"` | embedding-2, ... |
+| ZhipuAI API | Chat | [`ZhipuAIChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/zhipu_model.py) | `"zhipuai_chat"` | glm-4, ... |
+| | Embedding | [`ZhipuAIEmbeddingWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/zhipu_model.py) | `"zhipuai_embedding"` | embedding-2, ... |
| ollama | Chat | [`OllamaChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/ollama_model.py) | `"ollama_chat"` | llama2, ... |
| | Embedding | [`OllamaEmbeddingWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/ollama_model.py) | `"ollama_embedding"` | llama2, ... |
| | Generation | [`OllamaGenerationWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/ollama_model.py) | `"ollama_generate"` | llama2, ... |
| LiteLLM API | Chat | [`LiteLLMChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/litellm_model.py) | `"litellm_chat"` | - |
| Post Request based API | - | [`PostAPIModelWrapperBase`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `"post_api"` | - |
-| | Chat | [`PostAPIChatModelWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `"post_api_chat"` | meta-llama/Meta-Llama-3-8B-Instruct, ... |
+| | Chat | [`PostAPIChatWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `"post_api_chat"` | meta-llama/Meta-Llama-3-8B-Instruct, ... |
+| | Image Synthesis | [`PostAPIDALLEWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `post_api_dall_e` | - | |
+| | Embedding | [`PostAPIEmbeddingWrapper`](https://github.com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py) | `post_api_embedding` | - |
#### 详细参数
@@ -460,38 +462,85 @@ com/modelscope/agentscope/blob/main/src/agentscope/models/litellm_model.py">agen
#### Post Request API
-Post request API (agentscope.models.PostAPIModelWrapperBase
)
+Post Request Chat API (agentscope.models.PostAPIChatWrapper
)
```python
{
"config_name": "my_postapiwrapper_config",
- "model_type": "post_api",
+ "model_type": "post_api_chat",
- # 必要参数
+ # Required parameters
"api_url": "https://xxx.xxx",
"headers": {
- # 例如:"Authorization": "Bearer xxx",
+ # e.g. "Authorization": "Bearer xxx",
},
- # 可选参数
+ # Optional parameters
"messages_key": "messages",
}
```
+> ⚠️ Post Request Chat model wrapper (`PostAPIChatWrapper`) 有以下特性:
+> 1) 它的 `.format()` 方法会确保输入的信息(messages)会被转换成字典列表(a list of dict).
+> 2) 它的 `._parse_response()` 方法假设了生成的文字内容会在 `response["data"]["response"]["choices"][0]["message"]["content"]`
-
-#### Post Request Chat API
+
+Post Request Image Synthesis API (agentscope.models.PostAPIDALLEWrapper
)
+
+```python
+{
+ "config_name": "my_postapiwrapper_config",
+ "model_type": "post_api_dall_e",
+
+ # Required parameters
+ "api_url": "https://xxx.xxx",
+ "headers": {
+ # e.g. "Authorization": "Bearer xxx",
+ },
+
+ # Optional parameters
+ "messages_key": "messages",
+}
+```
+> ⚠️ Post Request Image Synthesis model wrapper (`PostAPIDALLEWrapper`) 有以下特性:
+> 1) 它的 `._parse_response()` 方法假设生成的图片都以url的形式表示在`response["data"]["response"]["data"][i]["url"]`
+
+
+
+
+
+Post Request Embedding API (agentscope.models.PostAPIEmbeddingWrapper
)
+
+```python
+{
+ "config_name": "my_postapiwrapper_config",
+ "model_type": "post_api_embedding",
+
+ # Required parameters
+ "api_url": "https://xxx.xxx",
+ "headers": {
+ # e.g. "Authorization": "Bearer xxx",
+ },
+
+ # Optional parameters
+ "messages_key": "messages",
+}
+```
+
+> ⚠️ Post Request Embedding model wrapper (`PostAPIEmbeddingWrapper`) 有以下特性:
+> 1) 它的 `._parse_response()`方法假设生成的特征向量会存放在 `response["data"]["response"]["data"][i]["embedding"]`
+
+
-Post request Chat API (agentscope.models.PostAPIChatModelWrapper
)
+Post Request API (agentscope.models.PostAPIModelWrapperBase
)
```python
{
- "config_name": "my_postapichatwrapper_config",
- "model_type": "post_api_chat",
+ "config_name": "my_postapiwrapper_config",
+ "model_type": "post_api",
# 必要参数
"api_url": "https://xxx.xxx",
@@ -503,13 +552,19 @@ com/modelscope/agentscope/blob/main/src/agentscope/models/post_model.py">agentsc
"messages_key": "messages",
}
```
+> ⚠️ Post request model wrapper (`PostAPIModelWrapperBase`) 返回原生的 HTTP 响应值, 且没有实现 `.format()`. 当运行样例时,推荐使用 `Post Request Chat API`.
+> 使用`PostAPIModelWrapperBase`时,需要注意
+> 1) `.format()` 方法不能被调用;
+> 2) 或开发者希望实现自己的`.format()`和/或`._parse_response()`
-
+
+
+
## 从零搭建模型服务
针对需要自己搭建模型服务的开发者,AgentScope提供了一些脚本来帮助开发者快速搭建模型服务。您可以在[scripts](https://github.com/modelscope/agentscope/tree/main/scripts)目录下找到这些脚本以及说明。
diff --git a/examples/model_configs_template/postapi_model_config_template.json b/examples/model_configs_template/postapi_model_config_template.json
index c5e512dab..3f4586621 100644
--- a/examples/model_configs_template/postapi_model_config_template.json
+++ b/examples/model_configs_template/postapi_model_config_template.json
@@ -45,5 +45,37 @@
"Authorization": "Bearer {YOUR_API_TOKEN}"
},
"api_url": "https://api-inference.huggingface.co/models/gpt2"
+},
+{
+ "config_name": "post_api_img_syn_config",
+ "model_type": "post_api_dall_e",
+ "api_url": "http://xxx.xxx.xxx.xxx:xxxx/xxx",
+ "headers": {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer {YOUR_API_TOKEN}"
+ },
+ "messages_key": "prompt",
+ "json_args": {
+ "model": "{YOUR_MODEL}",
+ "n":1,
+ "quality":"standard",
+ "response_format":"url",
+ "size":"512x512",
+ "style":"natural"
+ }
+},
+{
+ "config_name": "post_api_embedding_config",
+ "model_type": "post_api_embedding",
+ "api_url": "http://xxx.xxx.xxx.xxx:xxxx/xxx",
+ "headers": {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer {YOUR_API_TOKEN}"
+ },
+ "messages_key": "input",
+ "json_args": {
+ "model": "{YOUR_MODEL}",
+ "encoding_format": "float"
+ }
}
]
\ No newline at end of file
diff --git a/src/agentscope/models/dashscope_model.py b/src/agentscope/models/dashscope_model.py
index c4183aa85..4abc6b4ff 100644
--- a/src/agentscope/models/dashscope_model.py
+++ b/src/agentscope/models/dashscope_model.py
@@ -505,18 +505,10 @@ def __call__(
)
# step5: return response
- if len(response.output["embeddings"]) == 0:
- return ModelResponse(
- embedding=response.output["embedding"][0],
- raw=response,
- )
- else:
- return ModelResponse(
- embedding=[
- _["embedding"] for _ in response.output["embeddings"]
- ],
- raw=response,
- )
+ return ModelResponse(
+ embedding=[_["embedding"] for _ in response.output["embeddings"]],
+ raw=response,
+ )
class DashScopeMultiModalWrapper(DashScopeWrapperBase):
diff --git a/src/agentscope/models/ollama_model.py b/src/agentscope/models/ollama_model.py
index b4157c586..9bdc07869 100644
--- a/src/agentscope/models/ollama_model.py
+++ b/src/agentscope/models/ollama_model.py
@@ -341,7 +341,7 @@ def __call__(
# step5: return response
return ModelResponse(
- embedding=response["embedding"],
+ embedding=[response["embedding"]],
raw=response,
)
diff --git a/src/agentscope/models/openai_model.py b/src/agentscope/models/openai_model.py
index 99542582b..9bbdc59c1 100644
--- a/src/agentscope/models/openai_model.py
+++ b/src/agentscope/models/openai_model.py
@@ -522,13 +522,7 @@ def __call__(
# step5: return response
response_json = response.model_dump()
- if len(response_json["data"]) == 0:
- return ModelResponse(
- embedding=response_json["data"]["embedding"][0],
- raw=response_json,
- )
- else:
- return ModelResponse(
- embedding=[_["embedding"] for _ in response_json["data"]],
- raw=response_json,
- )
+ return ModelResponse(
+ embedding=[_["embedding"] for _ in response_json["data"]],
+ raw=response_json,
+ )
diff --git a/src/agentscope/models/post_model.py b/src/agentscope/models/post_model.py
index c60fb2632..fe61be221 100644
--- a/src/agentscope/models/post_model.py
+++ b/src/agentscope/models/post_model.py
@@ -240,3 +240,63 @@ def format(
f"need to format the input. Please try to use the "
f"model wrapper directly.",
)
+
+
+class PostAPIEmbeddingWrapper(PostAPIModelWrapperBase):
+ """
+ A post api model wrapper for embedding model
+ """
+
+ model_type: str = "post_api_embedding"
+
+ def _parse_response(self, response: dict) -> ModelResponse:
+ """
+ Parse the response json data into ModelResponse with embedding.
+ Args:
+ response (`dict`):
+ The response obtained from the API. This parsing assume the
+ structure of the response is as following:
+ {
+ "code": 200,
+ "data": {
+ ...
+ "response": {
+ "data": [
+ {
+ "embedding": [
+ 0.001,
+ ...
+ ],
+ ...
+ }
+ ],
+ "model": "xxxx",
+ ...
+ },
+ },
+ }
+ """
+ if "data" not in response["data"]["response"]:
+ if "error" in response["data"]["response"]:
+ error_msg = response["data"]["response"]["error"]["message"]
+ else:
+ error_msg = response["data"]["response"]
+ logger.error(f"Error in embedding API call:\n{error_msg}")
+ raise ValueError(f"Error in embedding API call:\n{error_msg}")
+ embeddings = [
+ data["embedding"] for data in response["data"]["response"]["data"]
+ ]
+ return ModelResponse(
+ embedding=embeddings,
+ raw=response,
+ )
+
+ def format(
+ self,
+ *args: Union[MessageBase, Sequence[MessageBase]],
+ ) -> Union[List[dict], str]:
+ raise RuntimeError(
+ f"Model Wrapper [{type(self).__name__}] doesn't "
+ f"need to format the input. Please try to use the "
+ f"model wrapper directly.",
+ )