@@ -23,82 +23,78 @@ const ACTION_CONTEXT: Record<AiAction, string> = {
2323 'Verifique possíveis erros de sintaxe, parênteses órfãos ou vírgulas ausentes e conserte-os.' ,
2424} ;
2525
26+ const MODEL = 'gemini-2.0-flash' ;
2627const TIMEOUT_MS = 15_000 ;
2728
28- export interface RunAiActionOptions {
29- action : AiAction ;
30- code : string ;
31- history ?: { role : 'user' | 'model' ; text : string } [ ] ;
32- signal ?: AbortSignal ;
33- }
34-
35- export async function runAiAction ( opts : RunAiActionOptions ) : Promise < string > {
36- const { action, code, history, signal : externalSignal } = opts ;
29+ export async function runAiAction ( opts : { action : AiAction ; code : string } ) : Promise < string > {
30+ const { action, code } = opts ;
3731
3832 if ( ! code . trim ( ) ) {
3933 throw { kind : 'empty' } satisfies AiError ;
4034 }
4135
42- const proxyUrl = ( import . meta. env . VITE_GEMINI_PROXY_URL as string | undefined ) ?? '/api/gemini' ;
36+ const apiKey = import . meta. env . VITE_GEMINI_API_KEY as string | undefined ;
37+ if ( ! apiKey ) {
38+ throw { kind : 'config' } satisfies AiError ;
39+ }
40+
41+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${ MODEL } :generateContent?key=${ apiKey } ` ;
4342
4443 const controller = new AbortController ( ) ;
4544 const timeoutId = window . setTimeout ( ( ) => controller . abort ( ) , TIMEOUT_MS ) ;
46- if ( externalSignal ) {
47- if ( externalSignal . aborted ) controller . abort ( ) ;
48- else externalSignal . addEventListener ( 'abort' , ( ) => controller . abort ( ) , { once : true } ) ;
49- }
5045
5146 let response : Response ;
5247 try {
53- response = await fetch ( proxyUrl , {
48+ response = await fetch ( url , {
5449 method : 'POST' ,
5550 headers : { 'Content-Type' : 'application/json' } ,
5651 body : JSON . stringify ( {
57- action,
58- code,
59- history : history ?? [ ] ,
60- systemInstruction : promptText ,
61- actionContext : ACTION_CONTEXT [ action ] ,
62- model : 'gemini-2.5-flash' ,
52+ systemInstruction : { parts : [ { text : promptText } ] } ,
53+ contents : [
54+ {
55+ role : 'user' ,
56+ parts : [ { text : `${ ACTION_CONTEXT [ action ] } \n\nCódigo:\n${ code } ` } ] ,
57+ } ,
58+ ] ,
6359 } ) ,
6460 signal : controller . signal ,
6561 } ) ;
6662 } catch ( err ) {
6763 window . clearTimeout ( timeoutId ) ;
68- if ( ( err as Error ) ?. name === 'AbortError' ) {
69- throw { kind : 'network' } satisfies AiError ;
70- }
7164 throw { kind : 'network' } satisfies AiError ;
7265 }
7366 window . clearTimeout ( timeoutId ) ;
7467
7568 if ( ! response . ok ) {
7669 const status = response . status ;
7770 if ( status === 429 ) {
78- const scope = response . headers . get ( 'x-quota-scope' ) ;
79- if ( scope === 'daily' ) throw { kind : 'rate_daily' } satisfies AiError ;
80- throw { kind : 'rate_minute' } satisfies AiError ;
71+ let errorText = '' ;
72+ try { errorText = await response . text ( ) ; } catch { /* ignore */ }
73+ const scope = / p e r d a y | d a i l y / i. test ( errorText ) ? 'rate_daily' : 'rate_minute' ;
74+ throw { kind : scope } satisfies AiError ;
8175 }
8276 if ( status === 401 || status === 403 ) {
8377 throw { kind : 'auth' } satisfies AiError ;
8478 }
8579 let message = `HTTP ${ status } ` ;
8680 try {
8781 const body = await response . json ( ) ;
88- if ( body ?. error ) message = String ( body . error ) ;
89- } catch {
90- /* ignore */
91- }
82+ if ( body ?. error ?. message ) message = String ( body . error . message ) ;
83+ } catch { /* ignore */ }
9284 throw { kind : 'unknown' , message } satisfies AiError ;
9385 }
9486
95- let payload : { text ?: string } ;
87+ let data : { candidates ?: { content ?: { parts ?: { text ?: string } [ ] } } [ ] } ;
9688 try {
97- payload = await response . json ( ) ;
89+ data = await response . json ( ) ;
9890 } catch {
99- throw { kind : 'unknown' , message : 'Resposta inválida do servidor .' } satisfies AiError ;
91+ throw { kind : 'unknown' , message : 'Resposta inválida da API .' } satisfies AiError ;
10092 }
10193
102- const raw = payload . text ?? '' ;
94+ const raw =
95+ data ?. candidates ?. [ 0 ] ?. content ?. parts
96+ ?. map ( ( p ) => p . text ?? '' )
97+ . join ( '' ) ?? '' ;
98+
10399 return stripCodeFences ( raw ) ;
104100}
0 commit comments