@@ -13,6 +13,8 @@ type OpenCodeGoClient struct {
1313 anthropic * AnthropicClient
1414}
1515
16+ type openCodeGoStreamOpener func (context.Context , []EyrieMessage , ChatOptions ) (* StreamResult , error )
17+
1618// NewOpenCodeGoClient builds a dual-protocol OpenCode Go provider client.
1719func NewOpenCodeGoClient (apiKey , baseURL string , opts ... ClientOption ) * OpenCodeGoClient {
1820 openBase := strings .TrimRight (strings .TrimSpace (baseURL ), "/" )
@@ -30,7 +32,21 @@ func (c *OpenCodeGoClient) Name() string { return "opencodego" }
3032
3133func (c * OpenCodeGoClient ) Chat (ctx context.Context , messages []EyrieMessage , opts ChatOptions ) (* EyrieResponse , error ) {
3234 if openCodeGoUsesAnthropicMessages (opts .Model ) {
33- return c .anthropic .Chat (ctx , messages , opts )
35+ resp , err := c .anthropic .Chat (ctx , messages , opts )
36+ if err != nil {
37+ return nil , err
38+ }
39+ if resp != nil && strings .TrimSpace (resp .Content ) != "" {
40+ return resp , nil
41+ }
42+ if c .openAI == nil {
43+ return resp , err
44+ }
45+ openResp , openErr := c .openAI .Chat (ctx , messages , opts )
46+ if openErr == nil && openResp != nil && strings .TrimSpace (openResp .Content ) != "" {
47+ return openResp , nil
48+ }
49+ return resp , err
3450 }
3551 resp , err := c .openAI .Chat (ctx , messages , opts )
3652 if err != nil || c .anthropic == nil || ! openCodeGoMightNeedAnthropicFallback (opts .Model ) {
@@ -48,13 +64,17 @@ func (c *OpenCodeGoClient) Chat(ctx context.Context, messages []EyrieMessage, op
4864
4965func (c * OpenCodeGoClient ) StreamChat (ctx context.Context , messages []EyrieMessage , opts ChatOptions ) (* StreamResult , error ) {
5066 if openCodeGoUsesAnthropicMessages (opts .Model ) {
51- return c .anthropic .StreamChat (ctx , messages , opts )
67+ primary , err := c .anthropic .StreamChat (ctx , messages , opts )
68+ if err != nil || c .openAI == nil {
69+ return primary , err
70+ }
71+ return newOpenCodeGoStreamWithFallback (ctx , messages , opts , primary , c .openAI .StreamChat ), nil
5272 }
5373 primary , err := c .openAI .StreamChat (ctx , messages , opts )
5474 if err != nil || c .anthropic == nil || ! openCodeGoMightNeedAnthropicFallback (opts .Model ) {
5575 return primary , err
5676 }
57- return newOpenCodeGoFallbackStream (ctx , c . anthropic , messages , opts , primary ), nil
77+ return newOpenCodeGoStreamWithFallback (ctx , messages , opts , primary , c . anthropic . StreamChat ), nil
5878}
5979
6080func (c * OpenCodeGoClient ) Ping (ctx context.Context ) error {
@@ -64,9 +84,10 @@ func (c *OpenCodeGoClient) Ping(ctx context.Context) error {
6484 return c .anthropic .Ping (ctx )
6585}
6686
67- // newOpenCodeGoFallbackStream watches an OpenAI-format stream; if it ends with
68- // reasoning tokens but no answer, transparently retries via /v1/messages.
69- func newOpenCodeGoFallbackStream (ctx context.Context , anthropic * AnthropicClient , messages []EyrieMessage , opts ChatOptions , primary * StreamResult ) * StreamResult {
87+ // newOpenCodeGoStreamWithFallback watches a primary stream; if it ends with
88+ // reasoning tokens but no answer, transparently retries via the fallback opener.
89+ // Used for OpenAI→Anthropic and Anthropic→OpenAI on MiniMax/Qwen models.
90+ func newOpenCodeGoStreamWithFallback (ctx context.Context , messages []EyrieMessage , opts ChatOptions , primary * StreamResult , fallback openCodeGoStreamOpener ) * StreamResult {
7091 out := make (chan EyrieStreamEvent , streamChannelBuffer )
7192 cancelCtx , cancel := context .WithCancel (ctx )
7293 go func () {
@@ -126,7 +147,7 @@ func newOpenCodeGoFallbackStream(ctx context.Context, anthropic *AnthropicClient
126147 return
127148 }
128149
129- fallback , err := anthropic . StreamChat (cancelCtx , messages , opts )
150+ fallbackResult , err := fallback (cancelCtx , messages , opts )
130151 if err != nil {
131152 flush ()
132153 select {
@@ -135,8 +156,8 @@ func newOpenCodeGoFallbackStream(ctx context.Context, anthropic *AnthropicClient
135156 }
136157 return
137158 }
138- defer fallback .Close ()
139- for ev := range fallback .Events {
159+ defer fallbackResult .Close ()
160+ for ev := range fallbackResult .Events {
140161 select {
141162 case out <- ev :
142163 case <- cancelCtx .Done ():
0 commit comments