-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add openai service * feat: add openai auto completion plugin * feat: add visible icon for open ai input field * chore: optimize user experience * feat: add auto completion node plugin * feat: support keep and discard the auto generated text * fix: can't delete the auto completion node * feat: disable ai plugins if open ai key is null * fix: wrong auto completion node card color * fix: make sure the previous text node is pure when using auto generator
- Loading branch information
Showing
23 changed files
with
777 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
frontend/app_flowy/lib/plugins/document/presentation/plugins/openai/service/error.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
part 'error.freezed.dart'; | ||
part 'error.g.dart'; | ||
|
||
@freezed | ||
class OpenAIError with _$OpenAIError { | ||
const factory OpenAIError({ | ||
String? code, | ||
required String message, | ||
}) = _OpenAIError; | ||
|
||
factory OpenAIError.fromJson(Map<String, Object?> json) => | ||
_$OpenAIErrorFromJson(json); | ||
} |
85 changes: 85 additions & 0 deletions
85
...end/app_flowy/lib/plugins/document/presentation/plugins/openai/service/openai_client.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import 'dart:convert'; | ||
|
||
import 'text_completion.dart'; | ||
import 'package:dartz/dartz.dart'; | ||
import 'dart:async'; | ||
|
||
import 'error.dart'; | ||
import 'package:http/http.dart' as http; | ||
|
||
// Please fill in your own API key | ||
const apiKey = ''; | ||
|
||
enum OpenAIRequestType { | ||
textCompletion, | ||
textEdit; | ||
|
||
Uri get uri { | ||
switch (this) { | ||
case OpenAIRequestType.textCompletion: | ||
return Uri.parse('https://api.openai.com/v1/completions'); | ||
case OpenAIRequestType.textEdit: | ||
return Uri.parse('https://api.openai.com/v1/edits'); | ||
} | ||
} | ||
} | ||
|
||
abstract class OpenAIRepository { | ||
/// Get completions from GPT-3 | ||
/// | ||
/// [prompt] is the prompt text | ||
/// [suffix] is the suffix text | ||
/// [maxTokens] is the maximum number of tokens to generate | ||
/// [temperature] is the temperature of the model | ||
/// | ||
Future<Either<OpenAIError, TextCompletionResponse>> getCompletions({ | ||
required String prompt, | ||
String? suffix, | ||
int maxTokens = 50, | ||
double temperature = .3, | ||
}); | ||
} | ||
|
||
class HttpOpenAIRepository implements OpenAIRepository { | ||
const HttpOpenAIRepository({ | ||
required this.client, | ||
required this.apiKey, | ||
}); | ||
|
||
final http.Client client; | ||
final String apiKey; | ||
|
||
Map<String, String> get headers => { | ||
'Authorization': 'Bearer $apiKey', | ||
'Content-Type': 'application/json', | ||
}; | ||
|
||
@override | ||
Future<Either<OpenAIError, TextCompletionResponse>> getCompletions({ | ||
required String prompt, | ||
String? suffix, | ||
int maxTokens = 50, | ||
double temperature = 0.3, | ||
}) async { | ||
final parameters = { | ||
'model': 'text-davinci-003', | ||
'prompt': prompt, | ||
'suffix': suffix, | ||
'max_tokens': maxTokens, | ||
'temperature': temperature, | ||
'stream': false, | ||
}; | ||
|
||
final response = await http.post( | ||
OpenAIRequestType.textCompletion.uri, | ||
headers: headers, | ||
body: json.encode(parameters), | ||
); | ||
|
||
if (response.statusCode == 200) { | ||
return Right(TextCompletionResponse.fromJson(json.decode(response.body))); | ||
} else { | ||
return Left(OpenAIError.fromJson(json.decode(response.body)['error'])); | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...d/app_flowy/lib/plugins/document/presentation/plugins/openai/service/text_completion.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
part 'text_completion.freezed.dart'; | ||
part 'text_completion.g.dart'; | ||
|
||
@freezed | ||
class TextCompletionChoice with _$TextCompletionChoice { | ||
factory TextCompletionChoice({ | ||
required String text, | ||
required int index, | ||
// ignore: invalid_annotation_target | ||
@JsonKey(name: 'finish_reason') required String finishReason, | ||
}) = _TextCompletionChoice; | ||
|
||
factory TextCompletionChoice.fromJson(Map<String, Object?> json) => | ||
_$TextCompletionChoiceFromJson(json); | ||
} | ||
|
||
@freezed | ||
class TextCompletionResponse with _$TextCompletionResponse { | ||
const factory TextCompletionResponse({ | ||
required List<TextCompletionChoice> choices, | ||
}) = _TextCompletionResponse; | ||
|
||
factory TextCompletionResponse.fromJson(Map<String, Object?> json) => | ||
_$TextCompletionResponseFromJson(json); | ||
} |
44 changes: 44 additions & 0 deletions
44
...end/app_flowy/lib/plugins/document/presentation/plugins/openai/util/editor_extension.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import 'package:appflowy_editor/appflowy_editor.dart'; | ||
|
||
enum TextRobotInputType { | ||
character, | ||
word, | ||
} | ||
|
||
extension TextRobot on EditorState { | ||
Future<void> autoInsertText( | ||
String text, { | ||
TextRobotInputType inputType = TextRobotInputType.word, | ||
Duration delay = const Duration(milliseconds: 10), | ||
}) async { | ||
final lines = text.split('\n'); | ||
for (final line in lines) { | ||
if (line.isEmpty) continue; | ||
switch (inputType) { | ||
case TextRobotInputType.character: | ||
final iterator = line.runes.iterator; | ||
while (iterator.moveNext()) { | ||
await insertTextAtCurrentSelection( | ||
iterator.currentAsString, | ||
); | ||
await Future.delayed(delay, () {}); | ||
} | ||
break; | ||
case TextRobotInputType.word: | ||
final words = line.split(' ').map((e) => '$e '); | ||
for (final word in words) { | ||
await insertTextAtCurrentSelection( | ||
word, | ||
); | ||
await Future.delayed(delay, () {}); | ||
} | ||
break; | ||
} | ||
|
||
// insert new line | ||
if (lines.length > 1) { | ||
await insertNewLineAtCurrentSelection(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.