Skip to content

Commit 257b366

Browse files
committed
feat: add language config options to Chrome plugin
1 parent 94e7b21 commit 257b366

File tree

8 files changed

+141
-12
lines changed

8 files changed

+141
-12
lines changed

packages/genkit_chrome/lib/src/chrome_interop.dart

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension type LanguageModelFactory._(JSObject _) implements JSObject {
2323
/// Returns the availability of the language model.
2424
///
2525
/// Returns a promise that resolves to "readily", "after-download", or "no".
26-
external JSPromise<JSString> availability();
26+
external JSPromise<JSString> availability([LanguageModelOptions? options]);
2727

2828
/// Creates a new session with the language model.
2929
external JSPromise<LanguageModel> create([LanguageModelOptions? options]);
@@ -49,6 +49,8 @@ extension type LanguageModelOptions._(JSObject _) implements JSObject {
4949
num? topK,
5050
String? systemPrompt,
5151
List<LanguageModelInitialPrompt>? initialPrompts,
52+
List<LanguageModelExpectedInput>? expectedInputs,
53+
List<LanguageModelExpectedOutput>? expectedOutputs,
5254
}) {
5355
final options = JSObject();
5456
if (temperature != null) options['temperature'] = temperature.toJS;
@@ -57,13 +59,37 @@ extension type LanguageModelOptions._(JSObject _) implements JSObject {
5759
if (initialPrompts != null && initialPrompts.isNotEmpty) {
5860
options['initialPrompts'] = initialPrompts.toJS;
5961
}
62+
if (expectedInputs != null && expectedInputs.isNotEmpty) {
63+
options['expectedInputs'] = expectedInputs.toJS;
64+
}
65+
if (expectedOutputs != null && expectedOutputs.isNotEmpty) {
66+
options['expectedOutputs'] = expectedOutputs.toJS;
67+
}
6068
return options as LanguageModelOptions;
6169
}
6270

6371
external num? get temperature;
6472
external num? get topK;
6573
external String? get systemPrompt;
66-
external JSArray<LanguageModelInitialPrompt> get initialPrompts;
74+
external JSArray<LanguageModelInitialPrompt>? get initialPrompts;
75+
external JSArray<LanguageModelExpectedInput>? get expectedInputs;
76+
external JSArray<LanguageModelExpectedOutput>? get expectedOutputs;
77+
}
78+
79+
@JS()
80+
extension type LanguageModelExpectedInput._(JSObject _) implements JSObject {
81+
external factory LanguageModelExpectedInput({
82+
String type,
83+
JSArray<JSString>? languages,
84+
});
85+
}
86+
87+
@JS()
88+
extension type LanguageModelExpectedOutput._(JSObject _) implements JSObject {
89+
external factory LanguageModelExpectedOutput({
90+
String type,
91+
JSArray<JSString>? languages,
92+
});
6793
}
6894

6995
@JS()

packages/genkit_chrome/lib/src/chrome_model.dart

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ class ChromeModel extends Model<LanguageModelOptions> {
4949
status: StatusCodes.INVALID_ARGUMENT,
5050
);
5151
}
52-
// Availability check
53-
await _ensureAvailability();
5452

5553
final config = req.config ?? const {};
5654
final systemPrompt =
@@ -80,8 +78,17 @@ class ChromeModel extends Model<LanguageModelOptions> {
8078
temperature: config['temperature'] as num? ?? defaultOptions?.temperature,
8179
topK: config['topK'] as num? ?? defaultOptions?.topK,
8280
initialPrompts: initialPrompts,
81+
expectedInputs:
82+
_parseExpectedInputs(config) ??
83+
defaultOptions?.expectedInputs?.toDart,
84+
expectedOutputs:
85+
_parseExpectedOutputs(config) ??
86+
defaultOptions?.expectedOutputs?.toDart,
8387
);
8488

89+
// Availability check
90+
await _ensureAvailability(options);
91+
8592
final session = await _languageModel.create(options).toDart;
8693

8794
try {
@@ -134,18 +141,15 @@ class ChromeModel extends Model<LanguageModelOptions> {
134141
}
135142
}
136143

137-
bool _availabilityChecked = false;
138-
139-
Future<void> _ensureAvailability() async {
140-
if (_availabilityChecked) return;
141-
final availability = (await _languageModel.availability().toDart).toDart;
144+
Future<void> _ensureAvailability([LanguageModelOptions? options]) async {
145+
final availability =
146+
(await _languageModel.availability(options).toDart).toDart;
142147
if (availability == 'no') {
143148
throw GenkitException(
144149
'Chrome AI is not available.',
145150
status: StatusCodes.UNAVAILABLE,
146151
);
147152
}
148-
_availabilityChecked = true;
149153
}
150154

151155
LanguageModelFactory get _languageModel {
@@ -164,3 +168,48 @@ To enable local AI in Chrome (v128+):
164168
}
165169
return languageModelImpl!;
166170
}
171+
172+
List<T>? _parseExpected<T>({
173+
required Map<String, dynamic> config,
174+
required String key,
175+
required T Function({String type, JSArray<JSString>? languages}) constructor,
176+
}) {
177+
final items = config[key];
178+
if (items is List) {
179+
return items.map((e) {
180+
if (e is Map) {
181+
return constructor(
182+
type: e['type'] as String? ?? 'text',
183+
languages: (e['languages'] as List?)
184+
?.map((l) => (l as String).toJS)
185+
.toList()
186+
.toJS,
187+
);
188+
}
189+
throw ArgumentError('Invalid item in $key: $e');
190+
}).toList();
191+
}
192+
return null;
193+
}
194+
195+
List<LanguageModelExpectedInput>? _parseExpectedInputs(
196+
Map<String, dynamic> config,
197+
) {
198+
return _parseExpected(
199+
config: config,
200+
key: 'expectedInputs',
201+
constructor: ({String type = '', JSArray<JSString>? languages}) =>
202+
LanguageModelExpectedInput(type: type, languages: languages),
203+
);
204+
}
205+
206+
List<LanguageModelExpectedOutput>? _parseExpectedOutputs(
207+
Map<String, dynamic> config,
208+
) {
209+
return _parseExpected(
210+
config: config,
211+
key: 'expectedOutputs',
212+
constructor: ({String type = '', JSArray<JSString>? languages}) =>
213+
LanguageModelExpectedOutput(type: type, languages: languages),
214+
);
215+
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ workspace:
1818
- testapps/agentic_patterns
1919
- testapps/ai_features
2020
- testapps/basic
21-
- testapps/genkit_chrome_sample
21+
- testapps/chrome_example
2222
- testapps/mcp_server
2323

2424
dev_dependencies:
File renamed without changes.

testapps/genkit_chrome_sample/web/index.html renamed to testapps/chrome_example/web/index.html

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@
216216
}
217217

