Skip to content
Draft

wip #123

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion packages/genkit_chrome/lib/src/chrome_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ extension type LanguageModelOptions._(JSObject _) implements JSObject {
num? topK,
String? systemPrompt,
List<LanguageModelInitialPrompt>? initialPrompts,
List<LanguageModelExpectedInput>? expectedInputs,
List<LanguageModelExpectedOutput>? expectedOutputs,
}) {
final options = JSObject();
if (temperature != null) options['temperature'] = temperature.toJS;
Expand All @@ -57,13 +59,37 @@ extension type LanguageModelOptions._(JSObject _) implements JSObject {
if (initialPrompts != null && initialPrompts.isNotEmpty) {
options['initialPrompts'] = initialPrompts.toJS;
}
if (expectedInputs != null && expectedInputs.isNotEmpty) {
options['expectedInputs'] = expectedInputs.toJS;
}
if (expectedOutputs != null && expectedOutputs.isNotEmpty) {
options['expectedOutputs'] = expectedOutputs.toJS;
}
return options as LanguageModelOptions;
}

external num? get temperature;
external num? get topK;
external String? get systemPrompt;
external JSArray<LanguageModelInitialPrompt> get initialPrompts;
external JSArray<LanguageModelInitialPrompt>? get initialPrompts;
external JSArray<LanguageModelExpectedInput>? get expectedInputs;
external JSArray<LanguageModelExpectedOutput>? get expectedOutputs;
}

@JS()
extension type LanguageModelExpectedInput._(JSObject _) implements JSObject {
external factory LanguageModelExpectedInput({
String type,
JSArray<JSString>? languages,
});
}

@JS()
extension type LanguageModelExpectedOutput._(JSObject _) implements JSObject {
external factory LanguageModelExpectedOutput({
String type,
JSArray<JSString>? languages,
});
}

@JS()
Expand Down
48 changes: 48 additions & 0 deletions packages/genkit_chrome/lib/src/chrome_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ class ChromeModel extends Model<LanguageModelOptions> {
temperature: config['temperature'] as num? ?? defaultOptions?.temperature,
topK: config['topK'] as num? ?? defaultOptions?.topK,
initialPrompts: initialPrompts,
expectedInputs:
_parseExpectedInputs(config) ??
defaultOptions?.expectedInputs?.toDart,
expectedOutputs:
_parseExpectedOutputs(config) ??
defaultOptions?.expectedOutputs?.toDart,
);

final session = await _languageModel.create(options).toDart;
Expand Down Expand Up @@ -164,3 +170,45 @@ To enable local AI in Chrome (v128+):
}
return languageModelImpl!;
}

List<LanguageModelExpectedInput>? _parseExpectedInputs(
Map<String, dynamic> config,
) {
final inputs = config['expectedInputs'];
if (inputs is List) {
return inputs.map((e) {
if (e is Map) {
return LanguageModelExpectedInput(
type: e['type'] as String? ?? 'text',
languages: (e['languages'] as List?)
?.map((l) => (l as String).toJS)
.toList()
.toJS,
);
}
throw ArgumentError('Invalid expectedInput: $e');
}).toList();
}
return null;
}

List<LanguageModelExpectedOutput>? _parseExpectedOutputs(
Map<String, dynamic> config,
) {
final outputs = config['expectedOutputs'];
if (outputs is List) {
return outputs.map((e) {
if (e is Map) {
return LanguageModelExpectedOutput(
type: e['type'] as String? ?? 'text',
languages: (e['languages'] as List?)
?.map((l) => (l as String).toJS)
.toList()
.toJS,
);
}
throw ArgumentError('Invalid expectedOutput: $e');
}).toList();
}
return null;
}
Comment on lines +174 to +214
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The functions _parseExpectedInputs and _parseExpectedOutputs are nearly identical. To improve maintainability and reduce code duplication, you can refactor them into a single generic helper function that handles the parsing logic for both.

List<T>? _parseExpected<T>({
  required Map<String, dynamic> config,
  required String key,
  required T Function({String type, JSArray<JSString>? languages}) constructor,
}) {
  final items = config[key];
  if (items is List) {
    return items.map((e) {
      if (e is Map) {
        return constructor(
          type: e['type'] as String? ?? 'text',
          languages: (e['languages'] as List?)
              ?.map((l) => (l as String).toJS)
              .toList()
              .toJS,
        );
      }
      throw ArgumentError('Invalid item in $key: $e');
    }).toList();
  }
  return null;
}

