Working with Artifacts
The
Artifacttype comes frompackage:genkit/genkit.dart. Read agents.md first.
Artifacts are named, content-bearing deliverables an agent produces during a
session — files, reports, code, etc. They live in the session (deduplicated by
name) and are returned in res.artifacts / tracked on the client's
chat.artifacts.
Dart has no
artifacts()middleware yet. Definewrite_artifact/read_artifacttools directly on top of the session artifact API (ai.currentSession().addArtifacts()/getArtifacts()).
Session artifacts vs. real files. Artifacts (this page) live in the session state — they travel with the conversation and stream to the client. If instead you want the agent to work against real files on disk (a sandboxed workspace), use the
filesystem()middleware frompackage:genkit_middleware/filesystem.dart(filesystem(rootDirectory: ...), backed byFilesystemPlugin()), which gives the modellist_files/read_file/write_file/search_and_replacetools rooted at a directory. See middleware. The two approaches are complementary: session artifacts for conversation-scoped deliverables,filesystem()for persistent on-disk work.
Give the model artifact tools
import 'package:genkit/genkit.dart';
import 'package:schemantic/schemantic.dart';
import 'genkit.dart';
part 'workspace_agent.g.dart';
@Schema()
abstract class $WriteArtifactInput {
@Field(description: 'The name (e.g. filename) of the artifact.')
String get name;
@Field(description: 'The full content of the artifact.')
String get content;
}
@Schema()
abstract class $ReadArtifactInput {
@Field(description: 'The name of the artifact to read.')
String get name;
}
final writeArtifact = ai.defineTool(
name: 'write_artifact',
description: 'Create or overwrite a named artifact (e.g. a file). Pass the '
'filename as "name" and the full content as "content".',
inputSchema: WriteArtifactInput.$schema,
outputSchema: SchemanticType.string(),
fn: (input, _) async {
final session = ai.currentSession()!;
session.addArtifacts([
Artifact(name: input.name, parts: [TextPart(text: input.content)]),
]);
return 'Wrote artifact "${input.name}".';
},
);
final readArtifact = ai.defineTool(
name: 'read_artifact',
description: 'Read the content of a previously created artifact by name.',
inputSchema: ReadArtifactInput.$schema,
outputSchema: SchemanticType.string(),
fn: (input, _) async {
final session = ai.currentSession()!;
final match = session.getArtifacts().where((a) => a.name == input.name);
if (match.isEmpty) return 'Artifact "${input.name}" not found.';
return match.first.parts.map((p) => p.text ?? '').join();
},
);
final workspaceAgent = ai.defineAgent(
name: 'workspaceAgent',
system: 'You are a code generation assistant. Use write_artifact to create '
'files (pass the filename as "name" and the full content as "content"). '
'Use read_artifact to review or modify a previously created file.',
tools: [writeArtifact, readArtifact],
use: [retry()],
store: InMemorySessionStore(),
);
How artifacts flow
Adding an artifact to the session emits an event that the agent runtime forwards
to the client as an artifact stream chunk. Artifacts are deduplicated by
name — writing the same name again replaces it.
Run it; artifacts are produced via the tool and returned in the response:
final chat = workspaceAgent.chat();
final res = await chat.send(text: 'Write poem.txt with a poem about Genkit');
print(res.artifacts); // List<Artifact>
The Artifact shape
// An artifact's content lives in `parts` (text parts). `name` and `metadata`
// are optional on the type but you should always set `name`.
final artifact = Artifact(
name: 'poem.txt',
parts: [TextPart(text: 'Roses are red…')],
);
Programmatic access (inside tools / custom agents)
Use the active session. ai.currentSession() returns null when there's no
active session, so only call it inside an agent turn.
final session = ai.currentSession()!;
// Read all artifacts:
final all = session.getArtifacts(); // List<Artifact>
final found = all.where((a) => a.name == 'poem.txt').firstOrNull;
// Create / replace artifacts:
session.addArtifacts([
Artifact(name: 'notes.md', parts: [TextPart(text: '# Notes')]),
]);
Reading artifacts on the client
The remoteAgent client tracks artifacts on chat.artifacts; each response
exposes res.artifacts; and each streamed chunk exposes chunk.artifact as it
arrives.
import 'package:genkit/client.dart';
final agent = remoteAgent(url: '/api/workspaceAgent');
final chat = agent.chat();
final turn = chat.sendStream(text: 'Create index.html and styles.css');
await for (final chunk in turn.stream) {
final artifact = chunk.artifact;
if (artifact != null) {
// artifact.name, artifact.parts — render/store it live.
}
}
final res = await turn.response;
print(res.artifacts); // artifacts produced this turn
print(chat.artifacts); // all artifacts tracked for the session
In multi-agent orchestration, the
agents()delegation middleware can merge sub-agent artifacts into the parent session (namespaced by an invocation id) via itsartifactStrategyoption.