218218
input[type="number"],
219-
#settings-panel textarea {
219+
#settings-panel textarea,
220+
#settings-panel select {
220221
padding: 8px;
221222
border: 1px solid #ddd;
222223
border-radius: 8px;
@@ -285,6 +286,28 @@ <h1>Genkit Chrome Sample</h1>
285286
<input type="number" id="topK" min="1" step="1" placeholder="default">
286287
</div>
287288
</div>
289+
<div class="setting-group">
290+
<label for="expectedInputLanguages">
291+
Expected Input Languages
292+
<span class="help-icon" title="Select expected input languages">?</span>
293+
</label>
294+
<select id="expectedInputLanguages" multiple size="3">
295+
<option value="en" selected>English (en)</option>
296+
<option value="es">Spanish (es)</option>
297+
<option value="ja">Japanese (ja)</option>
298+
</select>
299+
</div>
300+
<div class="setting-group">
301+
<label for="expectedOutputLanguages">
302+
Expected Output Languages
303+
<span class="help-icon" title="Select expected output languages">?</span>
304+
</label>
305+
<select id="expectedOutputLanguages" multiple size="3">
306+
<option value="en" selected>English (en)</option>
307+
<option value="es">Spanish (es)</option>
308+
<option value="ja">Japanese (ja)</option>
309+
</select>
310+
</div>
288311
<div id="validation-error" class="validation-error"></div>
289312
</div>
290313
</details>

testapps/genkit_chrome_sample/web/main.dart renamed to testapps/chrome_example/web/main.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ final _systemPromptInput =
158158
final _temperatureInput =
159159
web.document.querySelector('#temperature') as web.HTMLInputElement;
160160
final _topKInput = web.document.querySelector('#topK') as web.HTMLInputElement;
161+
final _expectedInputLanguages =
162+
web.document.querySelector('#expectedInputLanguages')
163+
as web.HTMLSelectElement;
164+
final _expectedOutputLanguages =
165+
web.document.querySelector('#expectedOutputLanguages')
166+
as web.HTMLSelectElement;
161167

162168
final List<Message> _history = [];
163169

@@ -189,6 +195,17 @@ Future<void> _submit(Genkit ai) async {
189195
final systemPrompt = _systemPromptInput.value.trim();
190196
final temperature = double.tryParse(_temperatureInput.value);
191197
final topK = int.tryParse(_topKInput.value);
198+
List<String> parseSelectedLangs(web.HTMLSelectElement selectElement) {
199+
final selected = <String>[];
200+
final options = selectElement.selectedOptions;
201+
for (var i = 0; i < options.length; i++) {
202+
selected.add((options.item(i) as web.HTMLOptionElement).value);
203+
}
204+
return selected;
205+
}
206+
207+
final inputLangs = parseSelectedLangs(_expectedInputLanguages);
208+
final outputLangs = parseSelectedLangs(_expectedOutputLanguages);
192209

193210
// Update system prompt in history if it changed or is new
194211
// For simplicity in this sample, we'll just prepend it if it exists
@@ -207,6 +224,12 @@ Future<void> _submit(Genkit ai) async {
207224
if (topK != null && _defaultTopK != null && topK != _defaultTopK) {
208225
settingsList.add('Top K: $topK');
209226
}
227+
if (inputLangs.isNotEmpty) {
228+
settingsList.add('Input Langs: ${inputLangs.join(", ")}');
229+
}
230+
if (outputLangs.isNotEmpty) {
231+
settingsList.add('Output Langs: ${outputLangs.join(", ")}');
232+
}
210233

211234
final contentDiv = _appendMessage(
212235
'Model',
@@ -229,6 +252,14 @@ Future<void> _submit(Genkit ai) async {
229252
if (systemPrompt.isNotEmpty) 'systemPrompt': systemPrompt,
230253
'temperature': ?temperature,
231254
'topK': ?topK,
255+
if (inputLangs.isNotEmpty)
256+
'expectedInputs': [
257+
{'type': 'text', 'languages': inputLangs},
258+
],
259+
if (outputLangs.isNotEmpty)
260+
'expectedOutputs': [
261+
{'type': 'text', 'languages': outputLangs},
262+
],
232263
},
233264
onChunk: (chunk) {
234265
if (buffer.isEmpty) {

0 commit comments

Comments
 (0)