0

Reland "[Language Detector] Implement overhauled availability() method"

This is a reland of commit 1a53226877

Temporarily disabled failing browsertests on Chrome OS, as they
unexpectedly fail (linux-chrome-os). The rest of the CL is unchanged.

This bug possibly explains why the disabled tests fail on Chrome OS
specifically, with the "ai is not defined" error. crbug.com/392975539

Original change's description:
> [Language Detector] Implement overhauled availability() method
>
> Implementation of the availability() method for the
> Language Detector API. Determines whether the language model is available for Language Detection.
>
> Bug: 390459311
> Change-Id: Ie8c168b509b397fec08b3229c1a51930bec413c6
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6272235
> Reviewed-by: Nathan Memmott <memmott@chromium.org>
> Reviewed-by: Jiacheng Guo <gjc@google.com>
> Commit-Queue: Christine Hollingsworth <christinesm@chromium.org>
> Reviewed-by: Megan Jablonski <megjablon@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1423915}

Bug: 390459311
Change-Id: I5d825d137920f497d9e00bbedb4fb36378247b50
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6299237
Reviewed-by: Jiacheng Guo <gjc@google.com>
Auto-Submit: Christine Hollingsworth <christinesm@chromium.org>
Commit-Queue: Jiacheng Guo <gjc@google.com>
Reviewed-by: Megan Jablonski <megjablon@chromium.org>
Reviewed-by: Nathan Memmott <memmott@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1424214}
This commit is contained in:
Christine Hollingsworth
2025-02-24 16:00:23 -08:00
committed by Chromium LUCI CQ
parent b7fa79d9be
commit abe3e2cace
17 changed files with 152 additions and 3 deletions

@ -26,6 +26,7 @@ interface AILanguageDetectorCapabilities
method languageAvailable
interface AILanguageDetectorFactory
attribute @@toStringTag
method availability
method capabilities
method constructor
method create

@ -12,6 +12,7 @@
#include "base/functional/bind.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
@ -100,6 +101,27 @@ class LanguageDetectionModelServiceDisabledBrowserTest
InProcessBrowserTest::SetUp();
}
// TODO(crbug.com/398904210): Re-enable on Chrome OS. Assertion fails with
// "ReferenceError: ai is not defined" when this method is called from
// individual tests.
#if !BUILDFLAG(IS_CHROMEOS)
void TestLanguageDetectionAvailable(Browser* browser,
const std::string_view result) {
ASSERT_EQ(EvalJs(browser->tab_strip_model()->GetActiveWebContents(),
base::StringPrintf(R"(
(async () => {
try {
return await ai.languageDetector.availability();
} catch (e) {
return e.toString();
}
})();
)", ))
.ExtractString(),
result);
}
#endif // !BUILDFLAG(IS_CHROMEOS)
~LanguageDetectionModelServiceDisabledBrowserTest() override = default;
const GURL& english_url() const { return english_url_; }
@ -116,6 +138,18 @@ IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceDisabledBrowserTest,
browser()->profile()));
}
// TODO(crbug.com/398904210): Re-enable test on Chrome OS. Test fails with
// "ReferenceError: ai is not defined".
#if !BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceDisabledBrowserTest,
Availability_ModelUnavailable) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), english_url()));
// Language detection is not available when the model service is disabled.
TestLanguageDetectionAvailable(browser(), "unavailable");
}
#endif // !BUILDFLAG(IS_CHROMEOS)
class LanguageDetectionModelServiceWithoutOptimizationGuideBrowserTest
: public LanguageDetectionModelServiceDisabledBrowserTest {
public:
@ -574,5 +608,32 @@ IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
ASSERT_TRUE(RequestAndWaitForModelFile()->IsValid());
}
// TODO(crbug.com/398904210): Re-enable test on Chrome OS. Test fails with
// "ReferenceError: ai is not defined".
#if !BUILDFLAG(IS_CHROMEOS)
// Tests the behavior of availability().
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest, Availability) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
ASSERT_TRUE(language_detection_model_service());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), english_url()));
// Language detection is not readily available until the model is downloaded.
TestLanguageDetectionAvailable(browser(), "downloadable");
ModelFileGetter getter(*language_detection_model_service());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
getter.RequestModelFile();
auto model_file = getter.WaitForModelFile();
TestLanguageDetectionAvailable(browser(), "available");
}
#endif //! BUILDFLAG(IS_CHROMEOS)
} // namespace
} // namespace language_detection

