Skip to content
Merged
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
119 changes: 69 additions & 50 deletions colorist/codelab_rebuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ steps:
replace-contents: |
include: ../../analysis_options.yaml

# analyzer:
# plugins:
# - custom_lint
analyzer:
plugins:
- custom_lint

linter:
rules:
Expand Down Expand Up @@ -96,8 +96,8 @@ steps:

// A fake LLM that just echoes back what it receives.
void sendMessage(String message, WidgetRef ref) {
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
final chatStateNotifier = ref.read(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);

chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
Expand Down Expand Up @@ -167,18 +167,17 @@ steps:

import 'package:firebase_ai/firebase_ai.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../firebase_options.dart';

part 'gemini.g.dart';

@riverpod
@Riverpod(keepAlive: true)
Future<FirebaseApp> firebaseApp(Ref ref) =>
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

@riverpod
@Riverpod(keepAlive: true)
Future<GenerativeModel> geminiModel(Ref ref) async {
await ref.watch(firebaseAppProvider.future);

Expand Down Expand Up @@ -207,7 +206,6 @@ steps:

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../providers/gemini.dart';
Expand All @@ -220,8 +218,8 @@ steps:

Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
final chatStateNotifier = ref.read(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);

chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
Expand All @@ -247,7 +245,7 @@ steps:
}
}

@riverpod
@Riverpod(keepAlive: true)
GeminiChatService geminiChatService(Ref ref) => GeminiChatService(ref);
- name: Run build_runner
path: colorist
Expand Down Expand Up @@ -298,8 +296,8 @@ steps:
-
- // A fake LLM that just echoes back what it receives.
- void sendMessage(String message, WidgetRef ref) {
- final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
- final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
- final chatStateNotifier = ref.read(chatStateProvider.notifier);
- final logStateNotifier = ref.read(logStateProvider.notifier);
-
- chatStateNotifier.addUserMessage(message);
- logStateNotifier.logUserText(message);
Expand Down Expand Up @@ -446,29 +444,28 @@ steps:
// found in the LICENSE file.

import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'system_prompt.g.dart';

@riverpod
@Riverpod(keepAlive: true)
Future<String> systemPrompt(Ref ref) =>
rootBundle.loadString('assets/system_prompt.md');
- name: Patch lib/providers/gemini.dart
path: colorist/lib/providers/gemini.dart
patch-u: |
--- a/colorist/step_03/lib/providers/gemini.dart
+++ b/colorist/step_03/lib/providers/gemini.dart
@@ -10,6 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
--- b/colorist/step_03/lib/providers/gemini.dart
+++ a/colorist/step_03/lib/providers/gemini.dart
@@ -9,6 +9,7 @@ import 'package:firebase_core/firebase_core.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../firebase_options.dart';
+import 'system_prompt.dart';

part 'gemini.g.dart';

@@ -20,9 +21,11 @@ Future<FirebaseApp> firebaseApp(Ref ref) =>
@riverpod
@@ -19,9 +20,11 @@ Future<FirebaseApp> firebaseApp(Ref ref) =>
@Riverpod(keepAlive: true)
Future<GenerativeModel> geminiModel(Ref ref) async {
await ref.watch(firebaseAppProvider.future);
+ final systemPrompt = await ref.watch(systemPromptProvider.future);
Expand Down Expand Up @@ -505,7 +502,6 @@ steps:
// found in the LICENSE file.

import 'package:firebase_ai/firebase_ai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'gemini_tools.g.dart';
Expand All @@ -530,22 +526,22 @@ steps:
];
}

@riverpod
@Riverpod(keepAlive: true)
GeminiTools geminiTools(Ref ref) => GeminiTools(ref);
- name: Patch lib/providers/gemini.dart
path: colorist/lib/providers/gemini.dart
patch-u: |
--- a/colorist/step_04/lib/providers/gemini.dart
+++ b/colorist/step_04/lib/providers/gemini.dart
@@ -10,6 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
--- b/colorist/step_04/lib/providers/gemini.dart
+++ a/colorist/step_04/lib/providers/gemini.dart
@@ -9,6 +9,7 @@ import 'package:firebase_core/firebase_core.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../firebase_options.dart';
+import '../services/gemini_tools.dart';
import 'system_prompt.dart';

