@@ -9,26 +9,47 @@ interface CodebuffWebApiEnv {
99 ciEnv : CiEnv
1010}
1111
12- export async function callWebSearchAPI ( params : {
13- query : string
14- depth ?: 'standard' | 'deep'
15- repoUrl ?: string | null
12+ const tryParseJson = ( text : string ) : unknown => {
13+ try {
14+ return JSON . parse ( text )
15+ } catch {
16+ return null
17+ }
18+ }
19+
20+ const getStringField = ( value : unknown , key : string ) : string | undefined => {
21+ if ( ! value || typeof value !== 'object' ) return undefined
22+ const record = value as Record < string , unknown >
23+ const field = record [ key ]
24+ return typeof field === 'string' ? field : undefined
25+ }
26+
27+ const getNumberField = ( value : unknown , key : string ) : number | undefined => {
28+ if ( ! value || typeof value !== 'object' ) return undefined
29+ const record = value as Record < string , unknown >
30+ const field = record [ key ]
31+ return typeof field === 'number' ? field : undefined
32+ }
33+
34+ const callCodebuffV1 = async ( params : {
35+ endpoint : '/api/v1/web-search' | '/api/v1/docs-search'
36+ payload : unknown
1637 fetch : typeof globalThis . fetch
1738 logger : Logger
1839 env : CodebuffWebApiEnv
1940 baseUrl ?: string
2041 apiKey ?: string
21- } ) : Promise < { result ?: string ; error ?: string ; creditsUsed ?: number } > {
22- const { query, depth = 'standard' , repoUrl, fetch, logger, env } = params
42+ requestName : 'web-search' | 'docs-search'
43+ } ) : Promise < { json ?: unknown ; error ?: string ; creditsUsed ?: number } > => {
44+ const { endpoint, payload, fetch, logger, env, requestName } = params
2345 const baseUrl = params . baseUrl ?? env . clientEnv . NEXT_PUBLIC_CODEBUFF_APP_URL
2446 const apiKey = params . apiKey ?? env . ciEnv . CODEBUFF_API_KEY
2547
2648 if ( ! baseUrl || ! apiKey ) {
2749 return { error : 'Missing Codebuff base URL or API key' }
2850 }
2951
30- const url = `${ baseUrl } /api/v1/web-search`
31- const payload = { query, depth, ...( repoUrl ? { repoUrl } : { } ) }
52+ const url = `${ baseUrl } ${ endpoint } `
3253
3354 try {
3455 const res = await withTimeout (
@@ -45,40 +66,27 @@ export async function callWebSearchAPI(params: {
4566 )
4667
4768 const text = await res . text ( )
48- const tryJson = ( ) => {
49- try {
50- return JSON . parse ( text )
51- } catch {
52- return null
53- }
54- }
69+ const json = tryParseJson ( text )
5570
5671 if ( ! res . ok ) {
57- const maybe = tryJson ( )
5872 const err =
59- ( maybe && ( maybe . error || maybe . message ) ) || text || 'Request failed'
73+ getStringField ( json , 'error' ) ??
74+ getStringField ( json , 'message' ) ??
75+ text ??
76+ 'Request failed'
6077 logger . warn (
6178 {
6279 url,
6380 status : res . status ,
6481 statusText : res . statusText ,
6582 body : text ?. slice ( 0 , 500 ) ,
6683 } ,
67- ' Web API web-search request failed' ,
84+ ` Web API ${ requestName } request failed` ,
6885 )
69- return { error : typeof err === 'string' ? err : 'Unknown error' }
86+ return { error : err }
7087 }
7188
72- const data = tryJson ( )
73- if ( data && typeof data . result === 'string' ) {
74- return {
75- result : data . result ,
76- creditsUsed :
77- typeof data . creditsUsed === 'number' ? data . creditsUsed : undefined ,
78- }
79- }
80- if ( data && typeof data . error === 'string' ) return { error : data . error }
81- return { error : 'Invalid response format' }
89+ return { json, creditsUsed : getNumberField ( json , 'creditsUsed' ) }
8290 } catch ( error ) {
8391 logger . error (
8492 {
@@ -87,12 +95,46 @@ export async function callWebSearchAPI(params: {
8795 ? { name : error . name , message : error . message , stack : error . stack }
8896 : error ,
8997 } ,
90- ' Web API web-search network error' ,
98+ ` Web API ${ requestName } network error` ,
9199 )
92100 return { error : error instanceof Error ? error . message : 'Network error' }
93101 }
94102}
95103
104+ export async function callWebSearchAPI ( params : {
105+ query : string
106+ depth ?: 'standard' | 'deep'
107+ repoUrl ?: string | null
108+ fetch : typeof globalThis . fetch
109+ logger : Logger
110+ env : CodebuffWebApiEnv
111+ baseUrl ?: string
112+ apiKey ?: string
113+ } ) : Promise < { result ?: string ; error ?: string ; creditsUsed ?: number } > {
114+ const { query, depth = 'standard' , repoUrl, fetch, logger, env } = params
115+ const payload = { query, depth, ...( repoUrl ? { repoUrl } : { } ) }
116+
117+ const res = await callCodebuffV1 ( {
118+ endpoint : '/api/v1/web-search' ,
119+ payload,
120+ fetch,
121+ logger,
122+ env,
123+ baseUrl : params . baseUrl ,
124+ apiKey : params . apiKey ,
125+ requestName : 'web-search' ,
126+ } )
127+ if ( res . error ) return { error : res . error }
128+
129+ const result = getStringField ( res . json , 'result' )
130+ if ( result ) {
131+ return { result, creditsUsed : res . creditsUsed }
132+ }
133+
134+ const error = getStringField ( res . json , 'error' )
135+ return { error : error ?? 'Invalid response format' }
136+ }
137+
96138export async function callDocsSearchAPI ( params : {
97139 libraryTitle : string
98140 topic ?: string
@@ -105,78 +147,28 @@ export async function callDocsSearchAPI(params: {
105147 apiKey ?: string
106148} ) : Promise < { documentation ?: string ; error ?: string ; creditsUsed ?: number } > {
107149 const { libraryTitle, topic, maxTokens, repoUrl, fetch, logger, env } = params
108- const baseUrl = params . baseUrl ?? env . clientEnv . NEXT_PUBLIC_CODEBUFF_APP_URL
109- const apiKey = params . apiKey ?? env . ciEnv . CODEBUFF_API_KEY
110-
111- if ( ! baseUrl || ! apiKey ) {
112- return { error : 'Missing Codebuff base URL or API key' }
113- }
114-
115- const url = `${ baseUrl } /api/v1/docs-search`
116- const payload : Record < string , any > = { libraryTitle }
150+ const payload : Record < string , unknown > = { libraryTitle }
117151 if ( topic ) payload . topic = topic
118152 if ( typeof maxTokens === 'number' ) payload . maxTokens = maxTokens
119153 if ( repoUrl ) payload . repoUrl = repoUrl
120154
121- try {
122- const res = await withTimeout (
123- fetch ( url , {
124- method : 'POST' ,
125- headers : {
126- 'Content-Type' : 'application/json' ,
127- Authorization : `Bearer ${ apiKey } ` ,
128- 'x-codebuff-api-key' : apiKey ,
129- } ,
130- body : JSON . stringify ( payload ) ,
131- } ) ,
132- FETCH_TIMEOUT_MS ,
133- )
134-
135- const text = await res . text ( )
136- const tryJson = ( ) => {
137- try {
138- return JSON . parse ( text ) as any
139- } catch {
140- return null
141- }
142- }
143-
144- if ( ! res . ok ) {
145- const maybe = tryJson ( )
146- const err =
147- ( maybe && ( maybe . error || maybe . message ) ) || text || 'Request failed'
148- logger . warn (
149- {
150- url,
151- status : res . status ,
152- statusText : res . statusText ,
153- body : text ?. slice ( 0 , 500 ) ,
154- } ,
155- 'Web API docs-search request failed' ,
156- )
157- return { error : typeof err === 'string' ? err : 'Unknown error' }
158- }
159-
160- const data = tryJson ( )
161- if ( data && typeof data . documentation === 'string' ) {
162- return {
163- documentation : data . documentation ,
164- creditsUsed :
165- typeof data . creditsUsed === 'number' ? data . creditsUsed : undefined ,
166- }
167- }
168- if ( data && typeof data . error === 'string' ) return { error : data . error }
169- return { error : 'Invalid response format' }
170- } catch ( error ) {
171- logger . error (
172- {
173- error :
174- error instanceof Error
175- ? { name : error . name , message : error . message , stack : error . stack }
176- : error ,
177- } ,
178- 'Web API docs-search network error' ,
179- )
180- return { error : error instanceof Error ? error . message : 'Network error' }
155+ const res = await callCodebuffV1 ( {
156+ endpoint : '/api/v1/docs-search' ,
157+ payload,
158+ fetch,
159+ logger,
160+ env,
161+ baseUrl : params . baseUrl ,
162+ apiKey : params . apiKey ,
163+ requestName : 'docs-search' ,
164+ } )
165+ if ( res . error ) return { error : res . error }
166+
167+ const documentation = getStringField ( res . json , 'documentation' )
168+ if ( documentation ) {
169+ return { documentation, creditsUsed : res . creditsUsed }
181170 }
171+
172+ const error = getStringField ( res . json , 'error' )
173+ return { error : error ?? 'Invalid response format' }
182174}
0 commit comments