Skip to content

Commit

Permalink
Polyfill the window.translation apis ontop of the ai translation apis #1
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas101 committed Nov 14, 2024
1 parent 4527750 commit 75e1fba
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 32 deletions.
68 changes: 38 additions & 30 deletions src/extension/background/APIHandler/AILanguageDetectorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,39 +87,47 @@ class AILanguageDetectorHandler {
const input = payload.getString('input')
const prompt = this.#getPrompt(manifest, input)
const sessionId = payload.getNonEmptyString('sessionId')

const result = JSON.parse(await AILlmSession.prompt(
sessionId,
prompt,
{
...props,
grammar: {
type: 'object',
properties: {
detectedLanguage: {
type: 'string',
enum: AILanguageDetectorDefaultLanguages
const results: AILanguageDetectorDetectResult[] = []

for (let i = 0; i < 1; i++) {
const detectedLanguage = new Set(results.map(result => result.detectedLanguage))
const languages = AILanguageDetectorDefaultLanguages.filter((language) => !detectedLanguage.has(language))

const result = JSON.parse(await AILlmSession.prompt(
sessionId,
prompt,
{
...props,
grammar: {
type: 'object',
properties: {
detectedLanguage: {
type: 'string',
enum: languages
},
confidence: {
type: 'integer',
minimum: 0,
maximum: 100
}
},
confidence: {
type: 'integer',
minimum: 0,
maximum: 100
}
},
required: ['detectedLanguage', 'confidence'],
additionalProperties: false
required: ['detectedLanguage', 'confidence'],
additionalProperties: false
}
},
{
signal: channel.abortSignal,
stream: noop
}
},
{
signal: channel.abortSignal,
stream: noop
}
))
))

return {
detectedLanguage: result.detectedLanguage,
confidence: result.confidence / 100
} as AILanguageDetectorDetectResult
results.push({
detectedLanguage: result.detectedLanguage,
confidence: result.confidence / 100
} as AILanguageDetectorDetectResult)
}

return results
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ class AILanguageDetector extends AIRootModel {
// MARK: Detection
/* **************************************************************************/

detect = async (input: string, options: AILanguageDetectorDetectOptions = {}):Promise<AILanguageDetectorDetectResult> => {
detect = async (input: string, options: AILanguageDetectorDetectOptions = {}):Promise<AILanguageDetectorDetectResult[]> => {
this.#guardDestroyed()

const signal = AbortSignal.any([options.signal, this.#signal].filter(Boolean))

const result = (await IPC.request(kLanguageDetectorDetect, { props: this.#props, input }, { signal })) as AILanguageDetectorDetectResult
const result = (await IPC.request(kLanguageDetectorDetect, { props: this.#props, input }, { signal })) as AILanguageDetectorDetectResult[]
return result
}
}
Expand Down
133 changes: 133 additions & 0 deletions src/extension/contentscript-main/Translation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import AI from './AI'
import AILanguageDetector from './AILanguageDetector/AILanguageDetector'
import AITranslator from './AITranslator/AITranslator'

type DetectorBridge = {
aiDetector?: AILanguageDetector
readyResolvers: Array<(value: any) => void>
}

type TranslatorOptions = {
sourceLanguage: string
targetLanguage: string
}

/* **************************************************************************/
//
// MARK: ==Detector==
//
/* **************************************************************************/

class Detector extends EventTarget {
#bridge: DetectorBridge

constructor (bridge: DetectorBridge) {
super()
this.#bridge = bridge
}

get ready () {
if (this.#bridge.aiDetector) {
return Promise
} else {
return new Promise((resolve) => {
this.#bridge.readyResolvers.push(resolve)
})
}
}

detect = async (input: string) => {
await this.ready
return this.#bridge.aiDetector.detect(input)
}
}

/* **************************************************************************/
//
// MARK: ==Translator==
//
/* **************************************************************************/

class Translator {
#aiTranslator: AITranslator

constructor (aiTranslator: AITranslator) {
this.#aiTranslator = aiTranslator
}

translate = async (input: string) => {
return this.#aiTranslator.translate(input)
}
}

/* **************************************************************************/
//
// MARK: ==Translation==
//
/* **************************************************************************/

class Translation {
/* **************************************************************************/
// MARK: Private
/* **************************************************************************/

#ai: AI
#downloadProgressFn: (evt: Event) => void

/* **************************************************************************/
// MARK: Lifecycle
/* **************************************************************************/

constructor (ai: AI) {
this.#ai = ai
}

/* **************************************************************************/
// MARK: Detection
/* **************************************************************************/

canDetect = async () => {
const capabilities = await this.#ai.languageDetector.capabilities()
return capabilities.available
}

createDetector = async () => {
const bridge: DetectorBridge = {
readyResolvers: []
}
const detector = new Detector(bridge)

this.#ai.languageDetector.create({
monitor: (m: EventTarget) => {
m.addEventListener('downloadprogress', (evt) => {
detector.dispatchEvent(evt)
})
}
}).then((aiDetector: AILanguageDetector) => {
bridge.aiDetector = aiDetector
const resolvers = bridge.readyResolvers
bridge.readyResolvers = []
resolvers.forEach((resolve) => { resolve(undefined) })
})

return detector
}

/* **************************************************************************/
// MARK: Translation
/* **************************************************************************/

get downloadProgress () { return this.#downloadProgressFn }
set downloadProgress (v) { this.#downloadProgressFn = v }

canTranslate = async (opts: TranslatorOptions) => {
const capabilities = await this.#ai.translator.capabilities()
return capabilities.languagePairAvailable(opts.sourceLanguage, opts.targetLanguage)
}

createTranslator = async (opts: TranslatorOptions) => {
return new Translator(await this.#ai.translator.create(opts))
}
}

export default Translation
4 changes: 4 additions & 0 deletions src/extension/contentscript-main/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AI from './AI'
import Translation from './Translation'

const ai = new AI(window.ai)

Expand All @@ -7,6 +8,9 @@ if (process.env.BROWSER !== 'extlib') {
if (!window.ai) {
genericWindow.ai = ai
}
if (!genericWindow.translation) {
genericWindow.translation = new Translation(ai)
}
genericWindow.aibrow = ai
}

Expand Down

0 comments on commit 75e1fba

Please sign in to comment.