|
import { BaseChat } from './base/base'; |
|
import { IChatInputMessage, IStreamHandler } from '../interface'; |
|
import { DefaultSystem } from '../utils/constant'; |
|
import { httpRequest } from '../utils/utils'; |
|
import { fetchEventData } from 'fetch-sse'; |
|
|
|
const BASE_URL = 'https://generativelanguage.googleapis.com/v1beta'; |
|
|
|
const URLS = { |
|
geminiPro: '/models/gemini-pro:generateContent', |
|
geminiProStream: '/models/gemini-pro:streamGenerateContent?alt=sse', |
|
}; |
|
|
|
export class GoogleChat implements BaseChat { |
|
private key?: string; |
|
private baseUrl?: string; |
|
public platform = 'google'; |
|
|
|
constructor() { |
|
this.key = process.env.GOOGLE_KEY; |
|
this.baseUrl = process.env.GOOGLE_PROXY_URL || BASE_URL; |
|
console.log('GoogleAI BaseURL: ', this.baseUrl); |
|
} |
|
|
|
public async chat( |
|
messages: IChatInputMessage[], |
|
|
|
model: string |
|
) { |
|
const msgs = this.transformMessage(messages); |
|
const url = `${this.baseUrl}/${URLS.geminiProStream}`; |
|
const res = await httpRequest({ |
|
endpoint: url, |
|
method: 'POST', |
|
data: JSON.stringify({ |
|
contents: msgs |
|
}), |
|
query: { |
|
key: this.key, |
|
}, |
|
}); |
|
const data = await res.json(); |
|
const resMsg = data.candidates?.[0]; |
|
if (res.status !== 200 || !resMsg) { |
|
throw new Error(data.message ?? 'Google AI request error.'); |
|
} |
|
return resMsg.content?.parts[0]?.text; |
|
} |
|
|
|
public async chatStream( |
|
messages: IChatInputMessage[], |
|
onMessage: IStreamHandler, |
|
|
|
model: string, |
|
system = DefaultSystem |
|
) { |
|
const msgs = this.transformMessage(messages); |
|
if (system) { |
|
msgs.unshift({ |
|
role: 'user', |
|
parts: [ |
|
{ |
|
text: system |
|
} |
|
] |
|
}, { |
|
role: 'model', |
|
parts: [ |
|
{ |
|
text: 'ok.' |
|
} |
|
] |
|
}); |
|
} |
|
const url = `${this.baseUrl}${URLS.geminiProStream}`; |
|
const data = { |
|
contents: msgs |
|
}; |
|
const abort = new AbortController(); |
|
await fetchEventData(url, { |
|
method: 'POST', |
|
data, |
|
signal: abort.signal, |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'x-goog-api-key': this.key |
|
}, |
|
onOpen: async () => { |
|
|
|
}, |
|
onMessage: (eventData) => { |
|
const data = eventData?.data; |
|
const result = JSON.parse(data || '{}'); |
|
const msg = result.candidates?.[0]?.content?.parts[0]?.text ?? ''; |
|
onMessage(msg, false); |
|
}, |
|
onClose: () => { |
|
onMessage(null, true); |
|
}, |
|
onError: (error) => { |
|
abort.abort(); |
|
console.log(error); |
|
}, |
|
}); |
|
} |
|
|
|
private transformMessage(messages: IChatInputMessage[]) { |
|
return messages.map(msg => { |
|
const role = msg.role === 'assistant' ? 'model' : 'user'; |
|
return { |
|
role, |
|
parts: [ |
|
{ |
|
text: msg.content, |
|
}, |
|
], |
|
}; |
|
}); |
|
} |
|
} |
|
|
|
export const google = new GoogleChat(); |
|
|