transformers 0.0.2 copy "transformers: ^0.0.2" to clipboard
transformers: ^0.0.2 copied to clipboard

State-of-the-art Machine Learning for Dart. Run Huggingface Transformers cross-platform on your device, with no need for a server!

example/lib/main.dart

import 'dart:async';
import 'dart:isolate';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:transformers/transformers.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Transformers.ensureInitialized(throwOnFail: true);
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class SimilaritySearchIsolate {
  /// Spawn a new [Isolate] that handles similarity search.
  static Future<SimilaritySearchIsolate> spawnIsolate() async {
    final receivePort = ReceivePort();
    final Stream receivePortStream = receivePort.asBroadcastStream();
    late SendPort sendPort;
    final cls = SimilaritySearchIsolate._();
    final Completer<void> isolateReady = Completer();

    final StreamSubscription subscription = receivePortStream.listen((message) {
      if (message is SendPort) {
        sendPort = message;
        isolateReady.complete();
      }
    });

    final RootIsolateToken rootIsolateToken = ServicesBinding.rootIsolateToken!;
    final isolate = await Isolate.spawn(SimilaritySearchIsolate._create, (receivePort.sendPort, rootIsolateToken));

    await isolateReady.future;
    await subscription.cancel();
    await cls._initClient(isolate, receivePort, receivePortStream, sendPort);
    return cls;
  }

  /// The entrypoint for [Isolate.spawn].
  static Future<void> _create((SendPort sendPort, RootIsolateToken rootIsolateToken) args) async {
    final isolate = SimilaritySearchIsolate._();
    await isolate._initIsolate(args.$1, args.$2);
  }

  // --- Below is shared in both the client and isolate ---
  late final SendPort _sendPort;
  ReceivePort _receivePort = ReceivePort();
  late Stream _receivePortStream;

  SimilaritySearchIsolate._();

  // --- Below is for the client ---

  late final Isolate _isolate;

  Future<void> _initClient(Isolate isolate, ReceivePort receivePort, Stream receivePortStream, SendPort sendPort) async {
    _isolate = isolate;
    _receivePort = receivePort;
    _receivePortStream = receivePortStream;
    _sendPort = sendPort;
  }

  Future<List<({String text, double likelyhood})>> similaritySearch(List<String> texts, String query) async {
    final Completer<void> messageReceived = Completer();
    late List<double> results;

    final StreamSubscription subscription = _receivePortStream.listen((message) {
      if (message is List<double>) {
        results = message;
        messageReceived.complete();
      }
    });

    _sendPort.send({'query': query, 'texts': texts});

    await messageReceived.future;
    await subscription.cancel();

    return results.indexed
        .map((e) => (text: texts[e.$1], likelyhood: e.$2))
        .toList()
      ..sort((a, b) => b.likelyhood.compareTo(a.likelyhood));
  }

  void dispose() {
    _isolate.kill();
    _receivePort.close();
  }

  // --- Below is for the isolate ---

  late final Pipeline _extractor;

  Future<void> _initIsolate(SendPort sendPort, RootIsolateToken rootIsolateToken) async {
    _sendPort = sendPort;
    _sendPort.send(_receivePort.sendPort);

    BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
    await Transformers.ensureInitialized();

    _extractor = await pipeline(
      PipelineType.featureExtraction,
      'onnx-community/Qwen3-Embedding-0.6B-ONNX',
      PretrainedModelOptions(
        dtype: DataType.fp32,
      ),
    );

    _receivePort.listen((message) async {
      final query = message['query'] as String;
      final texts = message['texts'] as List<String>;

      String getDetailedInstruct(String taskDescription, String query) {
        return 'Instruct: $taskDescription\nQuery:$query';
      }

      // Each query must come with a one-sentence instruction that describes the task
      const instruct = "Given a web search query, retrieve relevant passages that are close to the query";
      final queries = [
        getDetailedInstruct(instruct, query),
      ];

      // No need to add instruction for retrieval documents
      final inputTexts = [...queries, ...texts];

      // Extract embeddings for queries and documents
      final Tensor output = await _extractor(inputTexts, FeatureExtractionPipelineOptions(
        pooling: "last_token",
        normalize: true,
      ));
      final scores = await matmul(
        await output.slice([[0, queries.length]]), // Query embeddings
        await (await output.slice([[queries.length, null]])).transpose([1, 0]), // Document embeddings
      );

      _sendPort.send(scores.data.cast<double>());
    });
  }
}

