diff --git a/server/routers/chat_router.py b/server/routers/chat_router.py index ad691346..b10945c4 100644 --- a/server/routers/chat_router.py +++ b/server/routers/chat_router.py @@ -171,32 +171,34 @@ def chat_agent(agent_name: str, **kwargs }, ensure_ascii=False).encode('utf-8') + b"\n" - try: - agent = agent_manager.get_runnable_agent(agent_name) - except Exception as e: - logger.error(f"Error getting agent {agent_name}: {e}") - return StreamingResponse(make_chunk(message=f"Error getting agent {agent_name}: {e}", status="error"), media_type='application/json') - # 从config中获取history_round - history_round = config.get("history_round") - history_manager = HistoryManager(history) - messages = history_manager.get_history_with_msg(query, max_rounds=history_round) - history_manager.add_user(query) - - # 如果没有thread_id则生成一个 - if "thread_id" not in config or not config["thread_id"]: - config["thread_id"] = str(uuid.uuid4()) - - # 构造运行时配置 - runnable_config = { - "configurable": { - **config - } - } def stream_messages(): - content = "" + + # 代表服务端已经收到了请求 yield make_chunk(status="init", meta=meta) + + try: + agent = agent_manager.get_runnable_agent(agent_name) + except Exception as e: + logger.error(f"Error getting agent {agent_name}: {e}, {traceback.format_exc()}") + yield make_chunk(message=f"Error getting agent {agent_name}: {e}", status="error") + return + + # 从config中获取history_round + history_round = config.get("history_round") + history_manager = HistoryManager(history) + messages = history_manager.get_history_with_msg(query, max_rounds=history_round) + history_manager.add_user(query) + + # 构造运行时配置,如果没有thread_id则生成一个 + if "thread_id" not in config or not config["thread_id"]: + config["thread_id"] = str(uuid.uuid4()) + + runnable_config = {"configurable": {**config}} + + content = "" + try: for msg, metadata in agent.stream_messages(messages, config_schema=runnable_config): if isinstance(msg, AIMessageChunk) and msg.content != "": @@ -214,7 +216,7 @@ def chat_agent(agent_name: str, history=history_manager.update_ai(content), meta=meta) except Exception as e: - logger.error(f"Error streaming messages: {e}") + logger.error(f"Error streaming messages: {e}, {traceback.format_exc()}") yield make_chunk(message=f"Error streaming messages: {e}", status="error") return StreamingResponse(stream_messages(), media_type='application/json') diff --git a/src/agents/registry.py b/src/agents/registry.py index dc974bd7..9ed7eca5 100644 --- a/src/agents/registry.py +++ b/src/agents/registry.py @@ -3,6 +3,7 @@ from __future__ import annotations import os from typing import Type, Annotated, Optional, TypedDict +from enum import Enum from abc import abstractmethod from dataclasses import dataclass, fields, field @@ -96,7 +97,7 @@ class BaseAgent(): return for requirement in self.requirements: if requirement not in os.environ: - raise ValueError(f"{requirement} is not set") + raise ValueError(f"没有配置{requirement} 环境变量,请在 src/.env 文件中配置,并重新启动服务") def stream_values(self, messages: list[str], config_schema: RunnableConfig = None, **kwargs): graph = self.get_graph(config_schema=config_schema, **kwargs) diff --git a/src/config/__init__.py b/src/config/__init__.py index 5da6842c..da6bcac8 100644 --- a/src/config/__init__.py +++ b/src/config/__init__.py @@ -47,7 +47,7 @@ class Config(SimpleConfig): self.add_item("enable_reranker", default=False, des="是否开启重排序") self.add_item("enable_knowledge_base", default=False, des="是否开启知识库") self.add_item("enable_knowledge_graph", default=False, des="是否开启知识图谱") - self.add_item("enable_web_search", default=False, des="是否开启网页搜索(需配置 TAVILY_API_KEY)") + self.add_item("enable_web_search", default=False, des="是否开启网页搜索(注:现阶段会根据 TAVILY_API_KEY 自动开启,无法手动配置,将会在下个版本移除此配置项)") # 模型配置 ## 注意这里是模型名,而不是具体的模型路径,默认使用 HuggingFace 的路径 ## 如果需要自定义本地模型路径,则在 src/.env 中配置 MODEL_DIR @@ -150,9 +150,13 @@ class Config(SimpleConfig): self.model_provider_status[provider] = all(conds_bool) # 检查web_search的环境变量 - if self.enable_web_search and not os.getenv("TAVILY_API_KEY"): - logger.warning("TAVILY_API_KEY not set, web search will be disabled") - self.enable_web_search = False + # if self.enable_web_search and not os.getenv("TAVILY_API_KEY"): + # logger.warning("TAVILY_API_KEY not set, web search will be disabled") + # self.enable_web_search = False + + # 2025.04.08 修改为不手动配置,只要配置了TAVILY_API_KEY,就默认开启web_search + if os.getenv("TAVILY_API_KEY"): + self.enable_web_search = True self.valuable_model_provider = [k for k, v in self.model_provider_status.items() if v] assert len(self.valuable_model_provider) > 0, f"No model provider available, please check your `.env` file. API_KEY_LIST: {conds}" diff --git a/src/models/chat_model.py b/src/models/chat_model.py index 065d9e36..0aacd6f6 100644 --- a/src/models/chat_model.py +++ b/src/models/chat_model.py @@ -1,18 +1,19 @@ import os +import requests from openai import OpenAI from src.utils import logger, get_docker_safe_url from langchain_openai import ChatOpenAI class OpenAIBase(): - def __init__(self, api_key, base_url, model_name): + def __init__(self, api_key, base_url, model_name, **kwargs): self.api_key = api_key self.base_url = base_url self.client = OpenAI(api_key=api_key, base_url=base_url) self.model_name = model_name + self.info = kwargs self.chat_open_ai = ChatOpenAI(model=model_name, api_key=api_key, - base_url=base_url, - temperature=0.7) + base_url=base_url) def predict(self, message, stream=False): if isinstance(message, str): @@ -54,6 +55,49 @@ class OpenAIBase(): logger.error(f"Error getting models: {e}") return [] + def _get_model_by_model_url(self, model_url): + """ + Refs: https://docs.together.ai/reference/models-1 + + Return: [ + { + "id": "meta-llama/Meta-Llama-3-70B-Instruct-Turbo", + "object": "model", + "created": 0, + "type": "chat", + "running": false, + "display_name": "Meta Llama 3 70B Instruct Turbo", + "organization": "Meta", + "link": "https://huggingface.co/meta-llama/Meta-Llama-3-70B-Instruct", + "license": "Llama-3 (Other)", + "context_length": 8192, + "config": { + "chat_template": "{% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' %}{% if loop.index0 == 0 %}{% set content = bos_token + content %}{% endif %}{{ content }}{% endfor %}{{ '<|start_header_id|>assistant<|end_header_id|>\n\n' }}", + "stop": [ + "<|eot_id|>" + ], + "bos_token": "<|begin_of_text|>", + "eos_token": "<|end_of_text|>" + }, + "pricing": { + "hourly": 0, + "input": 0.88, + "output": 0.88, + "base": 0, + "finetune": 0 + } + }, + ] + + """ + headers = { + "accept": "application/json", + "authorization": f"Bearer {self.api_key}" + } + response = requests.get(model_url, headers=headers) + return response.json() + + class OpenModel(OpenAIBase): def __init__(self, model_name=None): diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index d1e072cc..5272d74b 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -511,6 +511,18 @@ const handleStreamResponse = async (response) => { } } catch (error) { console.error('流式处理出错:', error); + const lastMsg = messages.value[messages.value.length - 1]; + if (lastMsg.role === 'assistant') { + lastMsg.status = 'error'; + lastMsg.message = error.message; + } else { + messages.value.push({ + role: 'assistant', + message: `发生错误: ${error.message}`, + status: 'error' + }); + await scrollToBottom(); + } isProcessing.value = false; } }; diff --git a/web/src/components/MessageComponent.vue b/web/src/components/MessageComponent.vue index 1a5d0a88..09d1a088 100644 --- a/web/src/components/MessageComponent.vue +++ b/web/src/components/MessageComponent.vue @@ -48,7 +48,7 @@ previewTheme="github" :showCodeRowNumber="false" :modelValue="message.content" - :key="index" + :key="message.id" class="message-md"/>
diff --git a/web/src/views/SettingView.vue b/web/src/views/SettingView.vue index 6af88d50..c8ba40df 100644 --- a/web/src/views/SettingView.vue +++ b/web/src/views/SettingView.vue @@ -6,7 +6,7 @@ @@ -63,10 +63,11 @@
{{ items?.enable_web_search.des }} - + /> --> +
{{ items?.enable_reranker.des }} @@ -264,7 +265,7 @@
@@ -387,11 +388,11 @@ const handleChange = (key, e) => { if (!isNeedRestart.value) { isNeedRestart.value = true notification.info({ - message: '需要重启服务', - description: '请点击右下角按钮重启服务', + message: '需要重新加载模型', + description: '请点击右下角按钮重新加载模型', placement: 'topLeft', duration: 0, - btn: h(Button, { type: 'primary', onClick: sendRestart }, '立即重启') + btn: h(Button, { type: 'primary', onClick: sendRestart }, '立即重新加载') }) } } @@ -543,7 +544,7 @@ const openProviderConfig = (provider) => { const saveProviderConfig = async () => { if (!modelStatus.value[providerConfig.provider]) { - message.error('请在 src/.env 中配置对应的 APIKEY') + message.error('请在 src/.env 中配置对应的 APIKEY,并重新启动服务') return }