Просмотр исходного кода

流式订阅测试通过-测试通过

qdy 3 недель назад
Родитель
Сommit
a2d450ad99
2 измененных файлов: 51 добавлений и 24 удалений
  1. 23
    4
      internal/routes/log_stream_routes.go
  2. 28
    20
      internal/routes/prompt_stream_routes.go

+ 23
- 4
internal/routes/log_stream_routes.go Просмотреть файл

1
 package routes
1
 package routes
2
 
2
 
3
 import (
3
 import (
4
+	"encoding/json"
4
 	"fmt"
5
 	"fmt"
5
 	"net/http"
6
 	"net/http"
6
 	"strings"
7
 	"strings"
68
 					// 客户端断开连接
69
 					// 客户端断开连接
69
 					return
70
 					return
70
 				case <-time.After(30 * time.Second):
71
 				case <-time.After(30 * time.Second):
71
-					// 发送心跳保持连接
72
-					fmt.Fprintf(w, ": heartbeat\n\n")
72
+					// 发送JSON格式的心跳事件保持连接
73
+					heartbeatEvent := map[string]interface{}{
74
+						"payload": map[string]interface{}{
75
+							"type": "server.heartbeat",
76
+							"properties": map[string]interface{}{
77
+								"timestamp": time.Now().Format(time.RFC3339),
78
+							},
79
+						},
80
+					}
81
+					jsonData, _ := json.Marshal(heartbeatEvent)
82
+					fmt.Fprintf(w, "data: %s\n\n", string(jsonData))
73
 					flusher.Flush()
83
 					flusher.Flush()
74
 				}
84
 				}
75
 			}
85
 			}
114
 				// 客户端断开连接
124
 				// 客户端断开连接
115
 				return
125
 				return
116
 			case <-time.After(30 * time.Second):
126
 			case <-time.After(30 * time.Second):
117
-				// 发送心跳保持连接
118
-				fmt.Fprintf(w, ": heartbeat\n\n")
127
+				// 发送JSON格式的心跳事件保持连接
128
+				heartbeatEvent := map[string]interface{}{
129
+					"payload": map[string]interface{}{
130
+						"type": "server.heartbeat",
131
+						"properties": map[string]interface{}{
132
+							"timestamp": time.Now().Format(time.RFC3339),
133
+						},
134
+					},
135
+				}
136
+				jsonData, _ := json.Marshal(heartbeatEvent)
137
+				fmt.Fprintf(w, "data: %s\n\n", string(jsonData))
119
 				flusher.Flush()
138
 				flusher.Flush()
120
 			}
139
 			}
121
 		}
140
 		}

+ 28
- 20
internal/routes/prompt_stream_routes.go Просмотреть файл

62
 		w.Header().Set("Connection", "keep-alive")
62
 		w.Header().Set("Connection", "keep-alive")
63
 		w.Header().Set("Access-Control-Allow-Origin", "*")
63
 		w.Header().Set("Access-Control-Allow-Origin", "*")
64
 
64
 
65
-		// 创建带超时的上下文
66
-		ctx, cancel := context.WithTimeout(r.Context(), 5*time.Minute)
65
+		// 创建带超时的上下文 - 增加超时时间确保AI有足够时间生成完整响应
66
+		ctx, cancel := context.WithTimeout(r.Context(), 15*time.Minute)
67
 		defer cancel()
67
 		defer cancel()
68
 
68
 
69
 		fmt.Printf("🔍 [StreamPromptHandler] 调用 SendPromptStream, sessionID=%s\n", req.SessionID)
69
 		fmt.Printf("🔍 [StreamPromptHandler] 调用 SendPromptStream, sessionID=%s\n", req.SessionID)
86
 		fmt.Printf("🔍 [StreamPromptHandler] 开始发送流式响应\n")
86
 		fmt.Printf("🔍 [StreamPromptHandler] 开始发送流式响应\n")
87
 		eventCount := 0
87
 		eventCount := 0
88
 
88
 