class _MyAppState extends State<MyApp> {
  static const List<String> _defaultTexts = [
    'The quick brown fox jumps over the lazy dog.',
    'A stitch in time saves nine.',
    'Actions speak louder than words.',
    'All that glitters is not gold.',
    'The early bird catches the worm.',
    'Honesty is the best policy.',
    'Laughter is the best medicine.',
    'The pen is mightier than the sword.',
    'There is no time like the present.',
    'Practice makes perfect.',
    'Where there is a will, there is a way.',
    'You can lead a horse to water, but you can\'t make it drink.',
    'The squeaky wheel gets the grease.',
    'Two heads are better than one.',
    'When in Rome, do as the Romans do.',
    'A picture is worth a thousand words.',
    'Beauty is in the eye of the beholder.',
    'Don\'t count your chickens before they hatch.',
    'Every cloud has a silver lining.',
    'Fortune favors the bold.',
    'Good things come to those who wait.',
    'Hope for the best, but prepare for the worst.',
    'If it ain\'t broke, don\'t fix it.',
    'Knowledge is power.',
    'Look before you leap.'
  ];

  late final SimilaritySearchIsolate _searchIsolate;
  bool _ready = false;
  final TextEditingController _searchController = TextEditingController();
  final TextEditingController _addTextController = TextEditingController();
  final TextEditingController _resultsController = TextEditingController();

  late List<String> _texts = _defaultTexts.toList();

  @override
  void initState() {
    super.initState();
    _spawnIsolate();
  }

  @override
  void dispose() {
    _searchIsolate.dispose();
    _searchController.dispose();
    _addTextController.dispose();
    _resultsController.dispose();
    super.dispose();
  }

  void _spawnIsolate() async {
    _searchIsolate = await SimilaritySearchIsolate.spawnIsolate();
    setState(() {
      _ready = true;
    });
  }

  void _runSimilaritySearch() async {
    final query = _searchController.text;
    if (query.isEmpty) return;
    if (!_ready) {
      setState(() {
        _resultsController.text = 'Process is starting up...';
      });
      return;
    }

    final results = await _searchIsolate.similaritySearch(_texts, query);

    setState(() {
      _resultsController.text = results.join('\n');
    });
  }

  void _addText() {
    if (_addTextController.text.isNotEmpty) {
      setState(() {
        _texts.add(_addTextController.text);
        _addTextController.clear();
      });
      _runSimilaritySearch();
    }
  }

  void _removeLastText() {
    if (_texts.isNotEmpty) {
      setState(() {
        _texts.removeLast();
      });
      _runSimilaritySearch();
    }
  }

  void _resetTexts() {
    setState(() {
      _texts = _defaultTexts.toList();
    });
    _runSimilaritySearch();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Transformers Similarity Search'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Expanded(
                    child: TextField(
                      controller: _searchController,
                      decoration: const InputDecoration(
                        labelText: 'Enter text for similarity search',
                      ),
                    ),
                  ),
                  const SizedBox(width: 8.0),
                  ElevatedButton(
                    onPressed: _runSimilaritySearch,
                    child: const Text('Search'),
                  ),
                ],
              ),
              const SizedBox(height: 16.0),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Expanded(
                    child: TextField(
                      controller: _addTextController,
                      decoration: const InputDecoration(
                        labelText: 'Enter text to add',
                      ),
                    ),
                  ),
                  const SizedBox(width: 8.0),
                  ElevatedButton(
                    onPressed: _addText,
                    child: const Text('Add'),
                  ),
                  const SizedBox(width: 8.0),
                  ElevatedButton(
                    onPressed: _removeLastText,
                    child: const Text('Remove Last'),
                  ),
                  const SizedBox(width: 8.0),
                  ElevatedButton(
                    onPressed: _resetTexts,
                    child: const Text('Reset'),
                  ),
                ],
              ),
              const SizedBox(height: 16.0),
              const Text(
                'Items for Similarity Search:',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              Expanded(
                child: ListView.builder(
                  itemCount: _texts.length,
                  itemBuilder: (context, index) {
                    return Card(
                      child: ListTile(
                        title: Text(_texts[index]),
                      ),
                    );
                  },
                ),
              ),
              const SizedBox(height: 16.0),
              const Text(
                'Similarity Search Results:',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              Expanded(
                child: TextField(
                  controller: _resultsController,
                  readOnly: true,
                  maxLines: null,
                  decoration: const InputDecoration(
                    border: OutlineInputBorder(),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
0
likes
140
points
128
downloads

Publisher

verified publishernathankolbas.com

Weekly Downloads

State-of-the-art Machine Learning for Dart. Run Huggingface Transformers cross-platform on your device, with no need for a server!

Repository (GitHub)
View/report issues

Topics

#nlp #machine-learning #deep-learning #transformer #llm

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

crypto, dio, flutter, flutter_onnxruntime, flutter_web_plugins, huggingface_hub, image, jinja_minimal, path, path_provider, plugin_platform_interface, unorm_dart, web

More

Packages that depend on transformers

Packages that implement transformers