55 "context"
66 "encoding/json"
77 "fmt"
8+ "io"
89 "net/http"
910
1011 "github.com/tigillo/githubmodels-go/models"
@@ -66,7 +67,7 @@ func (c *Client) ChatCompletion(ctx context.Context, reqData models.ChatRequest)
6667
6768 bodyBytes , err := json .Marshal (reqData )
6869 if err != nil {
69- return nil , err
70+ return nil , fmt . Errorf ( "marshal error: %w" , err )
7071 }
7172
7273 req , err := http .NewRequestWithContext (ctx , "POST" , url , bytes .NewReader (bodyBytes ))
@@ -75,7 +76,7 @@ func (c *Client) ChatCompletion(ctx context.Context, reqData models.ChatRequest)
7576 }
7677
7778 req .Header .Set ("Authorization" , "Bearer " + c .token )
78- req .Header .Set ("Accept" , "application/vnd.github+ json" )
79+ req .Header .Set ("Accept" , "application/json" )
7980 req .Header .Set ("Content-Type" , "application/json" )
8081
8182 resp , err := c .Client .Do (req )
@@ -84,14 +85,58 @@ func (c *Client) ChatCompletion(ctx context.Context, reqData models.ChatRequest)
8485 }
8586 defer resp .Body .Close ()
8687
87- if resp .StatusCode != http .StatusOK {
88- return nil , fmt .Errorf ("unexpected status code: %d" , resp .StatusCode )
88+ body , _ := io .ReadAll (resp .Body )
89+
90+ // Parse rate limit headers (do this before checking status so we have them on errors too)
91+ rateLimit := parseRateLimitHeaders (resp .Header )
92+
93+ if resp .StatusCode < 200 || resp .StatusCode >= 300 {
94+ // Create a partial response with rate limit info for error cases
95+ errorResp := & models.ChatResponse {
96+ RateLimit : rateLimit ,
97+ }
98+ // Return the partial response so caller can access rate limit info
99+ // Note: This changes the signature behavior slightly - we return a response even on error
100+ return errorResp , fmt .Errorf (
101+ "unexpected status code: %d, response body: %s" ,
102+ resp .StatusCode ,
103+ string (body ),
104+ )
89105 }
90106
91107 var chatResp models.ChatResponse
92- if err := json .NewDecoder (resp .Body ).Decode (& chatResp ); err != nil {
93- return nil , err
108+ if err := json .Unmarshal (body , & chatResp ); err != nil {
109+ return nil , fmt .Errorf (
110+ "failed to decode success response: %w (body: %s)" ,
111+ err , string (body ),
112+ )
94113 }
95114
115+ // Attach rate limit info to response
116+ chatResp .RateLimit = rateLimit
117+
96118 return & chatResp , nil
97119}
120+
121+ // parseRateLimitHeaders extracts rate limit information from HTTP headers
122+ func parseRateLimitHeaders (headers http.Header ) models.RateLimitInfo {
123+ info := models.RateLimitInfo {}
124+
125+ if limit := headers .Get ("X-RateLimit-Limit" ); limit != "" {
126+ fmt .Sscanf (limit , "%d" , & info .Limit )
127+ }
128+
129+ if remaining := headers .Get ("X-RateLimit-Remaining" ); remaining != "" {
130+ fmt .Sscanf (remaining , "%d" , & info .Remaining )
131+ }
132+
133+ if reset := headers .Get ("X-RateLimit-Reset" ); reset != "" {
134+ fmt .Sscanf (reset , "%d" , & info .Reset )
135+ }
136+
137+ if retryAfter := headers .Get ("Retry-After" ); retryAfter != "" {
138+ fmt .Sscanf (retryAfter , "%d" , & info .RetryAfter )
139+ }
140+
141+ return info
142+ }
0 commit comments