package responses import ( "ds2api/internal/toolcall" "ds2api/internal/toolstream" "encoding/json" "strings" openaifmt "ds2api/internal/format/openai" "github.com/google/uuid" ) func (s *responsesStreamRuntime) allocateOutputIndex() int { idx := s.nextOutputID s.nextOutputID++ return idx } func (s *responsesStreamRuntime) ensureMessageItemID() string { if strings.TrimSpace(s.messageItemID) != "" { return s.messageItemID } s.messageItemID = "msg_" + strings.ReplaceAll(uuid.NewString(), "-", "") return s.messageItemID } func (s *responsesStreamRuntime) ensureMessageOutputIndex() int { if s.messageOutputID >= 0 { return s.messageOutputID } s.messageOutputID = s.allocateOutputIndex() return s.messageOutputID } func (s *responsesStreamRuntime) ensureMessageItemAdded() { if s.messageAdded { return } itemID := s.ensureMessageItemID() item := map[string]any{ "id": itemID, "type": "message", "role": "assistant", "status": "in_progress", } s.sendEvent( "response.output_item.added", openaifmt.BuildResponsesOutputItemAddedPayload(s.responseID, itemID, s.ensureMessageOutputIndex(), item), ) s.messageAdded = true } func (s *responsesStreamRuntime) ensureMessageContentPartAdded() { if s.messagePartAdded { return } s.ensureMessageItemAdded() s.sendEvent( "response.content_part.added", openaifmt.BuildResponsesContentPartAddedPayload( s.responseID, s.ensureMessageItemID(), s.ensureMessageOutputIndex(), 0, map[string]any{"type": "output_text", "text": ""}, ), ) s.messagePartAdded = true } func (s *responsesStreamRuntime) emitTextDelta(content string) { if content == "" { return } s.ensureMessageContentPartAdded() s.visibleText.WriteString(content) s.sendEvent( "response.output_text.delta", openaifmt.BuildResponsesTextDeltaPayload( s.responseID, s.ensureMessageItemID(), s.ensureMessageOutputIndex(), 0, content, ), ) } func (s *responsesStreamRuntime) closeMessageItem() { if !s.messageAdded { return } itemID := s.ensureMessageItemID() outputIndex := s.ensureMessageOutputIndex() text := s.visibleText.String() if s.messagePartAdded { s.sendEvent( "response.output_text.done", openaifmt.BuildResponsesTextDonePayload( s.responseID, itemID, outputIndex, 0, text, ), ) s.sendEvent( "response.content_part.done", openaifmt.BuildResponsesContentPartDonePayload( s.responseID, itemID, outputIndex, 0, map[string]any{"type": "output_text", "text": text}, ), ) s.messagePartAdded = false } item := map[string]any{ "id": itemID, "type": "message", "role": "assistant", "status": "completed", "content": []map[string]any{ { "type": "output_text", "text": text, }, }, } s.sendEvent( "response.output_item.done", openaifmt.BuildResponsesOutputItemDonePayload(s.responseID, itemID, outputIndex, item), ) } func (s *responsesStreamRuntime) ensureFunctionItemID(callIndex int) string { if id, ok := s.functionItemIDs[callIndex]; ok && strings.TrimSpace(id) != "" { return id } id := "fc_" + strings.ReplaceAll(uuid.NewString(), "-", "") s.functionItemIDs[callIndex] = id return id } func (s *responsesStreamRuntime) ensureToolCallID(callIndex int) string { if id, ok := s.streamToolCallIDs[callIndex]; ok && strings.TrimSpace(id) != "" { return id } id := "call_" + strings.ReplaceAll(uuid.NewString(), "-", "") s.streamToolCallIDs[callIndex] = id return id } func (s *responsesStreamRuntime) resetStreamToolCallState() { s.streamToolCallIDs = map[int]string{} s.functionItemIDs = map[int]string{} s.functionOutputIDs = map[int]int{} s.functionArgs = map[int]string{} s.functionDone = map[int]bool{} s.functionAdded = map[int]bool{} s.functionNames = map[int]string{} } func (s *responsesStreamRuntime) ensureFunctionOutputIndex(callIndex int) int { if idx, ok := s.functionOutputIDs[callIndex]; ok { return idx } idx := s.allocateOutputIndex() s.functionOutputIDs[callIndex] = idx return idx } func (s *responsesStreamRuntime) ensureFunctionItemAdded(callIndex int, name string) { if strings.TrimSpace(name) != "" { s.functionNames[callIndex] = strings.TrimSpace(name) } if s.functionAdded[callIndex] { return } fnName := strings.TrimSpace(s.functionNames[callIndex]) if fnName == "" { return } outputIndex := s.ensureFunctionOutputIndex(callIndex) itemID := s.ensureFunctionItemID(callIndex) callID := s.ensureToolCallID(callIndex) item := map[string]any{ "id": itemID, "type": "function_call", "call_id": callID, "name": fnName, "arguments": "", "status": "in_progress", } s.sendEvent( "response.output_item.added", openaifmt.BuildResponsesOutputItemAddedPayload(s.responseID, itemID, outputIndex, item), ) s.functionAdded[callIndex] = true s.toolCallsEmitted = true } func (s *responsesStreamRuntime) emitFunctionCallDeltaEvents(deltas []toolstream.ToolCallDelta) { for _, d := range deltas { s.ensureFunctionItemAdded(d.Index, d.Name) if strings.TrimSpace(d.Arguments) == "" { continue } s.functionArgs[d.Index] += d.Arguments outputIndex := s.ensureFunctionOutputIndex(d.Index) itemID := s.ensureFunctionItemID(d.Index) callID := s.ensureToolCallID(d.Index) s.sendEvent( "response.function_call_arguments.delta", openaifmt.BuildResponsesFunctionCallArgumentsDeltaPayload(s.responseID, itemID, outputIndex, callID, d.Arguments), ) } } func (s *responsesStreamRuntime) emitFunctionCallDoneEvents(calls []toolcall.ParsedToolCall) { normalizedCalls := toolcall.NormalizeParsedToolCallsForSchemas(calls, s.toolsRaw) for idx, tc := range normalizedCalls { if strings.TrimSpace(tc.Name) == "" { continue } s.ensureFunctionItemAdded(idx, tc.Name) if s.functionDone[idx] { continue } outputIndex := s.ensureFunctionOutputIndex(idx) itemID := s.ensureFunctionItemID(idx) callID := s.ensureToolCallID(idx) argsBytes, _ := json.Marshal(tc.Input) args := string(argsBytes) s.functionArgs[idx] = args s.sendEvent( "response.function_call_arguments.done", openaifmt.BuildResponsesFunctionCallArgumentsDonePayload(s.responseID, itemID, outputIndex, callID, tc.Name, args), ) item := map[string]any{ "id": itemID, "type": "function_call", "call_id": callID, "name": tc.Name, "arguments": args, "status": "completed", } s.sendEvent( "response.output_item.done", openaifmt.BuildResponsesOutputItemDonePayload(s.responseID, itemID, outputIndex, item), ) s.functionDone[idx] = true s.toolCallsDoneEmitted = true } }