Skip to content

Commit

Permalink
createOptionsAvailable -> availability; add expectedInputLanguages fo…
Browse files Browse the repository at this point in the history
…r languageDetector
  • Loading branch information
domenic committed Dec 12, 2024
1 parent 6981c9c commit 0b498ce
Showing 1 changed file with 34 additions and 34 deletions.
68 changes: 34 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,19 @@ Here `results` will be an array of `{ detectedLanguage, confidence }` objects, w

The language being unknown is represented by `detectedLanguage` being null. The array will always contain at least 1 entry, although it could be for the unknown (`null`) language.

### Checking before creation, and a more realistic combined example
### Language detection with expected input languages

If there are certain languages you need to be able to detect for your use case, you can include them in the `expectedInputLanguages` option when creating a language detector:

Both APIs provide the ability to know, before calling `create()`,what is possible with the implementation. For translator, this is done via `ai.translator.createOptionsAvailable({ sourceLanguage, targetLanguage })`, whereas for language detector, this is done via `ai.languageDetector.canDetect(languageTag)`, or `ai.languageDetector.canDetect()` if you just want to test for the existence of language detection capabilities without any guarantee on which languages are detectable.
```js
const detector = await ai.languageDetector.create({ expectedInputLanguages: ["en", "ja"] });
```

This will allow the implementation to download additional resources like language detection models if necessary, and will ensure that the promise is rejected with a `"NotSupportedError"` `DOMException` if the browser is unable to detect the given input languages.

### Checking before creation, and a more realistic combined example

Both methods return promises, which fulfill with one of the following values:
Both APIs provide the ability to know, before calling `create()`, what is possible with the implementation. This is done via `availability()` methods, which takes the same options as `create()`. They return a promise, which fulfills with one of the following values:

* `"no"` means that the implementation does not support translation or language detection of the given language(s).
* `"after-download"` means that the implementation supports translation or language detection of the given language(s), but it will have to download something (e.g., a machine learning model) as part of creating the associated object.
Expand All @@ -91,7 +99,7 @@ Here is an example that adds capability checking to log more information and fal

