11import type { Settings } from './index.ts'
22
3- export interface TicketPickerItem {
3+ export interface SearchResults {
4+ subject : string
5+ tickets : Ticket [ ]
6+ }
7+
8+ export interface SearchResponse {
9+ results : SearchResult [ ]
10+ }
11+
12+ interface SearchResult {
413 id : number
514 subject : string
6- url : string
15+ created_at : string
716}
817
918export interface Ticket {
1019 id : number
11- url : string
1220 subject : string
13- description : string
14- tags : string [ ]
15- status : string
16- priority : string
1721 created_at : string
18- updated_at : string
1922 comments : TicketComment [ ]
23+ summary ?: string
24+ summaries : Summary [ ]
25+ }
26+
27+ export interface Summary {
28+ id : number
29+ summary ?: string
30+ subject : string
2031}
2132
2233export interface TicketComment {
2334 id : number
24- type : string
2535 author_id : number
26- body : string
27- html_body : string
2836 plain_body : string
29- public : boolean
3037 created_at : string
3138}
3239
40+ export interface ChatCompletionRequest {
41+ messages : Array < { role : string ; content : string } >
42+ model : string
43+ max_tokens : number
44+ stream : boolean
45+ }
46+
47+ export interface ChatCompletionResponse {
48+ message : string
49+ choices : Array < {
50+ message : {
51+ content : string
52+ }
53+ } >
54+ }
55+
3356const authHeaders = ( settings : Settings ) => ( {
3457 Authorization : `Basic ${ Buffer . from ( `${ settings . email } /token:${ settings . apiToken } ` ) . toString ( 'base64' ) } ` ,
3558} )
3659
60+ const sgTokenHeaders = ( settings : Settings ) => ( {
61+ Authorization : `token ${ settings . sgToken } ` ,
62+ } )
63+
3764const buildUrl = ( settings : Settings , path : string , searchParams : Record < string , string > = { } ) => {
3865 const url = new URL ( `https://${ settings . subdomain } .zendesk.com/api/v2${ path } ` )
3966 url . search = new URLSearchParams ( searchParams ) . toString ( )
4067 return url
4168}
4269
70+ /**
71+ * Searches Zendesk tickets based on a query string and returns matching tickets with their details.
72+ * @param query - The search query string to filter tickets
73+ * @param settings - Zendesk API settings including subdomain, email, and API token
74+ * @returns Promise resolving to SearchResults containing matched tickets and their subjects
75+ */
76+
4377export const searchTickets = async (
4478 query : string | undefined ,
4579 settings : Settings ,
46- ) : Promise < TicketPickerItem [ ] > => {
80+ ) : Promise < SearchResults > => {
4781 const searchResponse = await fetch (
4882 buildUrl ( settings , '/search.json' , {
49- query : `type:ticket ${ query || '' } ` ,
83+ // Add search parameters here
84+ query : `${ query } order_by:created sort:desc` || '' ,
5085 } ) ,
5186 {
5287 method : 'GET' ,
5388 headers : authHeaders ( settings ) ,
54- } ,
89+ }
5590 )
5691 if ( ! searchResponse . ok ) {
5792 throw new Error (
58- `Error searching Zendesk tickets (${ searchResponse . status } ${
59- searchResponse . statusText
93+ `Error searching Zendesk tickets (${ searchResponse . status } ${ searchResponse . statusText
6094 } ): ${ await searchResponse . text ( ) } `,
6195 )
6296 }
6397
64- const searchJSON = ( await searchResponse . json ( ) ) as {
65- results : {
66- id : number
67- subject : string
68- url : string
69- } [ ]
98+ const result = ( await searchResponse . json ( ) ) as SearchResponse
99+
100+ const searchResults : SearchResults = {
101+ subject : '' ,
102+ tickets : [ ]
70103 }
71104
72- return searchJSON . results . map ( ticket => ( {
73- id : ticket . id ,
74- subject : ticket . subject ,
75- url : ticket . url ,
76- } ) )
105+ for ( const item of result . results ) {
106+ searchResults . subject += `${ item . id . toString ( ) } ${ item . subject } , `
107+ searchResults . tickets . push ( {
108+ id : item . id ,
109+ subject : item . subject ,
110+ created_at : item . created_at ,
111+ comments : [ ] ,
112+ summaries : [ ]
113+ } )
114+ // This is to limit the number of tickets.
115+ if ( searchResults . tickets . length === 5 ) {
116+ break
117+ }
118+ }
119+ return searchResults
77120}
78121
79- export const fetchTicket = async ( ticketId : number , settings : Settings ) : Promise < Ticket | null > => {
80- const ticketResponse = await fetch (
81- buildUrl ( settings , `/tickets/${ ticketId } .json` ) ,
122+ /**
123+ * Fetches comments for a given Zendesk ticket
124+ * @param ticket - The ticket object to fetch comments for
125+ * @param settings - Zendesk API settings including subdomain, email, and API token
126+ * @returns Promise resolving to Ticket object with comments field populated
127+ */
128+ export const fetchComments = async ( ticket : Ticket , settings : Settings ) : Promise < Ticket > => {
129+ const commentsResponse = await fetch (
130+ buildUrl ( settings , `/tickets/${ ticket . id } /comments.json` ) ,
82131 {
83132 method : 'GET' ,
84133 headers : authHeaders ( settings ) ,
85134 }
86135 )
87- if ( ! ticketResponse . ok ) {
136+ if ( ! commentsResponse . ok ) {
88137 throw new Error (
89- `Error fetching Zendesk ticket (${ ticketResponse . status } ${
90- ticketResponse . statusText
91- } ): ${ await ticketResponse . text ( ) } `
138+ `Error fetching Zendesk ticket comments (${ commentsResponse . status } ${ commentsResponse . statusText
139+ } ): ${ await commentsResponse . text ( ) } `
92140 )
93141 }
94142
95- const responseJSON = ( await ticketResponse . json ( ) ) as { ticket : Ticket }
96- const ticket = responseJSON . ticket
143+ const commentsJSON = ( await commentsResponse . json ( ) as { comments : TicketComment [ ] } ) . comments
144+ // Extract only necessary fields from comments
145+ const comments : TicketComment [ ] = commentsJSON . map ( comment => ( {
146+ id : comment . id ,
147+ author_id : comment . author_id ,
148+ plain_body : comment . plain_body ,
149+ created_at : comment . created_at ,
150+ } ) )
151+
152+ return { ...ticket , comments }
153+ }
154+
155+ /**
156+ * Fetches a chat completion from the Sourcegraph API to generate a summary for a Zendesk ticket
157+ * @param settings - Sourcegraph API settings
158+ * @param ticket - The ticket object to generate a summary for
159+ * @returns Promise resolving to Ticket object with summary field populated
160+ */
161+ export const fetchChatCompletion = async (
162+ settings : Settings ,
163+ ticket : Ticket
164+ ) : Promise < Ticket > => {
97165
98- if ( ! ticket ) {
99- return null
166+ const formatComments = ( comments : TicketComment [ ] ) : string => {
167+ return comments . map ( comment => {
168+ return `Comment ID: ${ comment . id } \nAuthor ID: ${ comment . author_id } \nContent: ${ comment . plain_body } \nCreated At: ${ comment . created_at } \n`
169+ } ) . join ( '\n' )
100170 }
101171
102- // Fetch comments for the ticket
103- const commentsResponse = await fetch (
104- buildUrl ( settings , `/tickets/${ ticketId } /comments.json` ) ,
105- {
106- method : 'GET' ,
107- headers : authHeaders ( settings ) ,
108- }
109- )
110- if ( ! commentsResponse . ok ) {
111- throw new Error (
112- `Error fetching Zendesk ticket comments (${ commentsResponse . status } ${
113- commentsResponse . statusText
114- } ): ${ await commentsResponse . text ( ) } `
115- )
172+ const ticketContent = `
173+ Ticket ID: ${ ticket . id }
174+ Subject: ${ ticket . subject || 'N/A' }
175+ Created At: ${ ticket . created_at || 'N/A' }
176+ Comments: ${ formatComments ( ticket . comments ) }
177+ `
178+ const requestData : ChatCompletionRequest = {
179+ messages : [
180+ {
181+ role : 'user' ,
182+ content : `${ settings . prompt } ${ ticketContent } `
183+ }
184+ ] ,
185+ model : `${ settings . model } ` ,
186+ max_tokens : 1000 ,
187+ stream : false
116188 }
117189
118- const commentsJSON = ( await commentsResponse . json ( ) ) as { comments : TicketComment [ ] }
119- ticket . comments = commentsJSON . comments
190+ const response = await fetch ( `${ settings . sgDomain } ` , {
191+ method : 'POST' ,
192+ headers : {
193+ 'Accept' : 'application/json' ,
194+ 'Content-Type' : 'application/json' ,
195+ 'X-Requested-With' : 'cody-api v1' ,
196+ ...sgTokenHeaders ( settings ) ,
197+ } ,
198+ body : JSON . stringify ( requestData ) ,
199+ } )
200+
201+ if ( ! response . ok ) {
202+ throw new Error ( `Error fetching chat completion (${ response . status } ${ response . statusText } ): ${ await response . text ( ) } ` )
203+ }
120204
121- return ticket
205+ const summary = ( await response . json ( ) as ChatCompletionResponse ) . choices [ 0 ] . message . content
206+
207+ return { ...ticket , summary }
122208}
209+
210+
211+ export const fetchSummary = async ( ticket : Ticket , settings : Settings ) : Promise < Ticket > => {
212+ const ticketWithComments = await fetchComments ( ticket , settings )
213+ const ticketWithSummary = await fetchChatCompletion ( settings , ticketWithComments )
214+ // Return the ticket with the summary
215+ return ticketWithSummary
216+ }
0 commit comments