Compare commits

..

14 Commits

Author SHA1 Message Date
Junyan Qin
59211191a4 chore: release v3.4.0.2 2024-11-23 00:23:47 +08:00
Junyan Qin
a3ca7e82c7 hotfix: 调用工具时bug 2024-11-23 00:23:08 +08:00
Junyan Qin
0094056def chore: release v3.4.0.1 2024-11-22 23:55:24 +08:00
Junyan Qin
a9f305a1c6 feat: 添加对pydantic v1的兼容性 2024-11-22 23:37:46 +08:00
Junyan Qin
e8cc048901 deps: bump pydantic to v2 2024-11-22 23:29:12 +08:00
Junyan Qin
05da43f606 chore: 更新模型信息 2024-11-22 22:33:05 +08:00
Junyan Qin
a81faa7d8e fix: gitee ai 配置 schema 2024-11-22 20:01:35 +08:00
Junyan Qin
18ba7d1da7 Merge pull request #929 from RockChinQ/feat/gitee-ai
feat: 添加对 Gitee AI 的支持
2024-11-22 19:59:25 +08:00
Junyan Qin
875adfcbaa feat: 添加对 Gitee AI 的支持 2024-11-21 23:28:19 +08:00
Junyan Qin
6e9c213893 fix: 登录失败时无提示 2024-11-20 21:03:02 +08:00
Junyan Qin
753066ccb9 fix: webui 访问提示在Windows上的编码问题 2024-11-19 19:33:58 +08:00
Junyan Qin
8b36782c25 chore: 更新 docker-compose.yaml 2024-11-18 23:31:49 +08:00
Junyan Qin
da9dde6bd2 doc: update README 2024-11-17 21:30:53 +08:00
Junyan Qin
07f6e69b93 doc: update README.md 2024-11-17 21:11:21 +08:00
46 changed files with 232 additions and 119 deletions

View File

