Dotprompt comes to Genkit Dart: Richer prompt templating and management
Prompt engineering is an iterative process. The model, its parameters, and the prompt text all shape the output together, and getting them right takes many rounds of tuning. As a prompt grows in complexity, it helps to treat that whole bundle as an artifact you can edit, version, and test on its own. That is where Dotrpompt can help!
Genkit Dart 0.14.0 brings Dotprompt to Dart and Flutter developers. Simple prompts are often fine inline, and Genkit still supports that. When a prompt does more work, Dotprompt lets you move it into a .prompt file that lives alongside your app. You define the prompt there, iterate on it in the Developer UI, and run it from Dart by name.
A prompt in a file
Section titled “A prompt in a file”A .prompt file is YAML front matter, holding the model and its config, followed by a Handlebars template. Drop this into your prompts/ directory as greeting.prompt:
---model: googleai/gemini-flash-latestconfig: temperature: 0.7input: schema: name: string, the person to greet style: string, the greeting style (e.g. cheerful, casual, pirate)---{{role "system"}}You are a friendly greeter. Greet the user warmly.{{role "user"}}Say hello to {{name}} in a {{style}} way.Then load and run it from your app. By default Genkit looks for prompts in ./prompts:
import 'package:genkit/genkit.dart';import 'package:genkit_google_genai/genkit_google_genai.dart';
void main() async { final ai = Genkit(plugins: [googleAI()]);
final greeting = await ai.prompt('greeting'); final response = await greeting({'name': 'Ada', 'style': 'cheerful'}); print(response.text);}For a prompt like this, none of the prose lives in your code. The model and temperature sit right next to the text they shape, and your application asks for the prompt by name and stays out of the tuning loop.
When to give a prompt its own file
Section titled “When to give a prompt its own file”Not every prompt needs one. A single-line system prompt can stay in your Dart. Reach for a .prompt file once a prompt does enough work to earn the benefits below, which usually means complex or highly dynamic templating, or text that a separate subject-matter expert needs to review and edit.
- Independent versioning: Prompt changes land as prompt diffs you can review and revert on their own, without untangling them from application logic. The history shows exactly how the wording, model, and config evolved.
- Real templating: Handlebars helpers, partials, and conditionals express complex, dynamic prompts cleanly, so you can drop the brittle string concatenation in Dart.
- Accessible for non-developers: A subject-matter expert who doesn’t work on the code can read the plain YAML and template and adjust the prompt with more confidence.
- An executable, testable unit: The model, config, input and output schemas, and template all travel together as one runnable artifact. You render it, run it, and assert on its output directly, rather than reassembling the pieces at every call site.
- Fast iteration: Load prompts in the Developer UI, where you run it against custom input, compare against variants, and view detailed execution traces for rapid iteration.
What shipped
Section titled “What shipped”This release brings the full Dotprompt feature set to Dart:
.promptfiles with YAML front matter, Handlebars templates, and the{{role}}and{{media}}helpers for multi-message and multimodal prompts.- Structured I/O via input and output schemas, including Picoschema and schemas defined in code with schemantic.
- Streaming with
prompt.stream(), plusrender()to inspect the final messages without calling the model. - Variants for side-by-side experimentation. Add a
greeting.formal.promptfile and load it withawait ai.prompt('greeting', variant: 'formal'). - Partials for reusable snippets. A file like
_signature.promptcan be included in any prompt with{{> signature}}.
Prompt templates in code
Section titled “Prompt templates in code”Prefer to keep prompts in code but still want the templating benefits? Define a strongly-typed input schema with schemantic:
@Schema()abstract class $JokeInput { @Field(description: 'The joke topic (e.g. programming, cats, cooking)') String get topic;
@Field(description: 'The joke style (e.g. punny, dry, dad)') String get style;}Then definePrompt() takes the same metadata as a .prompt file with a Handlebars template and the generated schema:
final jokePrompt = ai.definePrompt( name: 'joke', model: modelRef('googleai/gemini-flash-latest'), config: {'temperature': 0.9}, inputSchema: JokeInput.$schema, system: 'You are a witty comedian. Keep jokes family-friendly.', prompt: 'Tell me a {{style}} joke about {{topic}}.',);
final response = await jokePrompt(JokeInput(topic: 'cats', style: 'punny'));print(response.text);And when you need to build messages programmatically, defineCustomPrompt() lets you return a GenerateActionOptions describing the request yourself.
Iterate in the Developer UI
Section titled “Iterate in the Developer UI”Every .prompt file loads straight into the Genkit Developer UI. There you can edit parameters, run them against live input, and compare variants side by side in traces.
Start the UI alongside your app:
genkit start -- dart runGet started
Section titled “Get started”Update to Genkit Dart 0.14.0 and start moving your prompts into .prompt files today.
- Read the docs: Dive into the Dotprompt documentation to see everything you can do.
- New to Genkit Dart? Start with the quickstart guide.
- Join the community: Ask questions and chat with the team on our Discord server.
- Stay updated: Follow us on X and LinkedIn.
- Give feedback: Open an issue on the GitHub repository to report bugs or request features.
We can’t wait to see what you build. Happy prompting! 🚀