createWriteTool function

Tool<JsonMap, String> createWriteTool(
  1. Genkit ai, {
  2. required ToolRuntime runtime,
})

Builds the tool that writes whole files inside the workspace.

Implementation

Tool<JsonMap, String> createWriteTool(
  Genkit ai, {
  required ToolRuntime runtime,
}) {
  return defineTextTool(
    ai,
    runtime: runtime,
    name: 'write',
    description: _description,
    properties: <String, Object?>{
      'file_path': stringProperty('The absolute path of the file to write.'),
      'content': stringProperty('The full content to write to the file.'),
    },
    required: <String>['file_path', 'content'],
    run: (input) async {
      final path = runtime.resolvePath(requireString(input, 'file_path'));
      final content = readString(input, 'content');

      if (content == null) {
        throw const ToolFailure('`content` is required.');
      }
      if (!runtime.isWithinWorkspace(path)) {
        throw ToolFailure('$path is outside the workspace ${runtime.workspaceRoot}.');
      }

      final file = File(path);
      final existed = await file.exists();
      if (existed) {
        if (runtime.trackedFile(path) == null) {
          throw const ToolFailure('The file already exists. Read it with the read tool before overwriting it.');
        }
        if (!await runtime.fileMatchesTrackedState(path)) {
          throw const ToolFailure('The file changed since it was last read. Read it again before overwriting it.');
        }
      }

      runtime.startTool('write', runtime.displayPath(path));
      await file.parent.create(recursive: true);
      await file.writeAsString(content);
      runtime.recordFileRead(path, content, await file.stat());

      final lineCount = content.isEmpty ? 0 : '\n'.allMatches(content.trimRight()).length + 1;
      return existed ? 'Overwrote $path ($lineCount lines).' : 'Created $path ($lineCount lines).';
    },
  );
}