@@ -1,6 +1,6 @@
<p align="center">
<img src="https://docs.langbot.app/chrome-512.png" alt="QChatGPT" width="180" />
<img src="https://docs.langbot.app/langbot-logo-0.5x.png" alt="QChatGPT" width="180" />
</p>
<div align="center">
@@ -9,10 +9,7 @@
<a href="https://trendshift.io/repositories/6187" target="_blank"><img src="https://trendshift.io/api/badge/repositories/6187" alt="RockChinQ%2FQChatGPT | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/RockChinQ/LangBot)](https://github.com/RockChinQ/LangBot/releases/latest)
<a href="https://hub.docker.com/repository/docker/rockchin/langbot">
<img src="https://img.shields.io/docker/pulls/rockchin/langbot?color=blue" alt="docker pull">
</a>
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.docs.langbot.app%2Fapi%2Fv2%2Fview%2Frealtime%2Fcount_query%3Fminute%3D10080&query=%24.data.count&label=%E4%BD%BF%E7%94%A8%E9%87%8F%EF%BC%887%E6%97%A5%EF%BC%89)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.qchatgpt.rockchin.top%2Fapi%2Fv2%2Fview%2Frealtime%2Fcount_query%3Fminute%3D10080&query=%24.data.count&label=%E4%BD%BF%E7%94%A8%E9%87%8F%EF%BC%887%E6%97%A5%EF%BC%89)
![Wakapi Count](https://wakapi.rockchin.top/api/badge/RockChinQ/interval:any/project:QChatGPT)
<br/>
<img src="https://img.shields.io/badge/python-3.10 | 3.11 | 3.12-blue.svg" alt="python">
@@ -22,9 +19,6 @@
<a href="https://qm.qq.com/q/PClALFK242">
<img alt="Static Badge" src="https://img.shields.io/badge/%E7%A4%BE%E5%8C%BA%E7%BE%A4-619154800-purple">
</a>
<a href="https://codecov.io/gh/RockChinQ/QChatGPT" >
<img src="https://codecov.io/gh/RockChinQ/QChatGPT/graph/badge.svg?token=pjxYIL2kbC"/>
</a>
## 使用文档
@@ -42,5 +36,5 @@
<a href="https://github.com/RockChinQ/qcg-center">遥测服务端源码</a>
<a href="https://github.com/the-lazy-me/QChatGPT-Wiki">官方文档储存库</a>
<img alt="回复效果(带有联网插件)" src="https://docs.langbot.top/QChatGPT-0516.png" width="500px"/>
<img alt="回复效果(带有联网插件)" src="https://docs.langbot.app/QChatGPT-0516.png" width="500px"/>
</div>

View File

@@ -3,6 +3,7 @@ version: "3"
services:
langbot:
image: rockchin/langbot:latest
container_name: langbot
volumes:
- ./data:/app/data
- ./plugins:/app/plugins

View File

@@ -36,6 +36,12 @@ async def main_entry(loop: asyncio.AbstractEventLoop):
print("已自动安装缺失的依赖包,请重启程序。")
sys.exit(0)
# 检查pydantic版本如果没有 pydantic.v1则把 pydantic 映射为 v1
import pydantic.version
if pydantic.version.VERSION < '2.0':
import pydantic
sys.modules['pydantic.v1'] = pydantic
# 检查配置文件
from pkg.core.bootutils import files

View File

@@ -1,5 +1,6 @@
import quart
import sqlalchemy
import argon2
from .. import group
from .....persistence.entities import user
@@ -32,7 +33,10 @@ class UserRouterGroup(group.RouterGroup):
async def _() -> str:
json_data = await quart.request.json
token = await self.ap.user_service.authenticate(json_data['user'], json_data['password'])
try:
token = await self.ap.user_service.authenticate(json_data['user'], json_data['password'])
except argon2.exceptions.VerifyMismatchError:
return self.fail(1, '用户名或密码错误')
return self.success(data={
'token': token

View File

@@ -51,8 +51,7 @@ class UserService:
ph = argon2.PasswordHasher()
if not ph.verify(user_obj.password, password):
raise ValueError('密码错误')
ph.verify(user_obj.password, password)
return await self.generate_jwt_token(user_email)

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
import typing
import pydantic
import pydantic.v1 as pydantic
from ..core import app, entities as core_entities
from . import errors, operator

View File

@@ -42,8 +42,11 @@ async def init_logging(extra_handlers: list[logging.Handler] = None) -> logging.
)
stream_handler = logging.StreamHandler(sys.stdout)
# stream_handler.setLevel(level)
# stream_handler.setFormatter(color_formatter)
stream_handler.stream = open(sys.stdout.fileno(), mode='w', encoding='utf-8', buffering=1)
log_handlers: list[logging.Handler] = [stream_handler, logging.FileHandler(log_file_name)]
log_handlers: list[logging.Handler] = [stream_handler, logging.FileHandler(log_file_name, encoding='utf-8')]
log_handlers += extra_handlers if extra_handlers is not None else []
for handler in log_handlers:

View File

@@ -5,7 +5,7 @@ import typing
import datetime
import asyncio
import pydantic
import pydantic.v1 as pydantic
from ..provider import entities as llm_entities
from ..provider.modelmgr import entities

View File

@@ -0,0 +1,26 @@
from __future__ import annotations
from .. import migration
@migration.migration_class("gitee-ai-config", 15)
class GiteeAIConfigMigration(migration.Migration):
"""迁移"""
async def need_migrate(self) -> bool:
"""判断当前环境是否需要运行此迁移"""
return 'gitee-ai-chat-completions' not in self.ap.provider_cfg.data['requester'] or 'gitee-ai' not in self.ap.provider_cfg.data['keys']
async def run(self):
"""执行迁移"""
self.ap.provider_cfg.data['requester']['gitee-ai-chat-completions'] = {
"base-url": "https://ai.gitee.com/v1",
"args": {},
"timeout": 120
}
self.ap.provider_cfg.data['keys']['gitee-ai'] = [
"XXXXX"
]
await self.ap.provider_cfg.dump_config()

View File

@@ -7,6 +7,7 @@ from .. import migration
from ..migrations import m001_sensitive_word_migration, m002_openai_config_migration, m003_anthropic_requester_cfg_completion, m004_moonshot_cfg_completion
from ..migrations import m005_deepseek_cfg_completion, m006_vision_config, m007_qcg_center_url, m008_ad_fixwin_config_migrate, m009_msg_truncator_cfg
from ..migrations import m010_ollama_requester_config, m011_command_prefix_config, m012_runner_config, m013_http_api_config, m014_force_delay_config
from ..migrations import m015_gitee_ai_config
@stage.stage_class("MigrationStage")
@@ -28,3 +29,4 @@ class MigrationStage(stage.BootingStage):
if await migration_instance.need_migrate():
await migration_instance.run()
print(f'已执行迁移 {migration_instance.name}')

View File

@@ -2,7 +2,7 @@
import typing
import enum
import pydantic
import pydantic.v1 as pydantic
from ...provider import entities as llm_entities

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
import enum
import typing
import pydantic
import pydantic.v1 as pydantic
from ..platform.types import message as platform_message
from ..core import entities

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
import typing
import pydantic
import pydantic.v1 as pydantic
from .. import strategy as strategy_model
from ....core import entities as core_entities

View File

@@ -58,7 +58,7 @@ class Text2ImageStrategy(strategy_model.LongTextStrategy):
"""
kv = []
nums = []
beforeDatas = re.findall('[\d]+', path)
beforeDatas = re.findall('[\\d]+', path)
for num in beforeDatas:
indexV = []
times = path.count(num)

View File

@@ -1,4 +1,4 @@
import pydantic
import pydantic.v1 as pydantic
from ...platform.types import message as platform_message

View File

@@ -1,7 +1,5 @@
from __future__ import annotations
import pydantic
from ..core import app
from . import stage
from .resprule import resprule

View File

@@ -9,8 +9,6 @@ import traceback
import botpy
import botpy.message as botpy_message
import botpy.types.message as botpy_message_type
import pydantic
import pydantic.networks
from .. import adapter as adapter_model
from ...pipeline.longtext.strategies import forward

View File

@@ -1,8 +1,8 @@
from typing import Dict, List, Type
import pydantic.main as pdm
from pydantic import BaseModel
import pydantic.v1.main as pdm
from pydantic.v1 import BaseModel
class PlatformMetaclass(pdm.ModelMetaclass):

View File

@@ -7,7 +7,7 @@ from datetime import datetime
from enum import Enum
import typing
import pydantic
import pydantic.v1 as pydantic
class Entity(pydantic.BaseModel):

View File

@@ -6,7 +6,7 @@ from datetime import datetime
from enum import Enum
import typing
import pydantic
import pydantic.v1 as pydantic
from . import entities as platform_entities
from . import message as platform_message

View File

@@ -5,8 +5,7 @@ from enum import Enum
from pathlib import Path
import typing
import pydantic
import pydantic.main
import pydantic.v1 as pydantic
from . import entities as platform_entities
from .base import PlatformBaseModel, PlatformIndexedMetaclass, PlatformIndexedModel

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
import typing
import abc
import pydantic
import pydantic.v1 as pydantic
import enum
from . import events

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
import typing
import pydantic
import pydantic.v1 as pydantic
from ..core import entities as core_entities
from ..provider import entities as llm_entities

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
import typing
import enum
import pydantic
import pydantic.v1 as pydantic
from ..platform.types import message as platform_message

View File

@@ -2,9 +2,9 @@ from __future__ import annotations
import typing
import pydantic
import pydantic.v1 as pydantic
from . import api
from . import requester
from . import token
@@ -17,7 +17,7 @@ class LLMModelInfo(pydantic.BaseModel):
token_mgr: token.TokenManager
requester: api.LLMAPIRequester
requester: requester.LLMAPIRequester
tool_call_supported: typing.Optional[bool] = False

View File

@@ -2,11 +2,11 @@ from __future__ import annotations
import aiohttp
from . import entities
from . import entities, requester
from ...core import app
from . import token, api
from .apis import chatcmpl, anthropicmsgs, moonshotchatcmpl, deepseekchatcmpl, ollamachat
from . import token
from .requesters import chatcmpl, anthropicmsgs, moonshotchatcmpl, deepseekchatcmpl, ollamachat, giteeaichatcmpl
FETCH_MODEL_LIST_URL = "https://api.qchatgpt.rockchin.top/api/v2/fetch/model_list"
@@ -18,7 +18,7 @@ class ModelManager:
model_list: list[entities.LLMModelInfo]
requesters: dict[str, api.LLMAPIRequester]
requesters: dict[str, requester.LLMAPIRequester]
token_mgrs: dict[str, token.TokenManager]
@@ -42,7 +42,7 @@ class ModelManager:
for k, v in self.ap.provider_cfg.data['keys'].items():
self.token_mgrs[k] = token.TokenManager(k, v)
for api_cls in api.preregistered_requesters:
for api_cls in requester.preregistered_requesters:
api_inst = api_cls(self.ap)
await api_inst.initialize()
self.requesters[api_inst.name] = api_inst
@@ -94,7 +94,7 @@ class ModelManager:
model_name = model.get('model_name', default_model_info.model_name)
token_mgr = self.token_mgrs[model['token_mgr']] if 'token_mgr' in model else default_model_info.token_mgr
requester = self.requesters[model['requester']] if 'requester' in model else default_model_info.requester
req = self.requesters[model['requester']] if 'requester' in model else default_model_info.requester
tool_call_supported = model.get('tool_call_supported', default_model_info.tool_call_supported)
vision_supported = model.get('vision_supported', default_model_info.vision_supported)
@@ -102,7 +102,7 @@ class ModelManager:
name=model['name'],
model_name=model_name,
token_mgr=token_mgr,
requester=requester,
requester=req,
tool_call_supported=tool_call_supported,
vision_supported=vision_supported
)

View File

@@ -5,17 +5,17 @@ import traceback
import anthropic
from .. import api, entities, errors
from .. import entities, errors, requester
from .. import api, entities, errors
from .. import entities, errors
from ....core import entities as core_entities
from ... import entities as llm_entities
from ...tools import entities as tools_entities
from ....utils import image
@api.requester_class("anthropic-messages")
class AnthropicMessages(api.LLMAPIRequester):
@requester.requester_class("anthropic-messages")
class AnthropicMessages(requester.LLMAPIRequester):
"""Anthropic Messages API 请求器"""
client: anthropic.AsyncAnthropic

View File

@@ -12,15 +12,15 @@ import httpx
import aiohttp
import async_lru
from .. import api, entities, errors
from .. import entities, errors, requester
from ....core import entities as core_entities, app
from ... import entities as llm_entities
from ...tools import entities as tools_entities
from ....utils import image
@api.requester_class("openai-chat-completions")
class OpenAIChatCompletions(api.LLMAPIRequester):
@requester.requester_class("openai-chat-completions")
class OpenAIChatCompletions(requester.LLMAPIRequester):
"""OpenAI ChatCompletion API 请求器"""
client: openai.AsyncClient

View File

@@ -3,13 +3,13 @@ from __future__ import annotations
from ....core import app
from . import chatcmpl
from .. import api, entities, errors
from .. import entities, errors, requester
from ....core import entities as core_entities, app
from ... import entities as llm_entities
from ...tools import entities as tools_entities
@api.requester_class("deepseek-chat-completions")
@requester.requester_class("deepseek-chat-completions")
class DeepseekChatCompletions(chatcmpl.OpenAIChatCompletions):
"""Deepseek ChatCompletion API 请求器"""

View File

@@ -0,0 +1,53 @@
from __future__ import annotations
import json
import asyncio
import aiohttp
import typing
from . import chatcmpl
from .. import entities, errors, requester
from ....core import app
from ... import entities as llm_entities
from ...tools import entities as tools_entities
from .. import entities as modelmgr_entities
@requester.requester_class("gitee-ai-chat-completions")
class GiteeAIChatCompletions(chatcmpl.OpenAIChatCompletions):
"""Gitee AI ChatCompletions API 请求器"""
def __init__(self, ap: app.Application):
self.ap = ap
self.requester_cfg = ap.provider_cfg.data['requester']['gitee-ai-chat-completions'].copy()
async def _closure(
self,
req_messages: list[dict],
use_model: entities.LLMModelInfo,
use_funcs: list[tools_entities.LLMFunction] = None,
) -> llm_entities.Message:
self.client.api_key = use_model.token_mgr.get_token()
args = self.requester_cfg['args'].copy()
args["model"] = use_model.name if use_model.model_name is None else use_model.model_name
if use_funcs:
tools = await self.ap.tool_mgr.generate_tools_for_openai(use_funcs)
if tools:
args["tools"] = tools
# gitee 不支持多模态把content都转换成纯文字
for m in req_messages:
if 'content' in m and isinstance(m["content"], list):
m["content"] = " ".join([c["text"] for c in m["content"]])
args["messages"] = req_messages
resp = await self._req(args)
message = await self._make_msg(resp)
return message

View File

@@ -3,13 +3,13 @@ from __future__ import annotations
from ....core import app
from . import chatcmpl
from .. import api, entities, errors
from .. import entities, errors, requester
from ....core import entities as core_entities, app
from ... import entities as llm_entities
from ...tools import entities as tools_entities
@api.requester_class("moonshot-chat-completions")
@requester.requester_class("moonshot-chat-completions")
class MoonshotChatCompletions(chatcmpl.OpenAIChatCompletions):
"""Moonshot ChatCompletion API 请求器"""

View File

@@ -8,7 +8,7 @@ from typing import Union, Mapping, Any, AsyncIterator
import async_lru
import ollama
from .. import api, entities, errors
from .. import entities, errors, requester
from ... import entities as llm_entities
from ...tools import entities as tools_entities
from ....core import app
@@ -17,8 +17,8 @@ from ....utils import image
REQUESTER_NAME: str = "ollama-chat"
@api.requester_class(REQUESTER_NAME)
class OllamaChatCompletions(api.LLMAPIRequester):
@requester.requester_class(REQUESTER_NAME)
class OllamaChatCompletions(requester.LLMAPIRequester):
"""Ollama平台 ChatCompletion API请求器"""
client: ollama.AsyncClient
request_cfg: dict

View File

@@ -2,8 +2,6 @@ from __future__ import annotations
import typing
import pydantic
class TokenManager():
"""鉴权 Token 管理器

View File

@@ -1,7 +1,7 @@
from __future__ import annotations
import typing
import pydantic
import pydantic.v1 as pydantic
from ...provider import entities

View File

@@ -4,7 +4,7 @@ import abc
import typing
import asyncio
import pydantic
import pydantic.v1 as pydantic
from ...core import entities as core_entities

View File

@@ -118,10 +118,9 @@ class ToolManager:
traceback.print_exc()
return f"error occurred when executing function {name}: {e}"
finally:
plugin = None
for p in self.ap.plugin_mgr.plugins:
for p in self.ap.plugin_mgr.plugins():
if function in p.content_functions:
plugin = p
break
@@ -137,4 +136,4 @@ class ToolManager:
},
function_name=function.name,
function_description=function.description,
)
)

