diff --git a/xiao_config.yaml.example b/xiao_config.yaml.example index fa5af26b..2c2a492d 100644 --- a/xiao_config.yaml.example +++ b/xiao_config.yaml.example @@ -20,7 +20,7 @@ verbose: false proxy: "" # ===== 对话 AI 设置 ===== -# 使用的 bot 类型,目前支持 chatgptapi, glm, gemini, langchain, qwen, doubao, moonshot, yi, llama, ppio +# 使用的 bot 类型,目前支持 chatgptapi, glm, gemini, langchain, qwen, doubao, moonshot, yi, llama, ppio, jiekou bot: chatgptapi # 自定义 prompt prompt: "请用300字以内回答" @@ -89,9 +89,13 @@ volc_access_key: "" volc_secret_key: "" # ----- PPIO 派欧云 ----- -# PPIO API key: https://ppio.com/docs/models/reference-authentication +# PPIO API key: https://ppio.com/settings/key-management ppio_api_key: "" +# ----- 接口AI ----- +# Jiekou AI API key: https://jiekou.ai/settings/key-management +jiekou_api_key: "" + # ===== 语音设置 ===== # 使用的 TTS 类型,目前支持 mi, edge, openai, azure, volc, baidu, google, minimax, fish tts: mi diff --git a/xiaogpt/bot/__init__.py b/xiaogpt/bot/__init__.py index be6c08d8..452810ed 100644 --- a/xiaogpt/bot/__init__.py +++ b/xiaogpt/bot/__init__.py @@ -5,6 +5,7 @@ from xiaogpt.bot.doubao_bot import DoubaoBot from xiaogpt.bot.gemini_bot import GeminiBot from xiaogpt.bot.glm_bot import GLMBot +from xiaogpt.bot.jiekou_bot import JiekouBot from xiaogpt.bot.langchain_bot import LangChainBot from xiaogpt.bot.llama_bot import LlamaBot from xiaogpt.bot.moonshot_bot import MoonshotBot @@ -24,6 +25,7 @@ "yi": YiBot, "llama": LlamaBot, "ppio": PPIOBot, + "jiekou": JiekouBot, } @@ -46,4 +48,5 @@ def get_bot(config: Config) -> BaseBot: "YiBot", "LlamaBot", "PPIOBot", + "JiekouBot", ] diff --git a/xiaogpt/bot/jiekou_bot.py b/xiaogpt/bot/jiekou_bot.py new file mode 100644 index 00000000..3a630c8f --- /dev/null +++ b/xiaogpt/bot/jiekou_bot.py @@ -0,0 +1,98 @@ +"""Jiekou AI bot""" + +from __future__ import annotations + +import dataclasses +from typing import TYPE_CHECKING, ClassVar + +import httpx +from rich import print + +from xiaogpt.bot.base_bot import BaseBot, ChatHistoryMixin +from xiaogpt.utils import split_sentences + +if TYPE_CHECKING: + import openai + + +@dataclasses.dataclass +class JiekouBot(ChatHistoryMixin, BaseBot): + name: ClassVar[str] = "Jiekou AI" + default_options: ClassVar[dict[str, str]] = {"model": "deepseek/deepseek-v3.2"} + jiekou_api_key: str + api_base: str = "https://api.jiekou.ai/openai" + proxy: str | None = None + history: list[tuple[str, str]] = dataclasses.field(default_factory=list, init=False) + + def _make_openai_client(self, sess: httpx.AsyncClient) -> openai.AsyncOpenAI: + import openai + + return openai.AsyncOpenAI( + api_key=self.jiekou_api_key, + base_url=self.api_base, + http_client=sess, + ) + + @classmethod + def from_config(cls, config): + return cls( + jiekou_api_key=config.jiekou_api_key, + api_base="https://api.jiekou.ai/openai", + proxy=config.proxy, + ) + + async def ask(self, query, **options): + ms = self.get_messages() + ms.append({"role": "user", "content": f"{query}"}) + kwargs = {**self.default_options, **options} + httpx_kwargs = {} + if self.proxy: + httpx_kwargs["proxies"] = self.proxy + async with httpx.AsyncClient(trust_env=True, **httpx_kwargs) as sess: + client = self._make_openai_client(sess) + try: + completion = await client.chat.completions.create(messages=ms, **kwargs) + except Exception as e: + print(str(e)) + return "" + + message = completion.choices[0].message.content + self.add_message(query, message) + print(message) + return message + + async def ask_stream(self, query, **options): + ms = self.get_messages() + ms.append({"role": "user", "content": f"{query}"}) + kwargs = {**self.default_options, **options} + httpx_kwargs = {} + if self.proxy: + httpx_kwargs["proxies"] = self.proxy + async with httpx.AsyncClient(trust_env=True, **httpx_kwargs) as sess: + client = self._make_openai_client(sess) + try: + completion = await client.chat.completions.create( + messages=ms, stream=True, **kwargs + ) + except Exception as e: + print(str(e)) + return + + async def text_gen(): + async for event in completion: + if not event.choices: + continue + chunk_message = event.choices[0].delta + if chunk_message.content is None: + continue + print(chunk_message.content, end="") + yield chunk_message.content + + message = "" + try: + async for sentence in split_sentences(text_gen()): + message += sentence + yield sentence + finally: + print() + self.add_message(query, message) diff --git a/xiaogpt/cli.py b/xiaogpt/cli.py index 6827c071..67ff4621 100644 --- a/xiaogpt/cli.py +++ b/xiaogpt/cli.py @@ -67,6 +67,11 @@ def main(): dest="ppio_api_key", help="PPIO api key", ) + parser.add_argument( + "--jiekou_api_key", + dest="jiekou_api_key", + help="Jiekou AI api key", + ) parser.add_argument( "--serpapi_api_key", dest="serpapi_api_key", @@ -198,6 +203,13 @@ def main(): const="ppio", help="if use PPIO api", ) + bot_group.add_argument( + "--use_jiekou", + dest="bot", + action="store_const", + const="jiekou", + help="if use Jiekou AI api", + ) parser.add_argument( "--bing_cookie_path", dest="bing_cookie_path", @@ -218,6 +230,7 @@ def main(): "yi", "llama", "ppio", + "jiekou", ], ) parser.add_argument( diff --git a/xiaogpt/config.py b/xiaogpt/config.py index 7c7c5229..89722212 100644 --- a/xiaogpt/config.py +++ b/xiaogpt/config.py @@ -69,6 +69,7 @@ class Config: volc_secret_key: str = os.getenv("VOLC_SECRET_KEY", "") volc_api_key: str = os.getenv("volc_api_key", "") ppio_api_key: str = os.getenv("PPIO_API_KEY", "") + jiekou_api_key: str = os.getenv("JIEKOU_API_KEY", "") proxy: str | None = None mi_did: str = os.getenv("MI_DID", "") keyword: Iterable[str] = KEY_WORD @@ -112,6 +113,11 @@ def __post_init__(self) -> None: raise Exception( "Using PPIO api needs PPIO API key, please visit https://ppio.com/docs/models/reference-authentication" ) + if self.bot == "jiekou": + if not self.jiekou_api_key: + raise Exception( + "Using Jiekou AI api needs Jiekou API key, please visit https://api.jiekou.ai" + ) @property def tts_command(self) -> str: @@ -182,6 +188,8 @@ def read_from_file(cls, config_path: str) -> dict: key, value = "bot", "langchain" elif key == "use_ppio": key, value = "bot", "ppio" + elif key == "use_jiekou": + key, value = "bot", "jiekou" elif key == "enable_edge_tts": key, value = "tts", "edge" if key in cls.__dataclass_fields__: