Skip to content

Commit 884153a

Browse files
committed
feat: 增强模型选择和错误处理机制
优化模型ID传递逻辑,支持请求级别的模型覆盖;完善流式响应的错误处理
1 parent 7cf5dff commit 884153a

File tree

10 files changed

+83
-26
lines changed

10 files changed

+83
-26
lines changed

frontend/src/components/Input-field.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,14 @@ const loadConversationConfig = async () => {
354354
try {
355355
const response = await getConversationConfig();
356356
conversationConfig.value = response.data;
357+
const modelId = response.data?.summary_model_id || '';
357358
settingsStore.updateConversationModels({
358-
summaryModelId: response.data?.summary_model_id || '',
359+
summaryModelId: modelId,
360+
selectedChatModelId: modelId,
359361
rerankModelId: response.data?.rerank_model_id || '',
360362
});
361363
if (!selectedModelId.value) {
362-
selectedModelId.value = response.data?.summary_model_id || '';
364+
selectedModelId.value = modelId;
363365
}
364366
ensureModelSelection();
365367
} catch (error) {
@@ -437,6 +439,7 @@ const handleModelChange = async (value: string | number | Array<string | number>
437439
// 同步到 store
438440
settingsStore.updateConversationModels({
439441
summaryModelId: val,
442+
selectedChatModelId: val,
440443
rerankModelId: conversationConfig.value?.rerank_model_id || '',
441444
});
442445

frontend/src/stores/menu.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const useMenuStore = defineStore('menuStore', () => {
3434
const isFirstSession = ref(false)
3535
const firstQuery = ref('')
3636
const firstMentionedItems = ref<any[]>([])
37+
const firstModelId = ref('')
3738

3839
const applyMenuTranslations = () => {
3940
menuArr.forEach(item => {
@@ -92,16 +93,18 @@ export const useMenuStore = defineStore('menuStore', () => {
9293
isFirstSession.value = payload
9394
}
9495

95-
const changeFirstQuery = (payload: string, mentionedItems: any[] = []) => {
96+
const changeFirstQuery = (payload: string, mentionedItems: any[] = [], modelId: string = '') => {
9697
firstQuery.value = payload
9798
firstMentionedItems.value = mentionedItems
99+
firstModelId.value = modelId
98100
}
99101

100102
return {
101103
menuArr,
102104
isFirstSession,
103105
firstQuery,
104106
firstMentionedItems,
107+
firstModelId,
105108
clearMenuArr,
106109
updatemenuArr,
107110
updataMenuChildren,

frontend/src/views/chat/index.vue

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const useSettingsStoreInstance = useSettingsStore();
6161
const uiStore = useUIStore();
6262
const { navigateToKnowledgeBaseList } = useKnowledgeBaseCreationNavigation();
6363
const { t } = useI18n();
64-
const { menuArr, isFirstSession, firstQuery, firstMentionedItems } = storeToRefs(usemenuStore);
64+
const { menuArr, isFirstSession, firstQuery, firstMentionedItems, firstModelId } = storeToRefs(usemenuStore);
6565
const { output, onChunk, isStreaming, isLoading, error, startStream, stopStream } = useStream();
6666
const route = useRoute();
6767
const router = useRouter();
@@ -672,13 +672,21 @@ const handleAgentChunk = (data) => {
672672
673673
// If this is an error response without tool data, handle it
674674
if (data.response_type === 'error' && !toolName) {
675-
message.content = data.content || t('chat.processError');
675+
const errorMsg = data.content || t('chat.processError');
676+
message.content = errorMsg;
676677
isReplying.value = false;
678+
loading.value = false;
679+
MessagePlugin.error(errorMsg);
680+
console.error('[Chat Error]', errorMsg);
677681
}
678682
} else if (data.response_type === 'error') {
679683
// Generic error without tool context
680-
message.content = data.content || t('chat.processError');
684+
const errorMsg = data.content || t('chat.processError');
685+
message.content = errorMsg;
681686
isReplying.value = false;
687+
loading.value = false;
688+
MessagePlugin.error(errorMsg);
689+
console.error('[Chat Error]', errorMsg);
682690
}
683691
break;
684692
@@ -793,8 +801,8 @@ onMounted(async () => {
793801
checkmenuTitle(session_id.value)
794802
if (firstQuery.value) {
795803
scrollLock.value = true;
796-
sendMsg(firstQuery.value, '', firstMentionedItems.value || []);
797-
usemenuStore.changeFirstQuery('');
804+
sendMsg(firstQuery.value, firstModelId.value || '', firstMentionedItems.value || []);
805+
usemenuStore.changeFirstQuery('', [], '');
798806
} else {
799807
scrollLock.value = false;
800808
let data = {

frontend/src/views/creatChat/creatChat.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ const { t } = useI18n();
4040
const { navigateToKnowledgeBaseList } = useKnowledgeBaseCreationNavigation();
4141
4242
const sendMsg = (value: string, modelId: string, mentionedItems: any[]) => {
43-
createNewSession(value, mentionedItems);
43+
createNewSession(value, modelId, mentionedItems);
4444
}
4545
46-
async function createNewSession(value: string, mentionedItems: any[] = []) {
46+
async function createNewSession(value: string, modelId: string, mentionedItems: any[] = []) {
4747
const selectedKbs = settingsStore.settings.selectedKnowledgeBases || [];
4848
const selectedFiles = settingsStore.settings.selectedFiles || [];
4949
@@ -63,7 +63,7 @@ async function createNewSession(value: string, mentionedItems: any[] = []) {
6363
try {
6464
const res = await createSessions(sessionData);
6565
if (res.data && res.data.id) {
66-
await navigateToSession(res.data.id, value, mentionedItems);
66+
await navigateToSession(res.data.id, value, modelId, mentionedItems);
6767
} else {
6868
console.error('[createChat] Failed to create session');
6969
MessagePlugin.error(t('createChat.messages.createFailed'));
@@ -74,7 +74,7 @@ async function createNewSession(value: string, mentionedItems: any[] = []) {
7474
}
7575
}
7676
77-
const navigateToSession = async (sessionId: string, value: string, mentionedItems: any[]) => {
77+
const navigateToSession = async (sessionId: string, value: string, modelId: string, mentionedItems: any[]) => {
7878
const now = new Date().toISOString();
7979
let obj = {
8080
title: t('createChat.newSessionTitle'),
@@ -87,7 +87,7 @@ const navigateToSession = async (sessionId: string, value: string, mentionedItem
8787
};
8888
usemenuStore.updataMenuChildren(obj);
8989
usemenuStore.changeIsFirstSession(true);
90-
usemenuStore.changeFirstQuery(value, mentionedItems);
90+
usemenuStore.changeFirstQuery(value, mentionedItems, modelId);
9191
router.push(`/platform/chat/${sessionId}`);
9292
}
9393

internal/application/service/chat_pipline/chat_completion_stream.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,24 @@ func (p *PluginChatCompletionStream) OnEvent(ctx context.Context,
104104
var finalContent string
105105

106106
for response := range responseChan {
107+
// Handle error responses from the stream
108+
if response.ResponseType == types.ResponseTypeError {
109+
logger.Errorf(ctx, "Stream error received: %s", response.Content)
110+
if err := eventBus.Emit(ctx, types.Event{
111+
ID: fmt.Sprintf("%s-error", uuid.New().String()[:8]),
112+
Type: types.EventType(event.EventError),
113+
SessionID: chatManage.SessionID,
114+
Data: event.ErrorData{
115+
Error: response.Content,
116+
Stage: "chat_completion_stream",
117+
SessionID: chatManage.SessionID,
118+
},
119+
}); err != nil {
120+
logger.Errorf(ctx, "Failed to emit error event: %v", err)
121+
}
122+
continue
123+
}
124+
107125
// Emit event for each answer chunk
108126
if response.ResponseType == types.ResponseTypeAnswer {
109127
finalContent += response.Content

internal/application/service/session.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,9 @@ func (s *sessionService) KnowledgeQA(
461461
// Ensure defaults are set
462462
customAgent.EnsureDefaults()
463463

464-
// Override model ID
465-
if customAgent.Config.ModelID != "" {
464+
// Override model ID only if request didn't specify summaryModelID
465+
// Request's summaryModelID has highest priority
466+
if summaryModelID == "" && customAgent.Config.ModelID != "" {
466467
chatModelID = customAgent.Config.ModelID
467468
logger.Infof(ctx, "Using custom agent's model_id: %s", chatModelID)
468469
}
@@ -1055,11 +1056,13 @@ func (s *sessionService) SearchKnowledge(ctx context.Context,
10551056

10561057
// AgentQA performs agent-based question answering with conversation history and streaming support
10571058
// customAgent is optional - if provided, uses custom agent configuration instead of tenant defaults
1059+
// summaryModelID is optional - if provided, overrides the model from customAgent config
10581060
func (s *sessionService) AgentQA(
10591061
ctx context.Context,
10601062
session *types.Session,
10611063
query string,
10621064
assistantMessageID string,
1065+
summaryModelID string,
10631066
eventBus *event.EventBus,
10641067
customAgent *types.CustomAgent,
10651068
knowledgeBaseIDs []string,
@@ -1163,15 +1166,23 @@ func (s *sessionService) AgentQA(
11631166
agentConfig.SearchTargets = searchTargets
11641167
logger.Infof(ctx, "Agent search targets built: %d targets", len(searchTargets))
11651168

1166-
// Get summary model from custom agent config
1169+
// Get summary model: prioritize request's summaryModelID, then custom agent config
11671170
// Note: tenantInfo.ConversationConfig is deprecated, all config comes from customAgent now
1168-
summaryModelID := customAgent.Config.ModelID
1169-
if summaryModelID == "" {
1171+
effectiveModelID := summaryModelID
1172+
if effectiveModelID == "" {
1173+
effectiveModelID = customAgent.Config.ModelID
1174+
}
1175+
if effectiveModelID == "" {
11701176
logger.Warnf(ctx, "No summary model configured for custom agent %s", customAgent.ID)
11711177
return errors.New("summary model (model_id) is not configured in custom agent settings")
11721178
}
1179+
if summaryModelID != "" {
1180+
logger.Infof(ctx, "Using request's summary model override: %s", effectiveModelID)
1181+
} else {
1182+
logger.Infof(ctx, "Using custom agent's model_id: %s", effectiveModelID)
1183+
}
11731184

1174-
summaryModel, err := s.modelService.GetChatModel(ctx, summaryModelID)
1185+
summaryModel, err := s.modelService.GetChatModel(ctx, effectiveModelID)
11751186
if err != nil {
11761187
logger.Warnf(ctx, "Failed to get chat model: %v", err)
11771188
return fmt.Errorf("failed to get chat model: %w", err)

internal/handler/session/qa.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ func (h *Handler) executeAgentModeQA(reqCtx *qaRequestContext) {
443443
reqCtx.session,
444444
reqCtx.query,
445445
reqCtx.assistantMessage.ID,
446+
reqCtx.summaryModelID,
446447
streamCtx.eventBus,
447448
reqCtx.customAgent,
448449
reqCtx.knowledgeBaseIDs,

internal/models/chat/ollama.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ func (c *OllamaChat) ChatStream(
189189
logger.GetLogger(ctx).Errorf("流式聊天请求失败: %v", err)
190190
// 发送错误响应
191191
streamChan <- types.StreamResponse{
192-
ResponseType: types.ResponseTypeAnswer,
192+
ResponseType: types.ResponseTypeError,
193+
Content: err.Error(),
193194
Done: true,
194195
}
195196
}

internal/models/chat/remote_api.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -419,12 +419,22 @@ func (c *RemoteAPIChat) ChatStream(ctx context.Context,
419419
for {
420420
response, err := stream.Recv()
421421
if err != nil {
422-
// 发送最后一个响应,包含收集到的 tool calls
423-
streamChan <- types.StreamResponse{
424-
ResponseType: types.ResponseTypeAnswer,
425-
Content: "",
426-
Done: true,
427-
ToolCalls: buildOrderedToolCalls(),
422+
// Check if it's a normal end of stream (io.EOF)
423+
if err.Error() == "EOF" {
424+
// Normal end of stream, send final response with collected tool calls
425+
streamChan <- types.StreamResponse{
426+
ResponseType: types.ResponseTypeAnswer,
427+
Content: "",
428+
Done: true,
429+
ToolCalls: buildOrderedToolCalls(),
430+
}
431+
} else {
432+
// Actual error, send error response
433+
streamChan <- types.StreamResponse{
434+
ResponseType: types.ResponseTypeError,
435+
Content: err.Error(),
436+
Done: true,
437+
}
428438
}
429439
return
430440
}

internal/types/interfaces/session.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ type SessionService interface {
4949
// AgentQA performs agent-based question answering with conversation history and streaming support
5050
// eventBus is optional - if nil, uses service's default EventBus
5151
// customAgent is optional - if provided, uses custom agent configuration instead of tenant defaults
52+
// summaryModelID is optional - if provided, overrides the model from customAgent config
5253
AgentQA(
5354
ctx context.Context,
5455
session *types.Session,
5556
query string,
5657
assistantMessageID string,
58+
summaryModelID string,
5759
eventBus *event.EventBus,
5860
customAgent *types.CustomAgent,
5961
knowledgeBaseIDs []string,

0 commit comments

Comments
 (0)