View File

@@ -6,7 +6,7 @@ import os
import base64
import logging
import pydantic
import pydantic.v1 as pydantic
import requests
from ..core import app

View File

@@ -1,4 +1,4 @@
semantic_version = "v3.4.0"
semantic_version = "v3.4.0.2"
debug_mode = False

View File

@@ -1,7 +1,5 @@
from __future__ import annotations
import pydantic
LOG_PAGE_SIZE = 20
MAX_CACHED_PAGES = 10

View File

@@ -9,7 +9,7 @@ Pillow
tiktoken
PyYaml
aiohttp
pydantic<2.0
pydantic>2.0
websockets
urllib3
psutil

View File

@@ -8,69 +8,59 @@
"vision_supported": false
},
{
"name": "gpt-3.5-turbo-0125",
"name": "gpt-4o",
"tool_call_supported": true,
"vision_supported": false
"vision_supported": true
},
{
"name": "gpt-3.5-turbo",
"name": "gpt-4o-2024-11-20",
"tool_call_supported": true,
"vision_supported": false
"vision_supported": true
},
{
"name": "gpt-3.5-turbo-1106",
"name": "gpt-4o-2024-08-06",
"tool_call_supported": true,
"vision_supported": false
"vision_supported": true
},
{
"name": "gpt-4o-2024-05-13",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "chatgpt-4o-latest",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4o-mini",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "o1-preview",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "o1-mini",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-turbo",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-turbo-2024-04-09",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-turbo-preview",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-0125-preview",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-1106-preview",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4o",
"name": "gpt-3.5-turbo",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-0613",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-32k",
"tool_call_supported": true,
"vision_supported": true
},
{
"name": "gpt-4-32k-0613",
"tool_call_supported": true,
"vision_supported": true
"vision_supported": false
},
{
"model_name": "SparkDesk",
@@ -81,19 +71,19 @@
"name": "OneAPI/gemini-pro"
},
{
"name": "claude-3-opus-20240229",
"name": "claude-3-opus-latest",
"requester": "anthropic-messages",
"token_mgr": "anthropic",
"vision_supported": true
},
{
"name": "claude-3-sonnet-20240229",
"name": "claude-3-5-sonnet-latest",
"requester": "anthropic-messages",
"token_mgr": "anthropic",
"vision_supported": true
},
{
"name": "claude-3-haiku-20240307",
"name": "claude-3-5-haiku-latest",
"requester": "anthropic-messages",
"token_mgr": "anthropic",
"vision_supported": true
@@ -120,6 +110,11 @@
"name": "deepseek-chat",
"requester": "deepseek-chat-completions",
"token_mgr": "deepseek"
},
{
"name": "deepseek-coder",
"requester": "deepseek-chat-completions",
"token_mgr": "deepseek"
}
]
}

