stream_transformer.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. package antigravity
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "log"
  7. "strings"
  8. )
  9. // BlockType 内容块类型
  10. type BlockType int
  11. const (
  12. BlockTypeNone BlockType = iota
  13. BlockTypeText
  14. BlockTypeThinking
  15. BlockTypeFunction
  16. )
  17. // UsageMapHook is a callback that can modify usage data before it's emitted in SSE events.
  18. type UsageMapHook func(usageMap map[string]any)
  19. // StreamingProcessor 流式响应处理器
  20. type StreamingProcessor struct {
  21. blockType BlockType
  22. blockIndex int
  23. messageStartSent bool
  24. messageStopSent bool
  25. usedTool bool
  26. pendingSignature string
  27. trailingSignature string
  28. originalModel string
  29. webSearchQueries []string
  30. groundingChunks []GeminiGroundingChunk
  31. usageMapHook UsageMapHook
  32. // 累计 usage
  33. inputTokens int
  34. outputTokens int
  35. cacheReadTokens int
  36. imageOutputTokens int
  37. }
  38. // NewStreamingProcessor 创建流式响应处理器
  39. func NewStreamingProcessor(originalModel string) *StreamingProcessor {
  40. return &StreamingProcessor{
  41. blockType: BlockTypeNone,
  42. originalModel: originalModel,
  43. }
  44. }
  45. // SetUsageMapHook sets an optional hook that modifies usage maps before they are emitted.
  46. func (p *StreamingProcessor) SetUsageMapHook(fn UsageMapHook) {
  47. p.usageMapHook = fn
  48. }
  49. func usageToMap(u ClaudeUsage) map[string]any {
  50. m := map[string]any{
  51. "input_tokens": u.InputTokens,
  52. "output_tokens": u.OutputTokens,
  53. }
  54. if u.CacheCreationInputTokens > 0 {
  55. m["cache_creation_input_tokens"] = u.CacheCreationInputTokens
  56. }
  57. if u.CacheReadInputTokens > 0 {
  58. m["cache_read_input_tokens"] = u.CacheReadInputTokens
  59. }
  60. if u.ImageOutputTokens > 0 {
  61. m["image_output_tokens"] = u.ImageOutputTokens
  62. }
  63. return m
  64. }
  65. // ProcessLine 处理 SSE 行,返回 Claude SSE 事件
  66. func (p *StreamingProcessor) ProcessLine(line string) []byte {
  67. line = strings.TrimSpace(line)
  68. if line == "" || !strings.HasPrefix(line, "data:") {
  69. return nil
  70. }
  71. data := strings.TrimSpace(strings.TrimPrefix(line, "data:"))
  72. if data == "" || data == "[DONE]" {
  73. return nil
  74. }
  75. // 解包 v1internal 响应
  76. var v1Resp V1InternalResponse
  77. if err := json.Unmarshal([]byte(data), &v1Resp); err != nil {
  78. // 尝试直接解析为 GeminiResponse
  79. var directResp GeminiResponse
  80. if err2 := json.Unmarshal([]byte(data), &directResp); err2 != nil {
  81. return nil
  82. }
  83. v1Resp.Response = directResp
  84. v1Resp.ResponseID = directResp.ResponseID
  85. v1Resp.ModelVersion = directResp.ModelVersion
  86. }
  87. geminiResp := &v1Resp.Response
  88. var result bytes.Buffer
  89. // 发送 message_start
  90. if !p.messageStartSent {
  91. _, _ = result.Write(p.emitMessageStart(&v1Resp))
  92. }
  93. // 更新 usage
  94. // 注意:Gemini 的 promptTokenCount 包含 cachedContentTokenCount,
  95. // 但 Claude 的 input_tokens 不包含 cache_read_input_tokens,需要减去
  96. if geminiResp.UsageMetadata != nil {
  97. cached := geminiResp.UsageMetadata.CachedContentTokenCount
  98. p.inputTokens = geminiResp.UsageMetadata.PromptTokenCount - cached
  99. p.outputTokens = geminiResp.UsageMetadata.CandidatesTokenCount + geminiResp.UsageMetadata.ThoughtsTokenCount
  100. p.cacheReadTokens = cached
  101. p.imageOutputTokens = geminiResp.UsageMetadata.ImageOutputTokens()
  102. }
  103. // 处理 parts
  104. if len(geminiResp.Candidates) > 0 && geminiResp.Candidates[0].Content != nil {
  105. for _, part := range geminiResp.Candidates[0].Content.Parts {
  106. _, _ = result.Write(p.processPart(&part))
  107. }
  108. }
  109. if len(geminiResp.Candidates) > 0 {
  110. p.captureGrounding(geminiResp.Candidates[0].GroundingMetadata)
  111. }
  112. // 检查是否结束
  113. if len(geminiResp.Candidates) > 0 {
  114. finishReason := geminiResp.Candidates[0].FinishReason
  115. if finishReason == "MALFORMED_FUNCTION_CALL" {
  116. log.Printf("[Antigravity] MALFORMED_FUNCTION_CALL detected in stream for model %s", p.originalModel)
  117. if geminiResp.Candidates[0].Content != nil {
  118. if b, err := json.Marshal(geminiResp.Candidates[0].Content); err == nil {
  119. log.Printf("[Antigravity] Malformed content: %s", string(b))
  120. }
  121. }
  122. }
  123. if finishReason != "" {
  124. _, _ = result.Write(p.emitFinish(finishReason))
  125. }
  126. }
  127. return result.Bytes()
  128. }
  129. // Finish 结束处理,返回最终事件和用量。
  130. // 若整个流未收到任何可解析的上游数据(messageStartSent == false),
  131. // 则不补发任何结束事件,防止客户端收到没有 message_start 的残缺流。
  132. func (p *StreamingProcessor) Finish() ([]byte, *ClaudeUsage) {
  133. usage := &ClaudeUsage{
  134. InputTokens: p.inputTokens,
  135. OutputTokens: p.outputTokens,
  136. CacheReadInputTokens: p.cacheReadTokens,
  137. ImageOutputTokens: p.imageOutputTokens,
  138. }
  139. if !p.messageStartSent {
  140. return nil, usage
  141. }
  142. var result bytes.Buffer
  143. if !p.messageStopSent {
  144. _, _ = result.Write(p.emitFinish(""))
  145. }
  146. return result.Bytes(), usage
  147. }
  148. // MessageStartSent 报告流中是否已发出过 message_start 事件(即是否收到过有效的上游数据)
  149. func (p *StreamingProcessor) MessageStartSent() bool {
  150. return p.messageStartSent
  151. }
  152. // emitMessageStart 发送 message_start 事件
  153. func (p *StreamingProcessor) emitMessageStart(v1Resp *V1InternalResponse) []byte {
  154. if p.messageStartSent {
  155. return nil
  156. }
  157. usage := ClaudeUsage{}
  158. if v1Resp.Response.UsageMetadata != nil {
  159. cached := v1Resp.Response.UsageMetadata.CachedContentTokenCount
  160. usage.InputTokens = v1Resp.Response.UsageMetadata.PromptTokenCount - cached
  161. usage.OutputTokens = v1Resp.Response.UsageMetadata.CandidatesTokenCount + v1Resp.Response.UsageMetadata.ThoughtsTokenCount
  162. usage.CacheReadInputTokens = cached
  163. usage.ImageOutputTokens = v1Resp.Response.UsageMetadata.ImageOutputTokens()
  164. }
  165. responseID := v1Resp.ResponseID
  166. if responseID == "" {
  167. responseID = v1Resp.Response.ResponseID
  168. }
  169. if responseID == "" {
  170. responseID = generateAnthropicMsgID()
  171. }
  172. var usageValue any = usage
  173. if p.usageMapHook != nil {
  174. usageMap := usageToMap(usage)
  175. p.usageMapHook(usageMap)
  176. usageValue = usageMap
  177. }
  178. message := map[string]any{
  179. "id": responseID,
  180. "type": "message",
  181. "role": "assistant",
  182. "content": []any{},
  183. "model": p.originalModel,
  184. "stop_reason": nil,
  185. "stop_sequence": nil,
  186. "usage": usageValue,
  187. }
  188. event := map[string]any{
  189. "type": "message_start",
  190. "message": message,
  191. }
  192. p.messageStartSent = true
  193. return p.formatSSE("message_start", event)
  194. }
  195. // processPart 处理单个 part
  196. func (p *StreamingProcessor) processPart(part *GeminiPart) []byte {
  197. var result bytes.Buffer
  198. signature := part.ThoughtSignature
  199. // 1. FunctionCall 处理
  200. if part.FunctionCall != nil {
  201. // 先处理 trailingSignature
  202. if p.trailingSignature != "" {
  203. _, _ = result.Write(p.endBlock())
  204. _, _ = result.Write(p.emitEmptyThinkingWithSignature(p.trailingSignature))
  205. p.trailingSignature = ""
  206. }
  207. _, _ = result.Write(p.processFunctionCall(part.FunctionCall, signature))
  208. return result.Bytes()
  209. }
  210. // 2. Text 处理
  211. if part.Text != "" || part.Thought {
  212. if part.Thought {
  213. _, _ = result.Write(p.processThinking(part.Text, signature))
  214. } else {
  215. _, _ = result.Write(p.processText(part.Text, signature))
  216. }
  217. }
  218. // 3. InlineData (Image) 处理
  219. if part.InlineData != nil && part.InlineData.Data != "" {
  220. markdownImg := fmt.Sprintf("![image](data:%s;base64,%s)",
  221. part.InlineData.MimeType, part.InlineData.Data)
  222. _, _ = result.Write(p.processText(markdownImg, ""))
  223. }
  224. return result.Bytes()
  225. }
  226. func (p *StreamingProcessor) captureGrounding(grounding *GeminiGroundingMetadata) {
  227. if grounding == nil {
  228. return
  229. }
  230. if len(grounding.WebSearchQueries) > 0 && len(p.webSearchQueries) == 0 {
  231. p.webSearchQueries = append([]string(nil), grounding.WebSearchQueries...)
  232. }
  233. if len(grounding.GroundingChunks) > 0 && len(p.groundingChunks) == 0 {
  234. p.groundingChunks = append([]GeminiGroundingChunk(nil), grounding.GroundingChunks...)
  235. }
  236. }
  237. // processThinking 处理 thinking
  238. func (p *StreamingProcessor) processThinking(text, signature string) []byte {
  239. var result bytes.Buffer
  240. // 处理之前的 trailingSignature
  241. if p.trailingSignature != "" {
  242. _, _ = result.Write(p.endBlock())
  243. _, _ = result.Write(p.emitEmptyThinkingWithSignature(p.trailingSignature))
  244. p.trailingSignature = ""
  245. }
  246. // 开始或继续 thinking 块
  247. if p.blockType != BlockTypeThinking {
  248. _, _ = result.Write(p.startBlock(BlockTypeThinking, map[string]any{
  249. "type": "thinking",
  250. "thinking": "",
  251. }))
  252. }
  253. if text != "" {
  254. _, _ = result.Write(p.emitDelta("thinking_delta", map[string]any{
  255. "thinking": text,
  256. }))
  257. }
  258. // 暂存签名
  259. if signature != "" {
  260. p.pendingSignature = signature
  261. }
  262. return result.Bytes()
  263. }
  264. // processText 处理普通 text
  265. func (p *StreamingProcessor) processText(text, signature string) []byte {
  266. var result bytes.Buffer
  267. // 空 text 带签名 - 暂存
  268. if text == "" {
  269. if signature != "" {
  270. p.trailingSignature = signature
  271. }
  272. return nil
  273. }
  274. // 处理之前的 trailingSignature
  275. if p.trailingSignature != "" {
  276. _, _ = result.Write(p.endBlock())
  277. _, _ = result.Write(p.emitEmptyThinkingWithSignature(p.trailingSignature))
  278. p.trailingSignature = ""
  279. }
  280. // 非空 text 带签名 - 特殊处理
  281. if signature != "" {
  282. _, _ = result.Write(p.startBlock(BlockTypeText, map[string]any{
  283. "type": "text",
  284. "text": "",
  285. }))
  286. _, _ = result.Write(p.emitDelta("text_delta", map[string]any{
  287. "text": text,
  288. }))
  289. _, _ = result.Write(p.endBlock())
  290. _, _ = result.Write(p.emitEmptyThinkingWithSignature(signature))
  291. return result.Bytes()
  292. }
  293. // 普通 text (无签名)
  294. if p.blockType != BlockTypeText {
  295. _, _ = result.Write(p.startBlock(BlockTypeText, map[string]any{
  296. "type": "text",
  297. "text": "",
  298. }))
  299. }
  300. _, _ = result.Write(p.emitDelta("text_delta", map[string]any{
  301. "text": text,
  302. }))
  303. return result.Bytes()
  304. }
  305. // processFunctionCall 处理 function call
  306. func (p *StreamingProcessor) processFunctionCall(fc *GeminiFunctionCall, signature string) []byte {
  307. var result bytes.Buffer
  308. p.usedTool = true
  309. toolID := fc.ID
  310. if toolID == "" {
  311. toolID = fmt.Sprintf("%s-%s", fc.Name, generateRandomID())
  312. }
  313. toolUse := map[string]any{
  314. "type": "tool_use",
  315. "id": toolID,
  316. "name": fc.Name,
  317. "input": map[string]any{},
  318. }
  319. if signature != "" {
  320. toolUse["signature"] = signature
  321. }
  322. _, _ = result.Write(p.startBlock(BlockTypeFunction, toolUse))
  323. // 发送 input_json_delta
  324. if fc.Args != nil {
  325. argsJSON, _ := json.Marshal(fc.Args)
  326. _, _ = result.Write(p.emitDelta("input_json_delta", map[string]any{
  327. "partial_json": string(argsJSON),
  328. }))
  329. }
  330. _, _ = result.Write(p.endBlock())
  331. return result.Bytes()
  332. }
  333. // startBlock 开始新的内容块
  334. func (p *StreamingProcessor) startBlock(blockType BlockType, contentBlock map[string]any) []byte {
  335. var result bytes.Buffer
  336. if p.blockType != BlockTypeNone {
  337. _, _ = result.Write(p.endBlock())
  338. }
  339. event := map[string]any{
  340. "type": "content_block_start",
  341. "index": p.blockIndex,
  342. "content_block": contentBlock,
  343. }
  344. _, _ = result.Write(p.formatSSE("content_block_start", event))
  345. p.blockType = blockType
  346. return result.Bytes()
  347. }
  348. // endBlock 结束当前内容块
  349. func (p *StreamingProcessor) endBlock() []byte {
  350. if p.blockType == BlockTypeNone {
  351. return nil
  352. }
  353. var result bytes.Buffer
  354. // Thinking 块结束时发送暂存的签名
  355. if p.blockType == BlockTypeThinking && p.pendingSignature != "" {
  356. _, _ = result.Write(p.emitDelta("signature_delta", map[string]any{
  357. "signature": p.pendingSignature,
  358. }))
  359. p.pendingSignature = ""
  360. }
  361. event := map[string]any{
  362. "type": "content_block_stop",
  363. "index": p.blockIndex,
  364. }
  365. _, _ = result.Write(p.formatSSE("content_block_stop", event))
  366. p.blockIndex++
  367. p.blockType = BlockTypeNone
  368. return result.Bytes()
  369. }
  370. // emitDelta 发送 delta 事件
  371. func (p *StreamingProcessor) emitDelta(deltaType string, deltaContent map[string]any) []byte {
  372. delta := map[string]any{
  373. "type": deltaType,
  374. }
  375. for k, v := range deltaContent {
  376. delta[k] = v
  377. }
  378. event := map[string]any{
  379. "type": "content_block_delta",
  380. "index": p.blockIndex,
  381. "delta": delta,
  382. }
  383. return p.formatSSE("content_block_delta", event)
  384. }
  385. // emitEmptyThinkingWithSignature 发送空 thinking 块承载签名
  386. func (p *StreamingProcessor) emitEmptyThinkingWithSignature(signature string) []byte {
  387. var result bytes.Buffer
  388. _, _ = result.Write(p.startBlock(BlockTypeThinking, map[string]any{
  389. "type": "thinking",
  390. "thinking": "",
  391. }))
  392. _, _ = result.Write(p.emitDelta("thinking_delta", map[string]any{
  393. "thinking": "",
  394. }))
  395. _, _ = result.Write(p.emitDelta("signature_delta", map[string]any{
  396. "signature": signature,
  397. }))
  398. _, _ = result.Write(p.endBlock())
  399. return result.Bytes()
  400. }
  401. // emitFinish 发送结束事件
  402. func (p *StreamingProcessor) emitFinish(finishReason string) []byte {
  403. var result bytes.Buffer
  404. // 关闭最后一个块
  405. _, _ = result.Write(p.endBlock())
  406. // 处理 trailingSignature
  407. if p.trailingSignature != "" {
  408. _, _ = result.Write(p.emitEmptyThinkingWithSignature(p.trailingSignature))
  409. p.trailingSignature = ""
  410. }
  411. if len(p.webSearchQueries) > 0 || len(p.groundingChunks) > 0 {
  412. groundingText := buildGroundingText(&GeminiGroundingMetadata{
  413. WebSearchQueries: p.webSearchQueries,
  414. GroundingChunks: p.groundingChunks,
  415. })
  416. if groundingText != "" {
  417. _, _ = result.Write(p.startBlock(BlockTypeText, map[string]any{
  418. "type": "text",
  419. "text": "",
  420. }))
  421. _, _ = result.Write(p.emitDelta("text_delta", map[string]any{
  422. "text": groundingText,
  423. }))
  424. _, _ = result.Write(p.endBlock())
  425. }
  426. }
  427. // 确定 stop_reason
  428. stopReason := "end_turn"
  429. if p.usedTool {
  430. stopReason = "tool_use"
  431. } else if finishReason == "MAX_TOKENS" {
  432. stopReason = "max_tokens"
  433. }
  434. usage := ClaudeUsage{
  435. InputTokens: p.inputTokens,
  436. OutputTokens: p.outputTokens,
  437. CacheReadInputTokens: p.cacheReadTokens,
  438. ImageOutputTokens: p.imageOutputTokens,
  439. }
  440. var usageValue any = usage
  441. if p.usageMapHook != nil {
  442. usageMap := usageToMap(usage)
  443. p.usageMapHook(usageMap)
  444. usageValue = usageMap
  445. }
  446. deltaEvent := map[string]any{
  447. "type": "message_delta",
  448. "delta": map[string]any{
  449. "stop_reason": stopReason,
  450. "stop_sequence": nil,
  451. },
  452. "usage": usageValue,
  453. }
  454. _, _ = result.Write(p.formatSSE("message_delta", deltaEvent))
  455. if !p.messageStopSent {
  456. stopEvent := map[string]any{
  457. "type": "message_stop",
  458. }
  459. _, _ = result.Write(p.formatSSE("message_stop", stopEvent))
  460. p.messageStopSent = true
  461. }
  462. return result.Bytes()
  463. }
  464. // formatSSE 格式化 SSE 事件
  465. func (p *StreamingProcessor) formatSSE(eventType string, data any) []byte {
  466. jsonData, err := json.Marshal(data)
  467. if err != nil {
  468. return nil
  469. }
  470. return []byte(fmt.Sprintf("event: %s\ndata: %s\n\n", eventType, string(jsonData)))
  471. }