[03-Developer] #260 in-app tool calling (Gemma 4 multi-turn)
ADR-0005 in-process tool runtime — 6 tools (catalog 2 + tracker 2 + habit 2), ToolDispatcher with JSON-schema validation + modal ConfirmGate for destructive ops, multi-turn LlmChatSession abstraction wired to flutter_gemma 0.16.5 (ToolChoice.auto), ChatSessionController with MAX_TURNS=4 safety + 8-turn history hint, ChatScreen entry behind AI opt-in. R3/R7/R8 enforced inside handlers. 41 new tests (envelope, catalog/tracker/habit tools, dispatcher, controller loop) — 151 total passing. Refs #260
This commit is contained in:
48
app/test/ai/tools/_tool_test_helpers.dart
Normal file
48
app/test/ai/tools/_tool_test_helpers.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:drift/drift.dart' as drift;
|
||||
import 'package:life_helper/ai/tools/tool_definition.dart';
|
||||
import 'package:life_helper/core/constants.dart';
|
||||
import 'package:life_helper/core/time.dart';
|
||||
import 'package:life_helper/data/catalog/catalog_repository.dart';
|
||||
import 'package:life_helper/data/db/app_database.dart';
|
||||
import 'package:life_helper/data/db/daos/habit_dao.dart';
|
||||
import 'package:life_helper/data/db/daos/tracker_dao.dart';
|
||||
import 'package:life_helper/data/seed/seed_importer.dart';
|
||||
import 'package:life_helper/domain/models/frame_pattern.dart';
|
||||
|
||||
import '../../data/seed/test_seeds.dart';
|
||||
|
||||
/// Tool tests share a tiny in-memory bootstrap. Returns the assembled
|
||||
/// [ToolDeps] plus the underlying [AppDatabase] so callers can close it
|
||||
/// in tearDown.
|
||||
Future<({AppDatabase db, ToolDeps deps})> bootstrapToolDeps() async {
|
||||
final db = AppDatabase.memory();
|
||||
// default user (seed importer doesn't insert users — bootstrap does).
|
||||
await db.into(db.users).insert(UsersCompanion.insert(
|
||||
id: kLocalDefaultUserId,
|
||||
displayName: const drift.Value('Test'),
|
||||
createdAt: nowKst().toIso8601String(),
|
||||
));
|
||||
await SeedImporter(db, loadAsset: testStubLoader).importIfNeeded();
|
||||
final patterns = await db.select(db.framePatterns).get();
|
||||
final framePatterns = patterns
|
||||
.map((r) => FramePatternModel(
|
||||
id: r.id,
|
||||
domain: r.domain,
|
||||
avoidanceKeyword: r.avoidanceKeyword,
|
||||
l0Example: r.l0Example,
|
||||
l1SimpleReplace: r.l1SimpleReplace,
|
||||
l2Suggestion: r.l2Suggestion,
|
||||
l3Identity: r.l3Identity,
|
||||
))
|
||||
.toList();
|
||||
return (
|
||||
db: db,
|
||||
deps: ToolDeps(
|
||||
habitDao: HabitDao(db),
|
||||
trackerDao: TrackerDao(db),
|
||||
catalog: CatalogRepository(db),
|
||||
framePatterns: framePatterns,
|
||||
userId: kLocalDefaultUserId,
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user