List<LanguageModelExpectedInput>? _parseExpectedInputs(
  Map<String, dynamic> config,
) {
  return _parseExpected(
    config: config,
    key: 'expectedInputs',
    constructor: LanguageModelExpectedInput.new,
  );
}

List<LanguageModelExpectedOutput>? _parseExpectedOutputs(
  Map<String, dynamic> config,
) {
  return _parseExpected(
    config: config,
    key: 'expectedOutputs',
    constructor: LanguageModelExpectedOutput.new,
  );
}

14 changes: 14 additions & 0 deletions testapps/genkit_chrome_sample/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,20 @@ <h1>Genkit Chrome Sample</h1>
<input type="number" id="topK" min="1" step="1" placeholder="default">
</div>
</div>
<div class="setting-group">
<label for="expectedInputLanguages">
Expected Input Languages
<span class="help-icon" title="Comma-separated language codes (e.g. en, es).">?</span>
</label>
<input type="text" id="expectedInputLanguages" placeholder="e.g. en, es">
</div>
<div class="setting-group">
<label for="expectedOutputLanguages">
Expected Output Languages
<span class="help-icon" title="Comma-separated language codes (e.g. ja).">?</span>
</label>
<input type="text" id="expectedOutputLanguages" placeholder="e.g. ja">
</div>
<div id="validation-error" class="validation-error"></div>
</div>
</details>
Expand Down
30 changes: 30 additions & 0 deletions testapps/genkit_chrome_sample/web/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ final _systemPromptInput =
final _temperatureInput =
web.document.querySelector('#temperature') as web.HTMLInputElement;
final _topKInput = web.document.querySelector('#topK') as web.HTMLInputElement;
final _expectedInputLanguages =
web.document.querySelector('#expectedInputLanguages')
as web.HTMLInputElement;
final _expectedOutputLanguages =
web.document.querySelector('#expectedOutputLanguages')
as web.HTMLInputElement;

final List<Message> _history = [];

Expand Down Expand Up @@ -189,6 +195,16 @@ Future<void> _submit(Genkit ai) async {
final systemPrompt = _systemPromptInput.value.trim();
final temperature = double.tryParse(_temperatureInput.value);
final topK = int.tryParse(_topKInput.value);
final inputLangs = _expectedInputLanguages.value
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
final outputLangs = _expectedOutputLanguages.value
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
Comment on lines +198 to +207
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's duplicated logic for parsing the comma-separated language strings from the input fields. You can extract this into a local helper function to avoid repetition and make the code cleaner.

Suggested change
final inputLangs = _expectedInputLanguages.value
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
final outputLangs = _expectedOutputLanguages.value
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
List<String> parseLangs(String value) => value
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
final inputLangs = parseLangs(_expectedInputLanguages.value);
final outputLangs = parseLangs(_expectedOutputLanguages.value);


// Update system prompt in history if it changed or is new
// For simplicity in this sample, we'll just prepend it if it exists
Expand All @@ -207,6 +223,12 @@ Future<void> _submit(Genkit ai) async {
if (topK != null && _defaultTopK != null && topK != _defaultTopK) {
settingsList.add('Top K: $topK');
}
if (inputLangs.isNotEmpty) {
settingsList.add('Input Langs: ${inputLangs.join(", ")}');
}
if (outputLangs.isNotEmpty) {
settingsList.add('Output Langs: ${outputLangs.join(", ")}');
}

final contentDiv = _appendMessage(
'Model',
Expand All @@ -229,6 +251,14 @@ Future<void> _submit(Genkit ai) async {
if (systemPrompt.isNotEmpty) 'systemPrompt': systemPrompt,
'temperature': ?temperature,
'topK': ?topK,
if (inputLangs.isNotEmpty)
'expectedInputs': [
{'type': 'text', 'languages': inputLangs},
],
if (outputLangs.isNotEmpty)
'expectedOutputs': [
{'type': 'text', 'languages': outputLangs},
],
},
onChunk: (chunk) {
if (buffer.isEmpty) {
Expand Down
Loading