Skip to content

Commit

Permalink
add an experimental gemini call to the backend (#2974)
Browse files Browse the repository at this point in the history
* add an experimental gemini call to the backend

* sort imports

* fix lint

* Update pkgs/dart_services/lib/src/common_server.dart

Co-authored-by: Parker Lougheed <parlough@gmail.com>

* review feedback

* merge from PR

---------

Co-authored-by: Parker Lougheed <parlough@gmail.com>
  • Loading branch information
devoncarew and parlough committed Jun 4, 2024
1 parent 17b54f6 commit b3926a5
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 6 deletions.
2 changes: 2 additions & 0 deletions pkgs/dart_services/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ARG PROJECT_ID
ARG FLUTTER_CHANNEL
ARG BUILD_SHA
ARG GOOGLE_API_KEY

FROM gcr.io/$PROJECT_ID/flutter:$FLUTTER_CHANNEL

Expand All @@ -16,6 +17,7 @@ RUN dart compile exe bin/server.dart -o bin/server
RUN dart run grinder build-project-templates

ENV BUILD_SHA=$BUILD_SHA
ENV GOOGLE_API_KEY=$GOOGLE_API_KEY

EXPOSE 8080
CMD ["/app/bin/server"]
6 changes: 0 additions & 6 deletions pkgs/dart_services/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ To rebuild the shelf router, run:
dart run build_runner build --delete-conflicting-outputs
```

And to update the shared code from dartpad_shared, run:

```
dart tool/grind.dart copy-shared-source
```

### Modifying supported packages

Package dependencies are pinned using the `pub_dependencies_<CHANNEL>.yaml`
Expand Down
1 change: 1 addition & 0 deletions pkgs/dart_services/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ linter:
rules:
- prefer_final_in_for_each
- prefer_final_locals
- sort_pub_dependencies
50 changes: 50 additions & 0 deletions pkgs/dart_services/lib/src/common_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import 'dart:convert';
import 'dart:io';

import 'package:dartpad_shared/model.dart' as api;
import 'package:google_generative_ai/google_generative_ai.dart' as google_ai;
import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
Expand Down Expand Up @@ -200,6 +202,50 @@ class CommonServerApi {
return ok(version().toJson());
}

static final String? googleApiKey = Platform.environment['GOOGLE_API_KEY'];
http.Client? geminiHttpClient;

@Route.post('$apiPrefix/_gemini')
Future<Response> gemini(Request request, String apiVersion) async {
if (apiVersion != api3) return unhandledVersion(apiVersion);

// Read the api key from env variables (populated on the server).
final apiKey = googleApiKey;
if (apiKey == null) {
return Response.internalServerError(
body: 'gemini key not configured on server');
}

// Only allow the call from dartpad.dev.
final origin = request.origin;
if (origin != 'https://dartpad.dev') {
return Response.badRequest(
body: 'Gemini calls only allowed from the DartPad front-end');
}

final sourceRequest =
api.SourceRequest.fromJson(await request.readAsJson());

geminiHttpClient ??= http.Client();

final model = google_ai.GenerativeModel(
model: 'models/gemini-1.5-flash-latest',
apiKey: apiKey,
httpClient: geminiHttpClient,
);

final result = await serialize(() async {
// call gemini
final result = await model.generateContent([
google_ai.Content.text(sourceRequest.source),
]);

return api.GeminiResponse(response: result.text!);
});

return ok(result.toJson());
}

Response ok(Map<String, dynamic> json) {
return Response.ok(
_jsonEncoder.convert(json),
Expand Down Expand Up @@ -327,3 +373,7 @@ String _formatMessage(

return message;
}

extension on Request {
String? get origin => headers['origin'];
}
5 changes: 5 additions & 0 deletions pkgs/dart_services/lib/src/common_server.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkgs/dart_services/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies:
bazel_worker: ^1.1.1
dartpad_shared: any
encrypt: ^5.0.3
google_generative_ai: ^0.4.0
http: ^1.2.1
json_annotation: any
logging: ^1.2.0
Expand Down
17 changes: 17 additions & 0 deletions pkgs/dartpad_shared/lib/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,23 @@ class VersionResponse {
Map<String, dynamic> toJson() => _$VersionResponseToJson(this);
}

@JsonSerializable()
class GeminiResponse {
final String response;

GeminiResponse({
required this.response,
});

factory GeminiResponse.fromJson(Map<String, dynamic> json) =>
_$GeminiResponseFromJson(json);

Map<String, dynamic> toJson() => _$GeminiResponseToJson(this);

@override
String toString() => 'GeminiResponse[response=$response]';
}

@JsonSerializable()
class PackageInfo {
final String name;
Expand Down
10 changes: 10 additions & 0 deletions pkgs/dartpad_shared/lib/model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions pkgs/dartpad_shared/lib/services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'dart:convert';

import 'package:http/http.dart';
import 'package:meta/meta.dart';

import 'model.dart';

Expand Down Expand Up @@ -40,6 +41,11 @@ class ServicesClient {
Future<CompileDDCResponse> compileDDC(CompileRequest request) =>
_requestPost('compileDDC', request.toJson(), CompileDDCResponse.fromJson);

/// Note: this API is experimental and could change or be removed at any time.
@experimental
Future<GeminiResponse> gemini(SourceRequest request) =>
_requestPost('_gemini', request.toJson(), GeminiResponse.fromJson);

void dispose() => client.close();

Future<T> _requestGet<T>(
Expand Down
1 change: 1 addition & 0 deletions pkgs/dartpad_shared/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ environment:
dependencies:
http: ^1.2.1
json_annotation: ^4.9.0
meta: ^1.14.0

dev_dependencies:
build_runner: ^2.4.9
Expand Down
4 changes: 4 additions & 0 deletions pkgs/dartpad_ui/lib/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ class AppServices {
return await services.document(request);
}

Future<GeminiResponse> gemini(SourceRequest request) async {
return await services.gemini(request);
}

Future<CompileResponse> compile(CompileRequest request) async {
try {
appModel.compilingBusy.value = true;
Expand Down

0 comments on commit b3926a5

Please sign in to comment.