View File

@@ -13,6 +13,9 @@
],
"deepseek": [
"sk-1234567890"
],
"gitee-ai": [
"XXXXX"
]
},
"requester": {
@@ -42,9 +45,14 @@
"base-url": "http://127.0.0.1:11434",
"args": {},
"timeout": 600
},
"gitee-ai-chat-completions": {
"base-url": "https://ai.gitee.com/v1",
"args": {},
"timeout": 120
}
},
"model": "gpt-3.5-turbo",
"model": "gpt-4o",
"prompt-mode": "normal",
"prompt": {
"default": ""

View File

@@ -54,6 +54,15 @@
"type": "string"
},
"default": []
},
"gitee": {
"type": "array",
"title": "Gitee API 密钥",
"description": "Gitee API 密钥",
"items": {
"type": "string"
},
"default": []
}
}
},
@@ -160,6 +169,25 @@
"default": 600
}
}
},
"gitee-ai-chat-completions": {
"type": "object",
"title": "Gitee AI API 请求配置",
"description": "仅可编辑 URL 和 超时时间,额外请求参数不支持可视化编辑,请到编辑器编辑",
"properties": {
"base-url": {
"type": "string",
"title": "API URL"
},
"args": {
"type": "object"
},
"timeout": {
"type": "number",
"title": "API 请求超时时间",
"default": 120
}
}
}
}
},

View File

@@ -47,17 +47,21 @@ const login = () => {
user: user.value,
password: password.value
}).then(res => {
if (res.data.data.token) {
if (res.data.code == 0) {
emit('success', '登录成功')
localStorage.setItem('user-token', res.data.data.token)
setTimeout(() => {
location.reload()
}, 1000)
} else {
emit('error', '登录失败')
emit('error', res.data.msg)
}
}).catch(err => {
emit('error', err.response.data.message)
if (err.response.data.msg) {
emit('error', err.response.data.msg)
} else {
emit('error', '登录失败')
}
})
}