```js
async function translateUnknownCustomerInput(textToTranslate, targetLanguage) {
const canDetect = await ai.languageDetector.canDetect();
const canDetect = await ai.languageDetector.availability();

// If there is no language detector, then assume the source language is the
// same as the document language.
Expand All @@ -103,25 +111,19 @@ async function translateUnknownCustomerInput(textToTranslate, targetLanguage) {
console.log("Language detection is available, but something will have to be downloaded. Hold tight!");
}

// Special-case check for Japanese since for our site it's particularly important.
if (await ai.languageDetector.canDetect("ja") === "no") {
console.warn("Japanese Language detection is not available. Falling back to cloud API.");
sourceLanguage = await useSomeCloudAPIToDetectLanguage(textToTranslate);
} else {
const detector = await ai.languageDetector.create();
const [bestResult] = await detector.detect(textToTranslate);

if (bestResult.detectedLangauge ==== null || bestResult.confidence < 0.4) {
// We'll just return the input text without translating. It's probably mostly punctuation
// or something.
return textToTranslate;
}
sourceLanguage = bestResult.detectedLanguage;
const detector = await ai.languageDetector.create();
const [bestResult] = await detector.detect(textToTranslate);

if (bestResult.detectedLangauge ==== null || bestResult.confidence < 0.4) {
// We'll just return the input text without translating. It's probably mostly punctuation
// or something.
return textToTranslate;
}
sourceLanguage = bestResult.detectedLanguage;
}

// Now we've figured out the source language. Let's translate it!
const availability = await ai.translator.createOptionsAvailable({ sourceLanguage, targetLanguage });
const availability = await ai.translator.availability({ sourceLanguage, targetLanguage });
if (availability === "no") {
console.warn("Translation is not available. Falling back to cloud API.");
return await useSomeCloudAPIToTranslate(textToTranslate, { sourceLanguage, targetLanguage });
Expand Down Expand Up @@ -224,7 +226,7 @@ enum AICapabilityAvailability { "readily", "after-download", "no" };
[Exposed=(Window,Worker), SecureContext]
interface AITranslatorFactory {
Promise<AITranslator> create(AITranslatorCreateOptions options);
Promise<AICapabilityAvailability> createOptionsAvailable(AITranslatorCreateCoreOptions options);
Promise<AICapabilityAvailability> availability(AITranslatorCreateCoreOptions options);
};
[Exposed=(Window,Worker), SecureContext]
Expand Down Expand Up @@ -259,18 +261,24 @@ dictionary AITranslatorTranslateOptions {
[Exposed=(Window,Worker), SecureContext]
interface AILanguageDetectorFactory {
Promise<AILanguageDetector> create(optional AILanguageDetectorCreateOptions options = {});
Promise<AICapabilityAvailability> canDetect(optional DOMString languageTag);
Promise<AICapabilityAvailability> availability(optional AILanguageDetectorCreateCoreOptions = {});
};
[Exposed=(Window,Worker), SecureContext]
interface AILanguageDetector {
Promise<sequence<LanguageDetectionResult>> detect(DOMString input,
optional AILanguageDetectorDetectOptions options = {});
readonly attribute FrozenArray<DOMString>? expectedInputLanguages;
undefined destroy();
};
dictionary AILanguageDetectorCreateOptions {
dictionary AILanguageDetectorCreateCoreOptions {
sequence<DOMString> expectedInputLanguages;
};
dictionary AILanguageDetectorCreateOptions : AILanguageDetectorCreateCoreOptions {
AbortSignal signal;
AICreateMonitorCallback monitor;
};
Expand All @@ -293,17 +301,9 @@ We're not clear on what the right model is here, and are discussing it in [issue

### Downloading

The current design envisions that the following operations will _not_ cause downloads of language packs or other material like a language detection model:

* `ai.translator.createOptionsAvailable()`
* `ai.languageDetector.canDetect()`

The following _can_ cause downloads. In all cases, whether or not a call will initiate a download can be detected beforehand by the previously-listed methods.

* `ai.translator.create()`
* `ai.languageDetector.create()`
The current design envisions that `availability()` methods will _not_ cause downloads of language packs or other material like a language detection model. Whereas, the `create()` methods _can_ cause downloads. In all cases, whether or not creation will initiate a download can be detected beforehand by the corresponding `availability()` method.

After a developer has a `AITranslator` or `AILanguageDetector` object created by these methods, further calls are not expected to cause any downloads. (Although they might require internet access, if the implementation is not entirely on-device.)
After a developer has a `AITranslator` or `AILanguageDetector` object, further calls are not expected to cause any downloads. (Although they might require internet access, if the implementation is not entirely on-device.)

This design means that the implementation must have all information about the capabilities of its translation and language detection models available beforehand, i.e. "shipped with the browser". (Either as part of the browser binary, or through some out-of-band update mechanism that eagerly pushes updates.)

Expand All @@ -319,7 +319,7 @@ Some sort of mitigation may be necessary here. We believe this is adjacent to ot
* Partitioning download status by top-level site, introducing a fake download (which takes time but does not actually download anything) for the second-onward site to download a language pack.
* Only exposing a fixed set of languages to this API, e.g. based on the user's locale or the document's main language.

As a first step, we require that detecting the availability of translation/detection be done via individual calls to `ai.translator.createOptionsAvailable()` and `ai.languageDetector.canDetect()`. This allows browsers to implement possible mitigation techniques, such as detecting excessive calls to these methods and starting to return `"no"`.
As a first step, we require that detecting the availability of translation/detection be done via individual calls to `ai.translator.availability()` and `ai.languageDetector.availability()`. This allows browsers to implement possible mitigation techniques, such as detecting excessive calls to these methods and starting to return `"no"`.

Another way in which this API might enhance the web's fingerprinting surface is if translation and language detection models are updated separately from browser versions. In that case, differing results from different versions of the model provide additional fingerprinting bits beyond those already provided by the browser's major version number. Mandating that older browser versions not receive updates or be able to download models from too far into the future might be a possible remediation for this.

Expand Down Expand Up @@ -353,7 +353,7 @@ Should we simplify these down with convenience APIs that do both steps at once?

We're open to this idea, but we think the existing complexity is necessary to support the design wherein translation and language detection models might not be already downloaded. By separating the two stages, we allow web developers to perform the initial creation-and-possibly-downloading steps early in their page's lifecycle, in preparation for later, hopefully-quick calls to APIs like `translate()`.

Another possible simplification is to make the `createOptionsAvailable()` / `canDetect()` APIs synchronous instead of asynchronous. This would be implementable by having the browser proactively load the capabilities information into the main thread's process, upon creation of the global object. We think this is not worthwhile, as it imposes a non-negligible cost on all global object creation, even when the APIs are not used.
Another possible simplification is to make the `availability()` APIs synchronous instead of asynchronous. This would be implementable by having the browser proactively load the capabilities information into the main thread's process, upon creation of the global object. We think this is not worthwhile, as it imposes a non-negligible cost on all global object creation, even when the APIs are not used.

### Allowing unknown source languages for translation

Expand Down

0 comments on commit 0b498ce

Please sign in to comment.