-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Gemini detect language to translate pr (#42)
* gemini detect language to add to list language in firestore * translate doi thanh dang TranslationModel thay vi list TranslationModel * when the list of languages in firebase is empty, get the language of first message that Gemini detect * change 2 chat sessions to only 1 session, that means make only 1 request to both detect language and translate message * fix error lint avoid_web_libraries_in_flutter * fix error lint avoid_web_libraries_in_flutter * format lib * detect locale language to translate * fix bug reviewed and remove browser package to try fix ios bug * thêm lại package browser * fix package browser * a * fix bugs suesi review: index: fix unknown detected language, chat_bubble_widget: fix null check poniter, not translate for mine. locale language: remove lib universal_html * remove lib dart:html (replace by universal_html to get locale language * fix lib/utils/global.dart:22:30: Error: Method 'split' cannot be called on 'String?' because it is potentially null. * a * a * a * a * do not disable lint check, use this command to fix lint automatically: npx eslint . --fix * change vietnamese comments to english
- Loading branch information
Showing
9 changed files
with
1,135 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
module.exports = { | ||
env: { | ||
es6: true, | ||
node: true, | ||
}, | ||
parserOptions: { | ||
"ecmaVersion": 2018, | ||
}, | ||
extends: [ | ||
"eslint:recommended", | ||
"google", | ||
], | ||
rules: { | ||
"no-restricted-globals": ["error", "name", "length"], | ||
"prefer-arrow-callback": "error", | ||
"quotes": ["error", "double", {"allowTemplateLiterals": true}], | ||
}, | ||
overrides: [ | ||
{ | ||
files: ["**/*.spec.*"], | ||
env: { | ||
mocha: true, | ||
}, | ||
rules: {}, | ||
}, | ||
], | ||
globals: {}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
const globals = require("globals"); | ||
const js = require("@eslint/js"); | ||
|
||
module.exports = [ | ||
js.configs.recommended, | ||
{ | ||
files: ["**/*.js"], | ||
languageOptions: { | ||
ecmaVersion: 2018, | ||
sourceType: "commonjs", | ||
globals: { | ||
...globals.node | ||
} | ||
}, | ||
rules: { | ||
"quotes": ["error", "double"], | ||
"max-len": ["error", {"code": 120}], | ||
"indent": ["error", 2], | ||
"comma-dangle": ["error", "only-multiline"], | ||
"camelcase": "error", | ||
"no-unused-vars": "warn" | ||
} | ||
} | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,140 @@ | ||
/** | ||
* Import function triggers from their respective submodules: | ||
* | ||
* const {onCall} = require("firebase-functions/v2/https"); | ||
* const {onDocumentWritten} = require("firebase-functions/v2/firestore"); | ||
* | ||
* See a full list of supported triggers at https://firebase.google.com/docs/functions | ||
*/ | ||
const v2 = require("firebase-functions/v2"); | ||
const vertexAIApi = require("@google-cloud/vertexai"); | ||
const admin = require("firebase-admin"); | ||
|
||
// Create and deploy your first functions | ||
// https://firebase.google.com/docs/functions/get-started | ||
admin.initializeApp(); | ||
|
||
const project = 'proj-atc'; | ||
const location = 'us-central1'; | ||
const textModel = 'gemini-1.5-flash'; | ||
const visionModel = 'gemini-1.0-pro-vision'; | ||
|
||
const vertexAI = new vertexAIApi.VertexAI({project: project, location: location}); | ||
const project = "proj-atc"; | ||
const location = "us-central1"; | ||
const textModel = "gemini-1.5-flash"; | ||
// const visionModel = 'gemini-1.0-pro-vision'; | ||
async function saveNewLanguageCode(languagesCollection, detectedLanguage, languages) { | ||
if (detectedLanguage && !languages.includes(detectedLanguage)) { | ||
console.log(`Adding new language code: ${detectedLanguage}`); | ||
try { | ||
await languagesCollection.add({ code: detectedLanguage }); | ||
console.log("Successfully added new language code to database"); | ||
return true; | ||
} catch (error) { | ||
console.error("Error adding new language code to database:", error); | ||
return false; | ||
} | ||
} | ||
return false; | ||
} | ||
const vertexAI = new vertexAIApi.VertexAI({ project: project, location: location }); | ||
|
||
const generativeVisionModel = vertexAI.getGenerativeModel({ | ||
model: visionModel, | ||
}); | ||
// const generativeVisionModel = vertexAI.getGenerativeModel({ | ||
// model: visionModel, | ||
// }); | ||
|
||
const generativeModelPreview = vertexAI.preview.getGenerativeModel({ | ||
model: textModel, | ||
model: textModel, | ||
}); | ||
|
||
const generationConfig = { | ||
// use onDocumentWritten here to prepare for "edit message" feature later | ||
exports.onChatWritten = v2.firestore.onDocumentWritten("/public/{messageId}", async (event) => { | ||
const document = event.data.after.data(); | ||
const message = document["message"]; | ||
console.log(`message: ${message}`); | ||
|
||
// no message? do nothing | ||
if (message == undefined) { | ||
return; | ||
} | ||
const curTranslated = document["translated"]; | ||
|
||
// check if message is translated | ||
if (curTranslated != undefined) { | ||
// message was translated before, | ||
// check the original message | ||
const original = curTranslated["original"]; | ||
|
||
console.log("Original: ", original); | ||
// message is same as original, meaning it's already translated. Do nothing | ||
if (message == original) { | ||
return; | ||
} | ||
} | ||
// Get list of languages from Firestore | ||
const db = admin.firestore(); | ||
const languagesCollection = db.collection("languages"); | ||
const languagesSnapshot = await languagesCollection.get(); | ||
const languages = languagesSnapshot.docs.map((e) => e.data().code); | ||
console.log("Current languages in database:", languages); | ||
|
||
const generationConfig = { | ||
temperature: 1, | ||
topP: 0.95, | ||
topK: 64, | ||
maxOutputTokens: 8192, | ||
responseMimeType: "application/json", | ||
responseSchema: { | ||
type: "object", | ||
properties: { | ||
en: { | ||
type: "string" | ||
} | ||
properties: | ||
{ | ||
"detectedLanguage": { | ||
type: "string", | ||
description: "The ISO 639-1 language code that was detected", | ||
pattern: "^[a-z]{2}$" | ||
}, | ||
"translation": languages.reduce((acc, lang) => { | ||
acc[lang] = { type: "string" }; | ||
return acc; | ||
}, {}), | ||
}, | ||
required: [ | ||
"en" | ||
] | ||
required: ["detectedLanguage", "translation"] | ||
}, | ||
}; | ||
|
||
// use onDocumentWritten here to prepare to "edit message" feature later | ||
exports.onChatWritten = v2.firestore.onDocumentWritten("/public/{messageId}",async (event) => { | ||
const document = event.data.after.data(); | ||
const message = document["message"]; | ||
console.log(`message: ${message}`); | ||
const translateSession = generativeModelPreview.startChat({ | ||
generateContentConfig: generationConfig, | ||
}); | ||
|
||
// no message? do nothing | ||
if (message == undefined) { | ||
return; | ||
} | ||
const curTranslated = document["translated"]; | ||
const result = await translateSession.sendMessage(` | ||
The target languages: ${languages.join(", ")}. | ||
The input text: "${message}". You must do: | ||
1. detect language of the input text. | ||
Only return the language code that is in ISO 639-1 format. | ||
If you can't detect the language, return "und" as value of "detectedLanguage" field. | ||
2. If the target languages are not empty and the detected language is not "und", | ||
translate the input text to target languages, ignore language that is identical to the detected language. | ||
Else, return null as value of "translation" field. | ||
Example: Translate "Chào" to ["ja", "en", "vi"]: | ||
{ | ||
"detectedLanguage": "vi", | ||
"translation": {"ja": "こんにちは", "en": "Hello"}, | ||
} | ||
` | ||
); | ||
|
||
// check if message is translated | ||
if (curTranslated != undefined) { | ||
// message is translated before, | ||
// check the original message | ||
const original = curTranslated["original"]; | ||
const response = result.response; | ||
console.log("Response:", JSON.stringify(response)); | ||
|
||
console.log('Original: ', original); | ||
// message is same as original, meaning it's already translated. Do nothing | ||
if (message == original) { | ||
return; | ||
} | ||
} | ||
const responseContent = response.candidates[0].content; | ||
let translationData = null; | ||
let detectedLanguage = null; | ||
|
||
const chatSession = generativeModelPreview.startChat({ | ||
generationConfig: generationConfig | ||
}); | ||
const result = await chatSession.sendMessage(`translate this text to English: ${message}`); | ||
const response = result.response; | ||
console.log('Response:', JSON.stringify(response)); | ||
try { | ||
// Extract JSON from text part (remove markdown ``` characters) | ||
const jsonText = responseContent.parts[0].text.replace(/```json\n|\n```/g, ""); | ||
translationData = JSON.parse(jsonText); | ||
detectedLanguage = translationData.detectedLanguage; | ||
console.log("Detected language:", detectedLanguage); | ||
console.log("Translation data:", translationData.translation); | ||
} catch (error) { | ||
console.error("Error parsing translation response:", error); | ||
} | ||
|
||
const jsonTranslated = response.candidates[0].content.parts[0].text; | ||
console.log('translated json: ', jsonTranslated); | ||
// parse this json to get translated text out | ||
const translated = JSON.parse(jsonTranslated); | ||
console.log('final result: ', translated.en); | ||
// Save new language if detected | ||
if (detectedLanguage && detectedLanguage !== "und") { | ||
await saveNewLanguageCode(languagesCollection, detectedLanguage, languages); | ||
} | ||
|
||
// write to message | ||
const data = event.data.after.data(); | ||
return event.data.after.ref.set({ | ||
'translated': { | ||
'original':message, | ||
'en': translated.en | ||
} | ||
}, {merge: true}); | ||
}) | ||
// Update document with translation | ||
return event.data.after.ref.set({ | ||
"translated": { | ||
"original": message, | ||
...translationData.translation | ||
} | ||
}, { merge: true }); | ||
}); |
Oops, something went wrong.