@ -2919,6 +2919,7 @@ if (!is_android) {
"../browser/invalidation/profile_invalidation_provider_factory_browsertest.cc",
"../browser/k_anonymity_service/k_anonymity_service_client_browsertest.cc",
"../browser/l10n_util_browsertest.cc",
"../browser/language_detection/language_detection_model_service_browsertest.cc",
"../browser/launch_time_navigation_signal/launch_time_navigation_signal_browsertest.cc",
"../browser/lifetime/browser_close_manager_browsertest.cc",
"../browser/lifetime/browser_shutdown_browsertest.cc",
@ -3227,7 +3228,6 @@ if (!is_android) {
"../browser/tpcd/metadata/manager_browsertest.cc",
"../browser/tpcd/support/top_level_trial_service_browsertest.cc",
"../browser/tracing/chrome_tracing_delegate_browsertest.cc",
"../browser/translate/language_detection_model_service_browsertest.cc",
"../browser/translate/translate_frame_binder_browsertest.cc",
"../browser/translate/translate_manager_browsertest.cc",
"../browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc",

@ -4712,6 +4712,7 @@ enum WebFeature {
kGeolocationWouldSucceedWhenAdScriptInStack = 5326,
kAdScriptInStackOnWatchGeoLocation = 5327,
kDeviceBoundSessionRegistered = 5328,
kV8AILanguageDetectorFactory_Availability_Method = 5329,
// Add new features immediately above this line. Don't change assigned
// numbers of any item, and don't reuse removed slots. Also don't add extra

@ -84,6 +84,29 @@ AIAvailability HandleTranslatorAvailabilityCheckResult(
}
}
AIAvailability HandleLanguageDetectionModelCheckResult(
ExecutionContext* execution_context,
language_detection::mojom::blink::LanguageDetectionModelStatus result) {
switch (result) {
case language_detection::mojom::blink::LanguageDetectionModelStatus::
kReadily:
return HandleModelAvailabilityCheckResult(
execution_context, AIMetrics::AISessionType::kLanguageDetector,
mojom::blink::ModelAvailabilityCheckResult::kAvailable);
case language_detection::mojom::blink::LanguageDetectionModelStatus::
kAfterDownload:
return HandleModelAvailabilityCheckResult(
execution_context, AIMetrics::AISessionType::kLanguageDetector,
mojom::blink::ModelAvailabilityCheckResult::kDownloadable);
case language_detection::mojom::blink::LanguageDetectionModelStatus::
kNotAvailable:
return HandleModelAvailabilityCheckResult(
execution_context, AIMetrics::AISessionType::kLanguageDetector,
mojom::blink::ModelAvailabilityCheckResult::
kUnavailableLanguageDetectionModelNotAvailable);
}
}
V8AIAvailability AIAvailabilityToV8(AIAvailability availability) {
switch (availability) {
case AIAvailability::kUnavailable:

@ -10,6 +10,7 @@
#include "third_party/blink/renderer/bindings/modules/v8/v8_ai_availability.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/modules/ai/ai_metrics.h"
#include "third_party/blink/renderer/platform/language_detection/language_detection_model.h"
namespace blink {
// These values are persisted to logs. Entries should not be renumbered and
@ -37,6 +38,10 @@ AIAvailability HandleTranslatorAvailabilityCheckResult(
ExecutionContext* execution_context,
mojom::blink::CanCreateTranslatorResult result);
AIAvailability HandleLanguageDetectionModelCheckResult(
ExecutionContext* execution_context,
language_detection::mojom::blink::LanguageDetectionModelStatus result);
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_AI_AI_AVAILABILITY_H_

@ -24,6 +24,8 @@ std::string_view GetAISessionTypeName(AIMetrics::AISessionType session_type) {
return "Summarizer";
case AIMetrics::AISessionType::kTranslator:
return "Translator";
case AIMetrics::AISessionType::kLanguageDetector:
return "LanguageDetector";
}
NOTREACHED();
}

@ -19,7 +19,8 @@ class AIMetrics {
kRewriter = 2,
kSummarizer = 3,
kTranslator = 4,
kMaxValue = kTranslator,
kLanguageDetector = 5,
kMaxValue = kLanguageDetector,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/ai/histograms.xml:SessionType)

@ -7,9 +7,11 @@
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_ai_create_monitor_callback.h"
#include "third_party/blink/renderer/modules/ai/ai.h"
#include "third_party/blink/renderer/modules/ai/ai_availability.h"
#include "third_party/blink/renderer/modules/ai/ai_create_monitor.h"
#include "third_party/blink/renderer/modules/ai/exception_helpers.h"
#include "third_party/blink/renderer/modules/ai/on_device_translation/ai_language_detector.h"
#include "third_party/blink/renderer/platform/language_detection/language_detection_model.h"
namespace blink {
@ -119,6 +121,42 @@ void AILanguageDetectorFactory::Trace(Visitor* visitor) const {
visitor->Trace(language_detection_driver_);
}
ScriptPromise<V8AIAvailability> AILanguageDetectorFactory::availability(
ScriptState* script_state,
ExceptionState& exception_state) {
if (!script_state->ContextIsValid()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"The execution context is not valid.");
return EmptyPromise();
}
ScriptPromiseResolver<V8AIAvailability>* resolver =
MakeGarbageCollected<ScriptPromiseResolver<V8AIAvailability>>(
script_state);
ScriptPromise<V8AIAvailability> promise = resolver->Promise();
ExecutionContext* execution_context = GetExecutionContext();
GetLanguageDetectionDriverRemote()->GetLanguageDetectionModelStatus(
WTF::BindOnce(
[](RejectOnDestructionHelper<V8AIAvailability> resolver_holder,
ExecutionContext* execution_context,
language_detection::mojom::blink::LanguageDetectionModelStatus
result) {
if (!execution_context) {
return;
}
auto resolver(resolver_holder.Take());
AIAvailability availability =
HandleLanguageDetectionModelCheckResult(execution_context,
result);
resolver->Resolve(AIAvailabilityToV8(availability));
},
RejectOnDestructionHelper(resolver),
WrapWeakPersistent(execution_context)));
return promise;
}
ScriptPromise<AILanguageDetector> AILanguageDetectorFactory::create(
ScriptState* script_state,
AILanguageDetectorCreateOptions* options,

@ -7,6 +7,7 @@
#include "third_party/blink/renderer/bindings/modules/v8/v8_ai_language_detector_create_options.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/modules/ai/ai_availability.h"
#include "third_party/blink/renderer/modules/ai/on_device_translation/ai_language_detector.h"
#include "third_party/blink/renderer/modules/ai/on_device_translation/ai_language_detector_capabilities.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@ -29,6 +30,10 @@ class AILanguageDetectorFactory final : public ScriptWrappable,
void Trace(Visitor* visitor) const override;
// Checks the availability of the Language Detection model.
ScriptPromise<V8AIAvailability> availability(ScriptState* script_state,
ExceptionState& exception_state);
// Creates an `AILanguageDetector`, with a model ready to use.
ScriptPromise<AILanguageDetector> create(
ScriptState* script_state,

@ -4,17 +4,23 @@
SecureContext
]
interface AILanguageDetectorFactory {
[
Measure,
CallWith=ScriptState,
RaisesException
]
Promise<AIAvailability> availability();
[
Measure,
CallWith=ScriptState,
RaisesException
]
Promise<AILanguageDetector> create(optional AILanguageDetectorCreateOptions options = {});
// TODO(crbug.com/390459311): Replace with availability.
[
CallWith=ScriptState,
RaisesException
]
// TODO(crbug.com/392185835): Remove capabilities method.
Promise<AILanguageDetectorCapabilities> capabilities();
};

@ -25,6 +25,7 @@ interface AILanguageDetectorCapabilities
method languageAvailable
interface AILanguageDetectorFactory
attribute @@toStringTag
method availability
method capabilities
method constructor
method create

@ -30,6 +30,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] method languageAvailable
[Worker] interface AILanguageDetectorFactory
[Worker] attribute @@toStringTag
[Worker] method availability
[Worker] method capabilities
[Worker] method constructor
[Worker] method create

@ -29,6 +29,7 @@ interface AILanguageDetectorCapabilities
method languageAvailable
interface AILanguageDetectorFactory
attribute @@toStringTag
method availability
method capabilities
method constructor
method create

@ -30,6 +30,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] method languageAvailable
[Worker] interface AILanguageDetectorFactory
[Worker] attribute @@toStringTag
[Worker] method availability
[Worker] method capabilities
[Worker] method constructor
[Worker] method create

@ -25,6 +25,7 @@ chromium-metrics-reviews@google.com.
<!-- LINT.IfChange(SessionType) -->
<variants name="SessionType">
<variant name="LanguageDetector" summary="AI language detector session"/>
<variant name="LanguageModel" summary="AI language model session"/>
<variant name="Rewriter" summary="AI rewriter session"/>
<variant name="Summarizer" summary="AI summarizer session"/>

@ -5929,6 +5929,7 @@ Called by update_use_counter_feature_enum.py.-->
<int value="5326" label="GeolocationWouldSucceedWhenAdScriptInStack"/>
<int value="5327" label="AdScriptInStackOnWatchGeoLocation"/>
<int value="5328" label="DeviceBoundSessionRegistered"/>
<int value="5329" label="V8AILanguageDetectorFactory_Availability_Method"/>
</enum>
<!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom:WebFeature) -->