@@ -109,17 +109,22 @@ pub struct ResponseMessage {
109109 pub tool_calls : Option < Vec < ToolCall > > ,
110110 pub refusal : Option < String > ,
111111 pub reasoning_details : Option < Vec < ReasoningDetail > > ,
112+ // GitHub Copilot format (flat fields instead of array)
113+ pub reasoning_text : Option < String > ,
114+ pub reasoning_opaque : Option < String > ,
112115}
113116
114- impl From < ReasoningDetail > for forge_domain:: ReasoningFull {
117+ impl From < ReasoningDetail > for forge_domain:: ReasoningDetail {
115118 fn from ( detail : ReasoningDetail ) -> Self {
116- forge_domain:: ReasoningFull { text : detail. text , signature : detail. signature }
117- }
118- }
119-
120- impl From < ReasoningDetail > for forge_domain:: ReasoningPart {
121- fn from ( detail : ReasoningDetail ) -> Self {
122- forge_domain:: ReasoningPart { text : detail. text , signature : detail. signature }
119+ forge_domain:: ReasoningDetail {
120+ text : detail. text ,
121+ signature : detail. signature ,
122+ data : detail. data ,
123+ id : detail. id ,
124+ format : detail. format ,
125+ index : detail. index ,
126+ type_of : Some ( detail. r#type ) ,
127+ }
123128 }
124129}
125130
@@ -164,6 +169,52 @@ impl From<ResponseUsage> for Usage {
164169 }
165170}
166171
172+ /// Intermediate representation of GitHub Copilot reasoning fields
173+ struct GitHubCopilotReasoning {
174+ text : Option < String > ,
175+ data : Option < String > ,
176+ r#type : Option < String > ,
177+ }
178+
179+ impl GitHubCopilotReasoning {
180+ fn into_reasoning_detail ( self ) -> forge_domain:: ReasoningDetail {
181+ forge_domain:: ReasoningDetail {
182+ text : self . text ,
183+ data : self . data ,
184+ type_of : self . r#type ,
185+ ..Default :: default ( )
186+ }
187+ }
188+ }
189+
190+ /// Converts GitHub Copilot flat reasoning fields to structured reasoning
191+ /// details
192+ fn convert_github_copilot_reasoning (
193+ reasoning_text : & Option < String > ,
194+ reasoning_opaque : & Option < String > ,
195+ ) -> Option < Vec < GitHubCopilotReasoning > > {
196+ if reasoning_text. is_some ( ) || reasoning_opaque. is_some ( ) {
197+ let mut details = Vec :: new ( ) ;
198+ if let Some ( text) = reasoning_text {
199+ details. push ( GitHubCopilotReasoning {
200+ text : Some ( text. clone ( ) ) ,
201+ data : None ,
202+ r#type : Some ( "reasoning.text" . to_string ( ) ) ,
203+ } ) ;
204+ }
205+ if let Some ( opaque) = reasoning_opaque {
206+ details. push ( GitHubCopilotReasoning {
207+ text : None ,
208+ data : Some ( opaque. clone ( ) ) ,
209+ r#type : Some ( "reasoning.encrypted" . to_string ( ) ) ,
210+ } ) ;
211+ }
212+ Some ( details)
213+ } else {
214+ None
215+ }
216+ }
217+
167218impl TryFrom < Response > for ChatCompletionMessage {
168219 type Error = anyhow:: Error ;
169220
@@ -214,6 +265,16 @@ impl TryFrom<Response> for ChatCompletionMessage {
214265 resp = resp. add_reasoning_detail ( forge_domain:: Reasoning :: Full (
215266 converted_details,
216267 ) ) ;
268+ } else if let Some ( details) = convert_github_copilot_reasoning (
269+ & message. reasoning_text ,
270+ & message. reasoning_opaque ,
271+ ) {
272+ resp = resp. add_reasoning_detail ( forge_domain:: Reasoning :: Full (
273+ details
274+ . into_iter ( )
275+ . map ( GitHubCopilotReasoning :: into_reasoning_detail)
276+ . collect ( ) ,
277+ ) ) ;
217278 }
218279
219280 if let Some ( tool_calls) = & message. tool_calls {
@@ -257,6 +318,16 @@ impl TryFrom<Response> for ChatCompletionMessage {
257318 resp = resp. add_reasoning_detail ( forge_domain:: Reasoning :: Part (
258319 converted_details,
259320 ) ) ;
321+ } else if let Some ( details) = convert_github_copilot_reasoning (
322+ & delta. reasoning_text ,
323+ & delta. reasoning_opaque ,
324+ ) {
325+ resp = resp. add_reasoning_detail ( forge_domain:: Reasoning :: Part (
326+ details
327+ . into_iter ( )
328+ . map ( GitHubCopilotReasoning :: into_reasoning_detail)
329+ . collect ( ) ,
330+ ) ) ;
260331 }
261332
262333 if let Some ( tool_calls) = & delta. tool_calls {
@@ -404,6 +475,8 @@ mod tests {
404475 tool_calls: None ,
405476 refusal: None ,
406477 reasoning_details: None ,
478+ reasoning_text: None ,
479+ reasoning_opaque: None ,
407480 } ,
408481 error: Some ( error_response. clone( ) ) ,
409482 } ] ,
@@ -437,6 +510,8 @@ mod tests {
437510 tool_calls: None ,
438511 refusal: None ,
439512 reasoning_details: None ,
513+ reasoning_text: None ,
514+ reasoning_opaque: None ,
440515 } ,
441516 error: Some ( error_response. clone( ) ) ,
442517 } ] ,
@@ -470,6 +545,8 @@ mod tests {
470545 tool_calls: None ,
471546 refusal: None ,
472547 reasoning_details: None ,
548+ reasoning_text: None ,
549+ reasoning_opaque: None ,
473550 } ,
474551 error: None ,
475552 } ] ,
0 commit comments