@@ -292,37 +292,73 @@ func (f *Factory) aggregateResponses(responses []ModelResponse, strategy Strateg
292292 }
293293}
294294
295- // aggregateByVoting implements majority voting
295+ // aggregateByVoting implements majority voting by comparing message content
296296func (f * Factory ) aggregateByVoting (responses []ModelResponse , metadata * Metadata ) ([]byte , Metadata , error ) {
297- // Count occurrences of each response
298- // This is a simplified implementation - in production, you'd parse the actual content
299- responseCounts := make (map [string ]int )
300- responseMap := make (map [string ][]byte )
297+ // Parse responses and extract message content for voting
298+ type parsedResponse struct {
299+ content string
300+ rawBytes []byte
301+ }
302+
303+ contentCounts := make (map [string ]int )
304+ contentToResponse := make (map [string ]parsedResponse )
301305
302306 for _ , resp := range responses {
303- key := string (resp .Response )
304- responseCounts [key ]++
305- responseMap [key ] = resp .Response
307+ // Try to parse OpenAI-style response
308+ var openAIResp map [string ]interface {}
309+ if err := json .Unmarshal (resp .Response , & openAIResp ); err != nil {
310+ // If parsing fails, use first response as fallback
311+ logging .Warnf ("Failed to parse response for voting: %v" , err )
312+ continue
313+ }
314+
315+ // Extract content from choices array
316+ content := extractContentFromResponse (openAIResp )
317+ if content != "" {
318+ contentCounts [content ]++
319+ contentToResponse [content ] = parsedResponse {
320+ content : content ,
321+ rawBytes : resp .Response ,
322+ }
323+ }
306324 }
307325
308- // Find the most common response
326+ // Find the most common content
309327 var maxCount int
310- var selectedResponse [] byte
311- for key , count := range responseCounts {
328+ var selectedContent string
329+ for content , count := range contentCounts {
312330 if count > maxCount {
313331 maxCount = count
314- selectedResponse = responseMap [ key ]
332+ selectedContent = content
315333 }
316334 }
317335
318- metadata .AggregationDetails ["votes " ] = responseCounts
336+ metadata .AggregationDetails ["vote_counts " ] = contentCounts
319337 metadata .AggregationDetails ["max_votes" ] = maxCount
320338
321- if selectedResponse == nil {
322- return responses [0 ].Response , * metadata , nil
339+ // Return the response with the most votes, or first response if no clear winner
340+ if selectedContent != "" {
341+ if selected , ok := contentToResponse [selectedContent ]; ok {
342+ return selected .rawBytes , * metadata , nil
343+ }
323344 }
324345
325- return selectedResponse , * metadata , nil
346+ return responses [0 ].Response , * metadata , nil
347+ }
348+
349+ // extractContentFromResponse extracts the message content from an OpenAI-style response
350+ func extractContentFromResponse (resp map [string ]interface {}) string {
351+ // Navigate: response["choices"][0]["message"]["content"]
352+ if choices , ok := resp ["choices" ].([]interface {}); ok && len (choices ) > 0 {
353+ if choice , ok := choices [0 ].(map [string ]interface {}); ok {
354+ if message , ok := choice ["message" ].(map [string ]interface {}); ok {
355+ if content , ok := message ["content" ].(string ); ok {
356+ return content
357+ }
358+ }
359+ }
360+ }
361+ return ""
326362}
327363
328364// aggregateByWeighted implements confidence-weighted selection
@@ -352,13 +388,67 @@ func (f *Factory) aggregateByWeighted(responses []ModelResponse, metadata *Metad
352388 return selectedResponse , * metadata , nil
353389}
354390
355- // aggregateByScoreAveraging implements score averaging (simplified)
391+ // aggregateByScoreAveraging averages logprobs or confidence scores from multiple models
356392func (f * Factory ) aggregateByScoreAveraging (responses []ModelResponse , metadata * Metadata ) ([]byte , Metadata , error ) {
357- // This is a simplified implementation
358- // In production, you'd parse the responses and average numerical scores
359- // For now, return the first response as a placeholder
360- metadata .SelectedModel = responses [0 ].ModelName
361- metadata .AggregationDetails ["note" ] = "score averaging not fully implemented"
362-
363- return responses [0 ].Response , * metadata , nil
393+ // For score averaging, we select the response with the median confidence/latency balance
394+ // This is more practical than trying to merge responses
395+
396+ type scoredResponse struct {
397+ response ModelResponse
398+ score float64
399+ }
400+
401+ scored := make ([]scoredResponse , 0 , len (responses ))
402+
403+ for _ , resp := range responses {
404+ // Compute a composite score based on confidence and latency
405+ // Higher confidence is better, lower latency is better
406+ score := resp .Confidence
407+ if resp .Latency .Seconds () > 0 {
408+ // Normalize latency (penalize slow responses)
409+ latencyPenalty := 1.0 / (1.0 + resp .Latency .Seconds ())
410+ score = score * latencyPenalty
411+ }
412+
413+ scored = append (scored , scoredResponse {
414+ response : resp ,
415+ score : score ,
416+ })
417+ }
418+
419+ // If no confidence scores available, fall back to selecting by fastest response
420+ allZeroConfidence := true
421+ for _ , s := range scored {
422+ if s .score > 0 {
423+ allZeroConfidence = false
424+ break
425+ }
426+ }
427+
428+ if allZeroConfidence {
429+ // Select fastest response
430+ fastest := scored [0 ]
431+ for _ , s := range scored [1 :] {
432+ if s .response .Latency < fastest .response .Latency {
433+ fastest = s
434+ }
435+ }
436+ metadata .SelectedModel = fastest .response .ModelName
437+ metadata .AggregationDetails ["selection_method" ] = "fastest_response"
438+ return fastest .response .Response , * metadata , nil
439+ }
440+
441+ // Find highest scoring response
442+ best := scored [0 ]
443+ for _ , s := range scored [1 :] {
444+ if s .score > best .score {
445+ best = s
446+ }
447+ }
448+
449+ metadata .SelectedModel = best .response .ModelName
450+ metadata .AggregationDetails ["best_score" ] = best .score
451+ metadata .AggregationDetails ["selection_method" ] = "score_based"
452+
453+ return best .response .Response , * metadata , nil
364454}
0 commit comments