diff --git a/.github/workflows/native_doc_dartifier.yaml b/.github/workflows/native_doc_dartifier.yaml index 8afac77b5..5cfa564cd 100644 --- a/.github/workflows/native_doc_dartifier.yaml +++ b/.github/workflows/native_doc_dartifier.yaml @@ -31,6 +31,10 @@ jobs: - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 with: channel: ${{ matrix.sdk }} + + - name: Download ObjectBox DB .so file + run: bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh) + - id: install name: Install dependencies run: flutter pub get diff --git a/pkgs/native_doc_dartifier/example/native_doc_dartifier_example.dart b/pkgs/native_doc_dartifier/example/dartify_example.dart similarity index 100% rename from pkgs/native_doc_dartifier/example/native_doc_dartifier_example.dart rename to pkgs/native_doc_dartifier/example/dartify_example.dart diff --git a/pkgs/native_doc_dartifier/example/dartify_rag_example.dart b/pkgs/native_doc_dartifier/example/dartify_rag_example.dart new file mode 100644 index 000000000..deb9cf115 --- /dev/null +++ b/pkgs/native_doc_dartifier/example/dartify_rag_example.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:native_doc_dartifier/src/context.dart'; +import 'package:native_doc_dartifier/src/dartify_code.dart'; +import 'package:native_doc_dartifier/src/populate_rag.dart'; + +void main() async { + const code = '''public void onClick() { + ImageCapture.OutputFileOptions outputFileOptions = + new ImageCapture.OutputFileOptions.Builder(new File("...")).build(); + imageCapture.takePicture(outputFileOptions, cameraExecutor, + new ImageCapture.OnImageSavedCallback() { + @Override + public void onImageSaved(ImageCapture.OutputFileResults outputFileResults) { + // insert your code here. + } + @Override + public void onError(ImageCaptureException error) { + // insert your code here. + } + } + ); +} +'''; + + final bindingsFile = File('example/camerax.dart'); + + final bindingsPath = bindingsFile.absolute.path; + + // Populate RAG with relevant information from the bindings file + // This step is crucial and must be done only once before using RAG + await populateRAG(Directory.current.path, bindingsPath); + + try { + final context = await Context.create( + Directory.current.path, + bindingsPath, + // Indicate that we are using RAG + usingRag: true, + ); + final dartCode = await dartifyNativeCode(code, context); + print(dartCode); + } catch (e) { + stderr.writeln('Error: $e'); + exit(1); + } +} diff --git a/pkgs/native_doc_dartifier/lib/native_doc_dartifier.dart b/pkgs/native_doc_dartifier/lib/native_doc_dartifier.dart deleted file mode 100644 index 9e0ed949a..000000000 --- a/pkgs/native_doc_dartifier/lib/native_doc_dartifier.dart +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// TODO: Export any libraries intended for clients of this package. diff --git a/pkgs/native_doc_dartifier/lib/objectbox-model.json b/pkgs/native_doc_dartifier/lib/objectbox-model.json new file mode 100644 index 000000000..fca7b38ef --- /dev/null +++ b/pkgs/native_doc_dartifier/lib/objectbox-model.json @@ -0,0 +1,44 @@ +{ + "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", + "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", + "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", + "entities": [ + { + "id": "1:7960413809486009791", + "lastPropertyId": "3:8923258437156876593", + "name": "ClassSummaryRAGModel", + "properties": [ + { + "id": "1:8829639415366563528", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:3000549143352790148", + "name": "summary", + "type": 9 + }, + { + "id": "3:8923258437156876593", + "name": "embeddings", + "indexId": "1:6912840214801688687", + "type": 28, + "flags": 8 + } + ], + "relations": [] + } + ], + "lastEntityId": "1:7960413809486009791", + "lastIndexId": "1:6912840214801688687", + "lastRelationId": "0:0", + "lastSequenceId": "0:0", + "modelVersion": 5, + "modelVersionParserMinimum": 5, + "retiredEntityUids": [], + "retiredIndexUids": [], + "retiredPropertyUids": [], + "retiredRelationUids": [], + "version": 1 +} \ No newline at end of file diff --git a/pkgs/native_doc_dartifier/lib/objectbox.g.dart b/pkgs/native_doc_dartifier/lib/objectbox.g.dart new file mode 100644 index 000000000..3fb257ae8 --- /dev/null +++ b/pkgs/native_doc_dartifier/lib/objectbox.g.dart @@ -0,0 +1,157 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// This code was generated by ObjectBox. To update it run the generator again +// with `dart run build_runner build`. +// See also https://docs.objectbox.io/getting-started#generate-objectbox-code + +// ignore_for_file: camel_case_types, depend_on_referenced_packages +// coverage:ignore-file + +import 'dart:typed_data'; + +import 'package:flat_buffers/flat_buffers.dart' as fb; +import 'package:objectbox/internal.dart' + as obx_int; // generated code can access "internal" functionality +import 'package:objectbox/objectbox.dart' as obx; + +import 'src/rag_models.dart'; + +export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file + +final _entities = [ + obx_int.ModelEntity( + id: const obx_int.IdUid(1, 7960413809486009791), + name: 'ClassSummaryRAGModel', + lastPropertyId: const obx_int.IdUid(3, 8923258437156876593), + flags: 0, + properties: [ + obx_int.ModelProperty( + id: const obx_int.IdUid(1, 8829639415366563528), + name: 'id', + type: 6, + flags: 1, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(2, 3000549143352790148), + name: 'summary', + type: 9, + flags: 0, + ), + obx_int.ModelProperty( + id: const obx_int.IdUid(3, 8923258437156876593), + name: 'embeddings', + type: 28, + flags: 8, + indexId: const obx_int.IdUid(1, 6912840214801688687), + hnswParams: obx_int.ModelHnswParams(dimensions: 3072, distanceType: 2), + ), + ], + relations: [], + backlinks: [], + ), +]; + +/// Shortcut for [obx.Store.new] that passes [getObjectBoxModel] and for Flutter +/// apps by default a [directory] using `defaultStoreDirectory()` from the +/// ObjectBox Flutter library. +/// +/// Note: for desktop apps it is recommended to specify a unique [directory]. +/// +/// See [obx.Store.new] for an explanation of all parameters. +/// +/// For Flutter apps, also calls `loadObjectBoxLibraryAndroidCompat()` from +/// the ObjectBox Flutter library to fix loading the native ObjectBox library +/// on Android 6 and older. +obx.Store openStore({ + String? directory, + int? maxDBSizeInKB, + int? maxDataSizeInKB, + int? fileMode, + int? maxReaders, + bool queriesCaseSensitiveDefault = true, + String? macosApplicationGroup, +}) => obx.Store( + getObjectBoxModel(), + directory: directory, + maxDBSizeInKB: maxDBSizeInKB, + maxDataSizeInKB: maxDataSizeInKB, + fileMode: fileMode, + maxReaders: maxReaders, + queriesCaseSensitiveDefault: queriesCaseSensitiveDefault, + macosApplicationGroup: macosApplicationGroup, +); + +/// Returns the ObjectBox model definition for this project for use with +/// [obx.Store.new]. +obx_int.ModelDefinition getObjectBoxModel() { + final model = obx_int.ModelInfo( + entities: _entities, + lastEntityId: const obx_int.IdUid(1, 7960413809486009791), + lastIndexId: const obx_int.IdUid(1, 6912840214801688687), + lastRelationId: const obx_int.IdUid(0, 0), + lastSequenceId: const obx_int.IdUid(0, 0), + retiredEntityUids: const [], + retiredIndexUids: const [], + retiredPropertyUids: const [], + retiredRelationUids: const [], + modelVersion: 5, + modelVersionParserMinimum: 5, + version: 1, + ); + + final bindings = >{ + ClassSummaryRAGModel: obx_int.EntityDefinition( + model: _entities[0], + toOneRelations: (ClassSummaryRAGModel object) => [], + toManyRelations: (ClassSummaryRAGModel object) => {}, + getId: (ClassSummaryRAGModel object) => object.id, + setId: (ClassSummaryRAGModel object, int id) { + object.id = id; + }, + objectToFB: (ClassSummaryRAGModel object, fb.Builder fbb) { + final summaryOffset = fbb.writeString(object.summary); + final embeddingsOffset = fbb.writeListFloat32(object.embeddings); + fbb.startTable(4); + fbb.addInt64(0, object.id); + fbb.addOffset(1, summaryOffset); + fbb.addOffset(2, embeddingsOffset); + fbb.finish(fbb.endTable()); + return object.id; + }, + objectFromFB: (obx.Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final summaryParam = const fb.StringReader( + asciiOptimization: true, + ).vTableGet(buffer, rootOffset, 6, ''); + final embeddingsParam = const fb.ListReader( + fb.Float32Reader(), + lazy: false, + ).vTableGet(buffer, rootOffset, 8, []); + final object = ClassSummaryRAGModel(summaryParam, embeddingsParam) + ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); + + return object; + }, + ), + }; + + return obx_int.ModelDefinition(model, bindings); +} + +/// [ClassSummaryRAGModel] entity fields to define ObjectBox queries. +class ClassSummaryRAGModel_ { + /// See [ClassSummaryRAGModel.id]. + static final id = obx.QueryIntegerProperty( + _entities[0].properties[0], + ); + + /// See [ClassSummaryRAGModel.summary]. + static final summary = obx.QueryStringProperty( + _entities[0].properties[1], + ); + + /// See [ClassSummaryRAGModel.embeddings]. + static final embeddings = obx.QueryHnswProperty( + _entities[0].properties[2], + ); +} diff --git a/pkgs/native_doc_dartifier/lib/src/ast.dart b/pkgs/native_doc_dartifier/lib/src/ast.dart index 4cb5244d2..e558b5705 100644 --- a/pkgs/native_doc_dartifier/lib/src/ast.dart +++ b/pkgs/native_doc_dartifier/lib/src/ast.dart @@ -174,6 +174,26 @@ class PackageSummary { } return buffer.toString(); } + + List getRAGSummaries() { + final summaries = []; + final topLevelDeclerations = StringBuffer(); + + if (packageName.isNotEmpty) { + topLevelDeclerations.writeln('// From: $packageName'); + } + for (final function in topLevelFunctions) { + topLevelDeclerations.writeln('$function;'); + } + for (final variable in topLevelVariables) { + topLevelDeclerations.writeln('$variable;'); + } + + summaries.add(topLevelDeclerations.toString()); + summaries.addAll(classesSummaries.map((c) => c.toDartLikeRepresentation())); + + return summaries; + } } class LibraryClassSummary { diff --git a/pkgs/native_doc_dartifier/lib/src/context.dart b/pkgs/native_doc_dartifier/lib/src/context.dart index d42394dbe..6314b4c00 100644 --- a/pkgs/native_doc_dartifier/lib/src/context.dart +++ b/pkgs/native_doc_dartifier/lib/src/context.dart @@ -14,8 +14,10 @@ import 'package:path/path.dart' as p; import 'ast.dart'; import 'public_abstractor.dart'; +import 'rag.dart'; class Context { + late final RAG? rag; final String projectAbsolutePath; final String bindingsFileAbsolutePath; final List importedPackages = []; @@ -29,12 +31,14 @@ class Context { static Future create( String projectAbsolutePath, - String bindingsFileAbsolutePath, - ) async { + String bindingsFileAbsolutePath, { + bool usingRag = false, + }) async { final context = Context._( projectAbsolutePath: projectAbsolutePath, bindingsFileAbsolutePath: bindingsFileAbsolutePath, ); + context.rag = usingRag ? await RAG.create() : null; await context._init(); return context; } @@ -46,15 +50,17 @@ class Context { exit(1); } - // Get the bindings file summary - final abstractor = PublicAbstractor(); - parseString( - content: await bindingsFile.readAsString(), - ).unit.visitChildren(abstractor); - bindingsSummary.addAll(abstractor.getBindingsClassesSummary()); + if (rag == null) { + // Get the bindings file summary + final abstractor = PublicAbstractor(); + parseString( + content: await bindingsFile.readAsString(), + ).unit.visitChildren(abstractor); + bindingsSummary.addAll(abstractor.getBindingsClassesSummary()); - // Get the packages classes summary, that are imported in the bindings file - await _getLibrariesSummary(projectAbsolutePath, bindingsFileAbsolutePath); + // Get packages classes summary, that are imported in the bindings file + await _getLibrariesSummary(projectAbsolutePath, bindingsFileAbsolutePath); + } } Future _getLibrariesSummary( @@ -155,14 +161,24 @@ class Context { return; } - String toDartLikeRepresentation() { + /// It will return the full context, + /// If [rag] is null or the given [querySnippet] is empty. + Future toDartLikeRepresentation(String querySnippet) async { final buffer = StringBuffer(); - for (final classSummary in bindingsSummary) { - buffer.writeln(classSummary.toDartLikeRepresentation()); - } - for (final packageSummary in packageSummaries) { - buffer.writeln(packageSummary.toDartLikeRepresentation()); + if (rag != null && querySnippet.isNotEmpty) { + final documents = await rag!.queryRAG(querySnippet); + for (final classSummary in documents) { + buffer.writeln(classSummary); + } + } else { + for (final classSummary in bindingsSummary) { + buffer.writeln(classSummary.toDartLikeRepresentation()); + } + for (final packageSummary in packageSummaries) { + buffer.writeln(packageSummary.toDartLikeRepresentation()); + } } + final dartLikeRepresentation = buffer.toString().replaceAll('jni\$_.', ''); return dartLikeRepresentation; } diff --git a/pkgs/native_doc_dartifier/lib/src/dartify_code.dart b/pkgs/native_doc_dartifier/lib/src/dartify_code.dart index 5fdc2bbe6..a9502c07f 100644 --- a/pkgs/native_doc_dartifier/lib/src/dartify_code.dart +++ b/pkgs/native_doc_dartifier/lib/src/dartify_code.dart @@ -32,7 +32,7 @@ Future dartifyNativeCode(String sourceCode, Context context) async { final translatePrompt = TranslatePrompt( sourceCode, - context.toDartLikeRepresentation(), + await context.toDartLikeRepresentation(sourceCode), ); final chatSession = model.startChat(); diff --git a/pkgs/native_doc_dartifier/lib/src/populate_rag.dart b/pkgs/native_doc_dartifier/lib/src/populate_rag.dart new file mode 100644 index 000000000..d1b654c4f --- /dev/null +++ b/pkgs/native_doc_dartifier/lib/src/populate_rag.dart @@ -0,0 +1,34 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'context.dart'; +import 'rag.dart'; + +Future populateRAG( + String projectAbsolutePath, + String bindingsFileAbsolutePath, +) async { + final context = await Context.create( + projectAbsolutePath, + bindingsFileAbsolutePath, + ); + + final listOfSummaries = []; + + final bindingsClassSummary = + context.bindingsSummary.map((c) => c.toDartLikeRepresentation()).toList(); + + listOfSummaries.addAll(bindingsClassSummary); + + for (final package in context.packageSummaries) { + final packageClassesSummary = + package.classesSummaries + .map((c) => c.toDartLikeRepresentation()) + .toList(); + listOfSummaries.addAll(packageClassesSummary); + } + + final rag = await RAG.create(); + await rag.addAllDocumentsToRag(listOfSummaries); +} diff --git a/pkgs/native_doc_dartifier/lib/src/rag.dart b/pkgs/native_doc_dartifier/lib/src/rag.dart new file mode 100644 index 000000000..e056f440a --- /dev/null +++ b/pkgs/native_doc_dartifier/lib/src/rag.dart @@ -0,0 +1,134 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:google_generative_ai/google_generative_ai.dart'; + +import '../objectbox.g.dart'; +import 'rag_models.dart'; + +class RAG { + late final Store _store; + late final Box _classSummaryBox; + + RAG._create(this._store) { + _classSummaryBox = Box(_store); + } + + static Future create() async { + final store = openStore(); + return RAG._create(store); + } + + void close() { + _store.close(); + } + + Future> queryRAG( + String javaSnippet, { + int numRetrievedDocs = 20, + }) async { + final apiKey = Platform.environment['GEMINI_API_KEY']; + if (apiKey == null) { + stderr.writeln(r'No $GEMINI_API_KEY environment variable'); + exit(1); + } + + final embeddingModel = GenerativeModel( + apiKey: apiKey, + model: 'gemini-embedding-001', + ); + + final queryEmbeddings = await embeddingModel + .embedContent(Content.text(javaSnippet)) + .then((embedContent) => embedContent.embedding.values); + + // The Database makes use of HNSW algorithm for embeddings search which + // is O(log n) in search time complexity better than O(n). + // but the tradeoff that it gets the approximate nearest neighbors + // instead of the exact ones + // so make it to return approx 100 nearest neighbors and then get the top K. + final query = + _classSummaryBox + .query( + ClassSummaryRAGModel_.embeddings.nearestNeighborsF32( + queryEmbeddings, + 100, + ), + ) + .build(); + query.limit = numRetrievedDocs; + final resultWithScore = query.findWithScores(); + print('RAG query returned ${resultWithScore.length} results.'); + final result = resultWithScore.map((e) => e.object.summary).toList(); + + query.close(); + return result; + } + + Future addAllDocumentsToRag(List classesSummary) async { + final apiKey = Platform.environment['GEMINI_API_KEY']; + if (apiKey == null) { + stderr.writeln(r'No $GEMINI_API_KEY environment variable'); + exit(1); + } + + final embeddingModel = GenerativeModel( + apiKey: apiKey, + model: 'gemini-embedding-001', + ); + + // TODO: Check if the documents already exist in the RAG and skip + // adding them instead of clearing all and re-adding them. + print('Clearing existing RAG documents...'); + _classSummaryBox.removeAll(); + + const batchSize = 100; + final batchEmbededContent = []; + + for (var i = 0; i < classesSummary.length; i += batchSize) { + final upperbound = + i + batchSize < classesSummary.length + ? i + batchSize + : classesSummary.length; + print('Processing batch from $i to $upperbound...'); + final batch = classesSummary.sublist( + i, + i + batchSize > classesSummary.length + ? classesSummary.length + : i + batchSize, + ); + + final batchResponse = await embeddingModel.batchEmbedContents( + List.generate( + batch.length, + (index) => EmbedContentRequest(Content.text(batch[index])), + ), + ); + + batchEmbededContent.add(batchResponse); + + // Quota limit is 100 requests per minute + await Future.delayed(const Duration(minutes: 1)); + } + + final embeddings = >[]; + for (final response in batchEmbededContent) { + for (final embedContent in response.embeddings) { + embeddings.add(embedContent.values); + } + } + + final classSummaries = []; + for (var i = 0; i < classesSummary.length; i++) { + classSummaries.add( + ClassSummaryRAGModel(classesSummary[i], embeddings[i]), + ); + } + _classSummaryBox.putMany(classSummaries); + + print('Added ${classesSummary.length} documents to the RAG.'); + } +} diff --git a/pkgs/native_doc_dartifier/lib/src/rag_models.dart b/pkgs/native_doc_dartifier/lib/src/rag_models.dart new file mode 100644 index 000000000..e29159f80 --- /dev/null +++ b/pkgs/native_doc_dartifier/lib/src/rag_models.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:objectbox/objectbox.dart'; + +@Entity() +class ClassSummaryRAGModel { + @Id() + int id = 0; + + String summary; + + @HnswIndex(dimensions: 3072, distanceType: VectorDistanceType.cosine) + @Property(type: PropertyType.floatVector) + List embeddings; + + ClassSummaryRAGModel(this.summary, this.embeddings); +} diff --git a/pkgs/native_doc_dartifier/pubspec.yaml b/pkgs/native_doc_dartifier/pubspec.yaml index 230f7a331..b8984945b 100644 --- a/pkgs/native_doc_dartifier/pubspec.yaml +++ b/pkgs/native_doc_dartifier/pubspec.yaml @@ -3,6 +3,7 @@ description: A library that converts code snippets from other languages into Dar version: 0.0.1-pre repository: https://github.com/dart-lang/native/tree/main/pkgs/native_doc_dartifier issue_tracker: https://github.com/dart-lang/native/issues?q=is%3Aopen+label%3Apackage%3Anative_doc_dartifier +publish_to: none environment: sdk: ">=3.7.0 <4.0.0" @@ -12,9 +13,12 @@ dependencies: analyzer: ^8.1.1 google_generative_ai: ^0.4.7 jni: ^0.14.2 + objectbox: ^4.3.1 path: ^1.8.0 dev_dependencies: + build_runner: ^2.5.4 dart_flutter_team_lints: ^3.5.2 jnigen: ^0.14.2 + objectbox_generator: ^4.3.1 test: ^1.26.0 diff --git a/pkgs/native_doc_dartifier/test/imported_packages_test.dart b/pkgs/native_doc_dartifier/test/imported_packages_test.dart index a4c4725b9..a008ea778 100644 --- a/pkgs/native_doc_dartifier/test/imported_packages_test.dart +++ b/pkgs/native_doc_dartifier/test/imported_packages_test.dart @@ -15,8 +15,6 @@ void main() { Directory.current.path, p.absolute('test/dartify_simple_cases/bindings.dart'), ); - final logFile = File('output.txt'); - await logFile.writeAsString(context!.toDartLikeRepresentation()); }); test('Get imported packages only', () { diff --git a/pkgs/native_doc_dartifier/test/rag_test.dart b/pkgs/native_doc_dartifier/test/rag_test.dart new file mode 100644 index 000000000..6ba8a7eaf --- /dev/null +++ b/pkgs/native_doc_dartifier/test/rag_test.dart @@ -0,0 +1,82 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:core'; +import 'dart:io'; + +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:native_doc_dartifier/src/public_abstractor.dart'; +import 'package:native_doc_dartifier/src/rag.dart'; +import 'package:test/test.dart'; + +Future main() async { + final bindingsFile = File('test/dartify_simple_cases/bindings.dart'); + + if (!await bindingsFile.exists()) { + stderr.writeln('File not found: '); + exit(1); + } + + final bindings = await bindingsFile.readAsString(); + + final abstractor = PublicAbstractor(); + parseString(content: bindings).unit.visitChildren(abstractor); + final classesSummary = + abstractor + .getBindingsClassesSummary() + .map((c) => c.toDartLikeRepresentation()) + .toList(); + + print('Total Number of Classes: ${classesSummary.length}'); + + final rag = await RAG.create(); + await rag.addAllDocumentsToRag(classesSummary); + + group('Normal RAG Query', () { + test('Snippet that uses Accumulator only', () async { + const javaSnippet = ''' +Boolean overloadedMethods() { + Accumulator acc1 = new Accumulator(); + acc1.add(10); + acc1.add(10, 10); + acc1.add(10, 10, 10); + + Accumulator acc2 = new Accumulator(20); + acc2.add(acc1); + + Accumulator acc3 = new Accumulator(acc2); + return acc3.accumulator == 80; +} +'''; + + final documents = await rag.queryRAG(javaSnippet, numRetrievedDocs: 2); + final ragSummary = documents.join('\n'); + + print('Query Results:'); + for (var i = 0; i < documents.length; i++) { + print(documents[i].split('\n')[0]); + } + + expect(ragSummary.contains('class Accumulator'), isTrue); + }); + + test('Snippet that uses Example only', () async { + const javaSnippet = ''' +Boolean useEnums() { + Example example = new Example(); + Boolean isTrueUsage = example.enumValueToString(Operation.ADD) == "Addition"; + return isTrueUsage; +}'''; + final documents = await rag.queryRAG(javaSnippet, numRetrievedDocs: 2); + final ragSummary = documents.join('\n'); + + print('Query Results:'); + for (var i = 0; i < documents.length; i++) { + print(documents[i].split('\n')[0]); + } + + expect(ragSummary.contains('class Example'), isTrue); + }); + }); +}