aicommit2
Version:
A Reactive CLI that generates commit messages for Git and Jujutsu with various AI
2 lines (1 loc) • 6.44 kB
JavaScript
import{HarmCategory as r,HarmBlockThreshold as s,GoogleGenerativeAI as B}from"@google/generative-ai";import T from"chalk";import{concatMap as G,from as M,map as I,catchError as S}from"rxjs";import{fromPromise as P}from"rxjs/internal/observable/innerFrom";import{A as H,l as x,a as R,b as v,c as L,d as N,e as w}from"./ai.service-d8e94c3a.mjs";import{g as D,b as $}from"./cli-8ee62906.mjs";import"fs";import"path";import"@pacote/xxhash";import"winston";import"cleye";import"module";import"crypto";import"os";import"node:buffer";import"node:path";import"node:child_process";import"node:process";import"child_process";import"node:url";import"node:os";import"assert";import"events";import"node:fs";import"buffer";import"stream";import"util";import"node:util";import"inquirer";import"fs/promises";import"readline";import"figlet";import"gradient-string";import"ora";import"inquirer-reactive-list-prompt";import"winston-daily-rotate-file";import"axios";import"url";import"node:fs/promises";import"chokidar";import"rxjs/operators";class Y extends H{constructor(t){super(t),this.params=t,this.generateStreamingCommitMessage$=()=>{const{generate:e,type:o}=this.params.config;return this.createStreamingCommitMessages$(i=>{this.streamChunks(i).catch(c=>i.error(c))},o,e)},this.streamChunks=async e=>{const o=this.params.stagedDiff.diff,{logging:i}=this.params.config,c=this.params.config.maxTokens,p=D(this.buildPromptOptions()),d={maxOutputTokens:c,temperature:this.params.config.temperature,topP:this.params.config.topP},g=this.genAI.getGenerativeModel({model:this.params.config.model,systemInstruction:p,generationConfig:d,safetySettings:[{category:r.HARM_CATEGORY_HATE_SPEECH,threshold:s.BLOCK_LOW_AND_ABOVE},{category:r.HARM_CATEGORY_SEXUALLY_EXPLICIT,threshold:s.BLOCK_LOW_AND_ABOVE},{category:r.HARM_CATEGORY_HARASSMENT,threshold:s.BLOCK_LOW_AND_ABOVE},{category:r.HARM_CATEGORY_DANGEROUS_CONTENT,threshold:s.BLOCK_LOW_AND_ABOVE}]}),n=this.buildUserPrompt(o,"commit"),l=`${this.params.config.url||"https://generativelanguage.googleapis.com"}/v1beta/models/${this.params.config.model}:streamGenerateContent`,y={"Content-Type":"application/json","x-goog-api-key":this.params.config.key};x(o,"commit","Gemini",this.params.config.model,l,y,i),R(o,"commit","Gemini",p,n,i),v(o,"commit","Gemini",{systemInstruction:{parts:[{text:p}]},contents:[{parts:[{text:n}]}],generationConfig:d},i);const O=Date.now();let f="";try{const a=this.params.config.timeout>1e4?{request:{timeout:this.params.config.timeout}}:void 0,m=await g.generateContentStream(n,a);for await(const h of m.stream){const A=h.text();A&&(f+=A,e.next(A))}const u=Date.now()-O;L(o,"commit","Gemini",{streamed:!0,totalLength:f.length},i),N(o,"commit","Gemini",u,f,i),e.complete()}catch(a){w(o,"commit","Gemini",a,i),e.error(a)}},this.colors={primary:"#0077FF",secondary:"#fff"},this.serviceName=T.bgHex(this.colors.primary).hex(this.colors.secondary).bold(`[Gemini${this.formatModelSuffix()}]`),this.errorPrefix=T.red.bold(`[Gemini${this.formatModelSuffix()}]`),this.genAI=new B(this.params.config.key)}getServiceSpecificErrorMessage(t){const e=t.message||"";return e.includes("API key")||e.includes("api_key")?"Invalid API key. Check your Google AI Studio API key in configuration":e.includes("quota")||e.includes("QUOTA_EXCEEDED")?"API quota exceeded. Check your Google AI Studio usage limits":e.includes("model")||e.includes("Model")?"Model not found or not accessible. Check if the Gemini model name is correct":e.includes("SAFETY")||e.includes("safety")?"Content blocked by safety filters. Try rephrasing your request":e.includes("RECITATION")||e.includes("recitation")?"Content blocked due to recitation concerns. Try a different approach":e.includes("403")||e.includes("Forbidden")?"Access denied. Your API key may not have permission for this Gemini model":e.includes("404")||e.includes("Not Found")?"Model or endpoint not found. Check your Gemini model configuration":e.includes("500")||e.includes("Internal Server Error")?"Google AI service error. Try again later":e.includes("MAX_TOKENS")||e.includes("truncated")||e.includes("maxOutputTokens")?"Response truncated due to token limit. Gemini 2.5+ models use thinking tokens. Try increasing maxTokens (recommended: 8192+)":null}generateCommitMessage$(){return this.params.config.stream||!1?this.generateStreamingCommitMessage$():P(this.generateMessage("commit")).pipe(G(e=>M(e)),I(this.formatAsChoice),S(this.handleError$))}generateCodeReview$(){return P(this.generateMessage("review")).pipe(G(t=>M(t)),I(this.formatCodeReviewAsChoice),S(this.handleError$))}async generateMessage(t){const e=this.params.stagedDiff.diff,{logging:o,generate:i,type:c}=this.params.config,p=this.params.config.maxTokens,d=this.buildPromptOptions(),g=t==="review"?$(d):D(d),n={maxOutputTokens:p,temperature:this.params.config.temperature,topP:this.params.config.topP},_=this.genAI.getGenerativeModel({model:this.params.config.model,systemInstruction:g,generationConfig:n,safetySettings:[{category:r.HARM_CATEGORY_HATE_SPEECH,threshold:s.BLOCK_LOW_AND_ABOVE},{category:r.HARM_CATEGORY_SEXUALLY_EXPLICIT,threshold:s.BLOCK_LOW_AND_ABOVE},{category:r.HARM_CATEGORY_HARASSMENT,threshold:s.BLOCK_LOW_AND_ABOVE},{category:r.HARM_CATEGORY_DANGEROUS_CONTENT,threshold:s.BLOCK_LOW_AND_ABOVE}]}),l=this.buildUserPrompt(e,t),E=`${this.params.config.url||"https://generativelanguage.googleapis.com"}/v1beta/models/${this.params.config.model}:generateContent`,O={"Content-Type":"application/json","x-goog-api-key":this.params.config.key};x(e,t,"Gemini",this.params.config.model,E,O,o),R(e,t,"Gemini",g,l,o),v(e,t,"Gemini",{systemInstruction:{parts:[{text:g}]},contents:[{parts:[{text:l}]}],generationConfig:n},o);const a=Date.now();try{const m=this.params.config.timeout>1e4?{request:{timeout:this.params.config.timeout}}:void 0,u=await _.generateContent(l,m),h=u.response;if(h.candidates?.[0]?.finishReason==="MAX_TOKENS"){const k=h.usageMetadata;throw new Error(`Response truncated: maxOutputTokens exceeded. Thinking tokens: ${k?.thoughtsTokenCount??"N/A"}, Output tokens: ${k?.candidatesTokenCount??"N/A"}. Increase maxTokens config for Gemini 2.5+ thinking models.`)}const C=h.text(),b=Date.now()-a;return L(e,t,"Gemini",{response:C,candidates:u.response.candidates,usageMetadata:u.response.usageMetadata},o),N(e,t,"Gemini",b,C,o),t==="review"?this.parseCodeReview(C):this.parseMessage(C,c,i)}catch(m){throw w(e,t,"Gemini",m,o),m}}}export{Y as GeminiService};