part 'gemini.g.dart';
@@ -22,10 +23,12 @@ Future<FirebaseApp> firebaseApp(Ref ref) =>
@@ -21,10 +22,12 @@ Future<FirebaseApp> firebaseApp(Ref ref) =>
Future<GenerativeModel> geminiModel(Ref ref) async {
await ref.watch(firebaseAppProvider.future);
final systemPrompt = await ref.watch(systemPromptProvider.future);
Expand Down Expand Up @@ -634,15 +630,15 @@ steps:
patch-u: |
--- b/colorist/step_05/lib/services/gemini_chat_service.dart
+++ a/colorist/step_05/lib/services/gemini_chat_service.dart
@@ -10,6 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -9,6 +9,7 @@ import 'package:firebase_ai/firebase_ai.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../providers/gemini.dart';
+import 'gemini_tools.dart';

part 'gemini_chat_service.g.dart';

@@ -33,6 +34,27 @@ class GeminiChatService {
@@ -32,6 +33,27 @@ class GeminiChatService {
logStateNotifier.logLlmText(responseText);
chatStateNotifier.appendToMessage(llmMessage.id, responseText);
}
Expand Down Expand Up @@ -681,9 +677,9 @@ steps:

+import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -26,6 +27,44 @@ class GeminiTools {

@@ -25,6 +26,44 @@ class GeminiTools {
List<Tool> get tools => [
Tool.functionDeclarations([setColorFuncDecl]),
];
Expand All @@ -692,7 +688,7 @@ steps:
+ String functionName,
+ Map<String, Object?> arguments,
+ ) {
+ final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
+ final logStateNotifier = ref.read(logStateProvider.notifier);
+ logStateNotifier.logFunctionCall(functionName, arguments);
+ return switch (functionName) {
+ 'set_color' => handleSetColor(arguments),
Expand All @@ -701,7 +697,7 @@ steps:
+ }
+
+ Map<String, Object?> handleSetColor(Map<String, Object?> arguments) {
+ final colorStateNotifier = ref.read(colorStateNotifierProvider.notifier);
+ final colorStateNotifier = ref.read(colorStateProvider.notifier);
+ final red = (arguments['red'] as num).toDouble();
+ final green = (arguments['green'] as num).toDouble();
+ final blue = (arguments['blue'] as num).toDouble();
Expand All @@ -712,13 +708,13 @@ steps:
+ .toLLMContextMap(),
+ };
+
+ final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
+ final logStateNotifier = ref.read(logStateProvider.notifier);
+ logStateNotifier.logFunctionResults(functionResults);
+ return functionResults;
+ }
+
+ Map<String, Object?> handleUnknownFunction(String functionName) {
+ final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
+ final logStateNotifier = ref.read(logStateProvider.notifier);
+ logStateNotifier.logWarning('Unsupported function call $functionName');
+ return {
+ 'success': false,
Expand All @@ -727,7 +723,7 @@ steps:
+ }
}

@riverpod
@Riverpod(keepAlive: true)
- name: Run build_runner
path: colorist
dart: run build_runner build --delete-conflicting-outputs
Expand All @@ -751,13 +747,35 @@ steps:
patch-u: |
--- b/colorist/step_06/lib/services/gemini_chat_service.dart
+++ a/colorist/step_06/lib/services/gemini_chat_service.dart
@@ -14,46 +14,41 @@ import 'gemini_tools.dart';
@@ -6,6 +6,7 @@ import 'dart:async';

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../providers/gemini.dart';
@@ -13,46 +14,55 @@ import 'gemini_tools.dart';

part 'gemini_chat_service.g.dart';

+final conversationStateProvider = StateProvider(
+ (ref) => ConversationState.idle,
+);

+class ConversationStateNotifier extends Notifier<ConversationState> {
+ @override
+ ConversationState build() => ConversationState.idle;
+
+ void busy() {
+ state = ConversationState.busy;
+ }
+
+ void idle() {
+ state = ConversationState.idle;
+ }
+}
+
+final conversationStateProvider =
+ NotifierProvider<ConversationStateNotifier, ConversationState>(
+ ConversationStateNotifier.new,
+ );
+
class GeminiChatService {
GeminiChatService(this.ref);
Expand All @@ -766,8 +784,8 @@ steps:
Future<void> sendMessage(String message) async {
final chatSession = await ref.read(chatSessionProvider.future);
+ final conversationState = ref.read(conversationStateProvider);
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
final chatStateNotifier = ref.read(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);

+ if (conversationState == ConversationState.busy) {
+ logStateNotifier.logWarning(
Expand All @@ -780,7 +798,7 @@ steps:
+ final conversationStateNotifier = ref.read(
+ conversationStateProvider.notifier,
+ );
+ conversationStateNotifier.state = ConversationState.busy;
+ conversationStateNotifier.busy();
chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
final llmMessage = chatStateNotifier.createLlmMessage();
Expand Down Expand Up @@ -820,11 +838,11 @@ steps:
}
} catch (e, st) {
logStateNotifier.logError(e, st: st);
@@ -64,6 +59,45 @@ class GeminiChatService {
@@ -63,6 +73,44 @@ class GeminiChatService {
);
} finally {
chatStateNotifier.finalizeMessage(llmMessage.id);
+ conversationStateNotifier.state = ConversationState.idle;
+ conversationStateNotifier.idle();
+ }
+ }
+
Expand All @@ -833,10 +851,9 @@ steps:
+ String llmMessageId,
+ ) async {
+ final chatSession = await ref.read(chatSessionProvider.future);
+ final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
+ final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
+ final chatStateNotifier = ref.read(chatStateProvider.notifier);
+ final logStateNotifier = ref.read(logStateProvider.notifier);
+ final blockText = block.text;
+
+ if (blockText != null) {
+ logStateNotifier.logLlmText(blockText);
+ chatStateNotifier.appendToMessage(llmMessageId, blockText);
Expand Down Expand Up @@ -900,6 +917,7 @@ steps:
- name: Flutter clean
path: step_06
flutter: clean

- name: step_07
steps:
- name: Remove generated code
Expand All @@ -917,7 +935,7 @@ steps:

import 'package:colorist_ui/colorist_ui.dart';
import 'package:firebase_ai/firebase_ai.dart';
@@ -22,6 +23,10 @@ class GeminiChatService {
@@ -36,6 +37,10 @@ class GeminiChatService {
GeminiChatService(this.ref);
final Ref ref;

Expand Down Expand Up @@ -994,5 +1012,6 @@ steps:
- name: Flutter clean
path: step_07
flutter: clean

- name: Cleanup
rmdir: colorist
6 changes: 3 additions & 3 deletions colorist/step_01/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
include: ../../analysis_options.yaml

# analyzer:
# plugins:
# - custom_lint
analyzer:
plugins:
- custom_lint

linter:
rules:
Expand Down
4 changes: 2 additions & 2 deletions colorist/step_01/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class MainApp extends ConsumerWidget {

// A fake LLM that just echoes back what it receives.
void sendMessage(String message, WidgetRef ref) {
final chatStateNotifier = ref.read(chatStateNotifierProvider.notifier);
final logStateNotifier = ref.read(logStateNotifierProvider.notifier);
final chatStateNotifier = ref.read(chatStateProvider.notifier);
final logStateNotifier = ref.read(logStateProvider.notifier);

chatStateNotifier.addUserMessage(message);
logStateNotifier.logUserText(message);
Expand Down
18 changes: 9 additions & 9 deletions colorist/step_01/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ publish_to: 'none'
version: 0.1.0

environment:
sdk: ^3.9.0
sdk: ^3.9.2

dependencies:
flutter:
sdk: flutter
colorist_ui: ^0.2.5
flutter_riverpod: ^2.6.1
riverpod_annotation: ^2.6.1
colorist_ui: ^0.3.0
flutter_riverpod: ^3.0.0
riverpod_annotation: ^3.0.0

dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
build_runner: ^2.5.4
riverpod_generator: ^2.6.5
riverpod_lint: ^2.6.5
json_serializable: ^6.9.5
custom_lint: ^0.7.6
build_runner: ^2.7.1
riverpod_generator: ^3.0.0
riverpod_lint: ^3.0.0
json_serializable: ^6.11.1
custom_lint: ^0.8.0

flutter:
uses-material-design: true
Loading
Loading