89
+		// 创建心跳定时器,每30秒发送一次心跳保活(SSE注释格式)
90
+		heartbeatTicker := time.NewTicker(30 * time.Second)
91
+		defer heartbeatTicker.Stop()
92
+
93
+		// 发送初始心跳,确保连接立即活跃
94
+		fmt.Fprintf(w, ": heartbeat\n\n")
95
+		flusher.Flush()
96
+
89
 		for {
97
 		for {
90
 			select {
98
 			select {
91
 			case data, ok := <-ch:
99
 			case data, ok := <-ch:
139
 			case <-ctx.Done():
147
 			case <-ctx.Done():
140
 				fmt.Printf("🔍 [StreamPromptHandler] 上下文超时\n")
148
 				fmt.Printf("🔍 [StreamPromptHandler] 上下文超时\n")
141
 				return
149
 				return
150
+			case <-heartbeatTicker.C:
151
+				// 发送心跳保活(SSE注释格式)
152
+				fmt.Printf("🔍 [StreamPromptHandler] 发送心跳保活\n")
153
+				fmt.Fprintf(w, ": heartbeat\n\n")
154
+				flusher.Flush()
142
 			case <-r.Context().Done():
155
 			case <-r.Context().Done():
143
 				fmt.Printf("🔍 [StreamPromptHandler] 客户端断开连接\n")
156
 				fmt.Printf("🔍 [StreamPromptHandler] 客户端断开连接\n")
144
 				return
157
 				return
194
 		return obj
207
 		return obj
195
 	}
208
 	}
196
 
209
 
197
-	// 事件过滤策略:减少发送给前端的事件数量
210
+	// 事件过滤策略:保守过滤,保留大多数事件以确保连接稳定
198
 	switch typeStr {
211
 	switch typeStr {
199
 	case "message.updated":
212
 	case "message.updated":
200
-		// 检查是否有properties字段
213
+		// 对于message.updated事件,移除content字段避免重复,但不过滤事件本身
201
 		if properties, ok := obj["properties"].(map[string]interface{}); ok {
214
 		if properties, ok := obj["properties"].(map[string]interface{}); ok {
202
 			if info, ok := properties["info"].(map[string]interface{}); ok {
215
 			if info, ok := properties["info"].(map[string]interface{}); ok {
203
 				// 移除content字段,避免重复
216
 				// 移除content字段,避免重复
204
 				delete(info, "content")
217
 				delete(info, "content")
205
-
206
-				// 检查是否有completed时间,如果没有则过滤掉(只发送最终状态)
207
-				if timeInfo, ok := info["time"].(map[string]interface{}); ok {
208
-					if timeInfo["completed"] == nil {
209
-						// 没有completed时间,这是中间状态,过滤掉
210
-						return nil
211
-					}
212
-				}
213
 			}
218
 			}
214
 		}
219
 		}
220
+		// 保留事件,不过滤
221
+		return obj
215
 	case "session.status":
222
 	case "session.status":
216
-		// session.status事件很频繁但前端可能不需要,过滤掉
217
-		return nil
223
+		// 保留状态事件,可能包含重要状态信息
224
+		return obj
218
 	case "session.diff":
225
 	case "session.diff":
219
-		// session.diff事件通常为空,过滤掉
220
-		return nil
226
+		// 保留差异事件,避免客户端逻辑中断
227
+		return obj
221
 	case "server.heartbeat":
228
 	case "server.heartbeat":
222
-		// 心跳事件,过滤掉
223
-		return nil
229
+		// 保留心跳事件,保持连接活跃 - 非常重要!
230
+		return obj
224
 	case "session.idle":
231
 	case "session.idle":
225
-		// 空闲事件,过滤掉
226
-		return nil
232
+		// 保留事件,不过滤
233
+		return obj
227
 	}
234
 	}
228
 
235
 
236
+	// 其他所有事件类型都保留,包括message.part.updated(关键事件)
229
 	return obj
237
 	return obj
230
 }
238
 }

Загрузка…
Отмена
Сохранить