Compare commits

...

8 Commits

Author SHA1 Message Date
github-actions[bot]
f33d27f836 chore(release): Update version to v1.4.280 2025-08-10 12:34:52 +00:00
Kayvan Sylvan
1694324261 Merge pull request #1686 from ksylvan/0810-fix-openai-streaming-bug
Prevent duplicate text output in OpenAI streaming responses
2025-08-10 05:32:17 -07:00
Changelog Bot
3a3f5c50a8 chore: incoming 1686 changelog entry 2025-08-10 05:27:29 -07:00
Kayvan Sylvan
b1abfd71c2 fix: prevent duplicate text output in OpenAI streaming responses
## CHANGES

- Skip processing of ResponseOutputTextDone events
- Prevent doubled text in stream output
- Add clarifying comment about API behavior
- Maintain delta chunk streaming functionality
- Fix duplicate content issue in responses
2025-08-10 05:24:30 -07:00
github-actions[bot]
f5b7279225 chore(release): Update version to v1.4.279 2025-08-10 12:13:45 +00:00
Kayvan Sylvan
b974e1bfd5 Merge pull request #1685 from ksylvan/0810-fix-gemini-roles-in-sessions
Fix Gemini Role Mapping for API Compatibility
2025-08-10 05:11:16 -07:00
Changelog Bot
8dda68b3b9 chore: incoming 1685 changelog entry 2025-08-10 05:07:06 -07:00
Kayvan Sylvan
33c24e0cb2 fix(gemini): map chat roles to Gemini user/model in convertMessages
CHANGES
- Map assistant role to model per Gemini constraints
- Map system, developer, function, tool roles to user
- Default unrecognized roles to user to preserve instruction context
- Add unit test validating convertMessages role mapping logic
- Import chat package in tests for role constants
2025-08-10 04:55:33 -07:00
7 changed files with 66 additions and 3 deletions

View File

@@ -1,5 +1,25 @@
# Changelog
## v1.4.280 (2025-08-10)
### PR [#1686](https://github.com/danielmiessler/Fabric/pull/1686) by [ksylvan](https://github.com/ksylvan): Prevent duplicate text output in OpenAI streaming responses
- Fix: prevent duplicate text output in OpenAI streaming responses
- Skip processing of ResponseOutputTextDone events
- Prevent doubled text in stream output
- Add clarifying comment about API behavior
- Maintain delta chunk streaming functionality
## v1.4.279 (2025-08-10)
### PR [#1685](https://github.com/danielmiessler/Fabric/pull/1685) by [ksylvan](https://github.com/ksylvan): Fix Gemini Role Mapping for API Compatibility
- Fix Gemini role mapping to ensure proper API compatibility by converting chat roles to Gemini's user/model format
- Map assistant role to model role per Gemini API constraints
- Map system, developer, function, and tool roles to user role for proper handling
- Default unrecognized roles to user role to preserve instruction context
- Add comprehensive unit tests to validate convertMessages role mapping logic
## v1.4.278 (2025-08-09)
### PR [#1681](https://github.com/danielmiessler/Fabric/pull/1681) by [ksylvan](https://github.com/ksylvan): Enhance YouTube Support with Custom yt-dlp Arguments

View File

@@ -1,3 +1,3 @@
package main
var version = "v1.4.278"
var version = "v1.4.280"

Binary file not shown.

View File

@@ -336,6 +336,19 @@ func (o *Client) convertMessages(msgs []*chat.ChatCompletionMessage) []*genai.Co
for _, msg := range msgs {
content := &genai.Content{Parts: []*genai.Part{}}
switch msg.Role {
case chat.ChatMessageRoleAssistant:
content.Role = "model"
case chat.ChatMessageRoleUser:
content.Role = "user"
case chat.ChatMessageRoleSystem, chat.ChatMessageRoleDeveloper, chat.ChatMessageRoleFunction, chat.ChatMessageRoleTool:
// Gemini's API only accepts "user" and "model" roles.
// Map all other roles to "user" to preserve instruction context.
content.Role = "user"
default:
content.Role = "user"
}
if msg.Content != "" {
content.Parts = append(content.Parts, &genai.Part{Text: msg.Content})
}

View File

@@ -4,6 +4,8 @@ import (
"testing"
"google.golang.org/genai"
"github.com/danielmiessler/fabric/internal/chat"
)
// Test buildModelNameFull method
@@ -51,6 +53,30 @@ func TestExtractTextFromResponse(t *testing.T) {
}
}
// Test convertMessages handles role mapping correctly
func TestConvertMessagesRoles(t *testing.T) {
client := &Client{}
msgs := []*chat.ChatCompletionMessage{
{Role: chat.ChatMessageRoleUser, Content: "user"},
{Role: chat.ChatMessageRoleAssistant, Content: "assistant"},
{Role: chat.ChatMessageRoleSystem, Content: "system"},
}
contents := client.convertMessages(msgs)
expected := []string{"user", "model", "user"}
if len(contents) != len(expected) {
t.Fatalf("expected %d contents, got %d", len(expected), len(contents))
}
for i, c := range contents {
if c.Role != expected[i] {
t.Errorf("content %d expected role %s, got %s", i, expected[i], c.Role)
}
}
}
// Test isTTSModel method
func TestIsTTSModel(t *testing.T) {
client := &Client{}

View File

@@ -115,7 +115,11 @@ func (o *Client) sendStreamResponses(
case string(constant.ResponseOutputTextDelta("").Default()):
channel <- event.AsResponseOutputTextDelta().Delta
case string(constant.ResponseOutputTextDone("").Default()):
channel <- event.AsResponseOutputTextDone().Text
// The Responses API sends the full text again in the
// final "done" event. Since we've already streamed all
// delta chunks above, sending it would duplicate the
// output. Ignore it here to prevent doubled results.
continue
}
}
if stream.Err() == nil {

View File

@@ -1 +1 @@
"1.4.278"
"1.4.280"