PRODUCTION AI FOR THE DART ECOSYSTEM

Build intelligent agents. Ship them in pure Dart.

Agents, multimodal providers, tools, memory, and streaming—inside one production-ready runtime.

Connect OpenAI, Gemini, Anthropic, or your own provider. Orchestrate typed tools, persistent threads, secure workflows, and real-time responses without leaving the Flint application architecture.

Works with
OpenAI
Gemini
Anthropic
Custom
agent_runtime.dart
LIVE

final agent = Agent( name: 'ReleasePilot', model: 'gpt-5', instructions: releasePolicy, tools: [searchDocs, createPlan], memory: threadMemory, ); final run = await ctx.ai.run( agent, input: request, stream: true, );

STREAM48 tok/s
TOOLS2 approved
TRACEpersisted
Multi-providerOne typed provider contract
Native streamingPartial chunks to final output
Policy guardedRoles and tool permissions
Persistent tracesThreads, runs, and artifacts
EVERY LAYER INCLUDED

A complete runtime for agentic applications

Move from a model response to a secure, observable AI product without assembling a fragile collection of unrelated packages.

REAL-TIME UX

Streaming chat and events

Stream text, structured events, tool progress, and completion state directly into reactive Flint UI experiences.

Partial token delivery
Typed lifecycle events
Backpressure-aware streams
AGENT RUNTIME

Typed agents and handoffs

Define instructions, models, tools, memory, and specialist handoffs as one composable Dart object graph.

Reusable agent definitions
Multi-agent delegation
Structured final outputs
SAFE EXECUTION

Tools with production policies

Expose application capabilities with schemas, authorization checks, audit context, and environment-aware restrictions.

Role and capability gates
Destructive-action policies
Tool execution traces
LONG-TERM CONTEXT

Memory and persistence

Persist threads, messages, summaries, run state, and artifacts through repository-backed storage.

Thread repositories
Run and event history
Durable agent memory
ORCHESTRATION

Workflows and background runs

Compose deterministic application steps with agent reasoning, tool calls, queues, and resumable state.

Typed workflow steps
Queue-ready execution
Failure recovery hooks
APP-NATIVE

Controllers, routes, and dashboards

Run AI beside your existing services, models, authentication, APIs, WebSockets, and server-rendered UI.

Context-aware controllers
Shared application models
Realtime dashboard updates
FROM REQUEST TO RESULT

One observable execution path

Every run moves through typed context, model reasoning, policy-checked tools, persistent memory, and a realtime response channel.

Typed inputValidate request context before execution.
Policy checksAuthorize every sensitive tool call.
Durable statePersist messages, runs, and artifacts.
Live outputStream progress and final responses.
Agent execution pipeline
OBSERVABLE
Request contextAuth, tenant, input schema
01
Agent reasoningProvider, model, instructions
02
Tool executionSchema validation and policies
03
Memory and traceThread, run, events, artifacts
04
Realtime responseChunks, status, final output
05
DEEP DOCUMENTATION

Build your first production agent

Providers, chat, streaming, tools, routes, workflows, persistence, and security—from setup to a complete support agent.

Start reading

AI Runtime

Flint AI gives Flint Dart applications a complete AI layer: chat providers, agents, tools, workflows, memory, trace events, database persistence, and production tool policies.

Use it when you want AI features that are part of your real application, not a separate script. A support agent can read a ticket, call a safe tool, draft a reply, store the thread, and leave a trace that staff can review later.

What You Build

A typical Flint AI feature has these layers:

  • Providers connect to OpenAI, Gemini, Anthropic, or another chat API.
  • Agents receive a goal and create a plan.
  • Tools perform controlled work such as summarizing data, drafting content, creating reports, queueing email, or calling internal services.
  • Workflows run named reusable operations without needing a full agent plan.
  • Memory and repository adapters store runs, trace events, threads, and artifacts.
  • Flint Dart adapters expose everything through app.ai and ctx.ai.

The normal execution flow is:

CodeBlock text
HTTP request -> ctx.ai.run()
             -> AiAgent.plan()
             -> AiToolPolicy.canExecute()
             -> AiTool.execute()
             -> AiAgent.synthesize()
             -> AiRunResult JSON

Install And Setup

Flint Dart re-exports the AI runtime from package:flint_dart/ai.dart.

CodeBlock dart
import 'package:flint_dart/flint_dart.dart';
import 'package:flint_dart/ai.dart';

When using the standalone package outside Flint Dart, install flint_ai directly:

CodeBlock bash
dart pub add flint_ai

For Flint Dart apps, use app.ai:

CodeBlock dart
final app = Flint();

app.ai.useChatProvidersFromEnv();

Configure Providers From .env

Provider setup can be manual, but the easiest Flint Dart path is to use the .env helpers.

CodeBlock dart
final registered = app.ai.useChatProvidersFromEnv();

This registers every provider with credentials present in the environment.

Supported keys:

  • OPENAIAPIKEY
  • OPENAIBEARERTOKEN
  • OPENAICHATENDPOINT
  • GEMINIAPIKEY
  • GEMINIBEARERTOKEN
  • GEMINICHATENDPOINT
  • ANTHROPICAPIKEY
  • ANTHROPICCHATENDPOINT

Example .env:

CodeBlock env
OPENAI_API_KEY=sk-your-key
GEMINI_API_KEY=your-gemini-key
ANTHROPIC_API_KEY=your-anthropic-key

You can also register one provider at a time:

CodeBlock dart
app.ai.useOpenAiFromEnv();
app.ai.useGeminiFromEnv();
app.ai.useAnthropicFromEnv();

Manual provider registration is also supported:

CodeBlock dart
app.ai.registerChatProvider(
  OpenAiChatProvider(apiKey: 'openai-key'),
);

Chat Completions

Use ctx.ai.chat() for a normal non-streaming chat response.

CodeBlock dart
app.post('/ai/chat', (Context ctx) async {
  final body = await ctx.req.json();

  final result = await ctx.ai.chat(
    providerId: body['provider']?.toString() ?? 'openai',
    request: ChatRequest(
      model: body['model']?.toString() ?? 'gpt-4o-mini',
      messages: [
        ChatMessage(
          role: 'user',
          content: body['message']?.toString() ?? 'Say hello from Flint',
        ),
      ],
    ),
  );

  return ctx.res?.json({
    'status': true,
    'data': result.toMap(),
  });
});

The built-in chat providers are:

  • OpenAiChatProvider
  • GeminiChatProvider
  • AnthropicChatProvider

Streaming Chat

Use ctx.ai.streamChat() when you want the UI to render text as it arrives.

Providers emit:

  • chat.delta for incremental text chunks
  • chat.completed when the response is finished
CodeBlock dart
app.get('/ai/chat/stream', (Context ctx) async {
  final events = <Map<String, dynamic>>[];

  await for (final event in ctx.ai.streamChat(
    providerId: 'openai',
    request: const ChatRequest(
      model: 'gpt-4o-mini',
      messages: [
        ChatMessage(role: 'user', content: 'Write a short welcome note'),
      ],
    ),
  )) {
    events.add({
      'type': event.type,
      'payload': event.payload,
    });
  }

  return ctx.res?.json({
    'status': true,
    'events': events,
  });
});

OpenAiChatProvider, GeminiChatProvider, and AnthropicChatProvider support native server-sent event streaming.

Create An Agent

An agent receives an AiGoal, creates an AiPlan, and returns structured output after the plan runs.

CodeBlock dart
class SupportAgent extends AiAgent {
  @override
  String get name => 'support_agent';

  @override
  Future<AiPlan> plan(AiRunContext context) async {
    return AiPlan(
      steps: [
        AiPlanStep(
          id: 'support_summary',
          type: 'tool',
          description: 'Prepare support summary and customer draft',
          toolName: 'support.summary',
          arguments: context.goal.input,
        ),
      ],
    );
  }

  @override
  Future<Map<String, dynamic>> synthesize(AiRunContext context) async {
    final summary = Map<String, dynamic>.from(
      context.state['support_summary'] as Map? ?? const {},
    );
    return {
      'summary': summary['summary'],
      'draft': summary['draft'],
      'category': summary['category'],
      'recommendedActions': summary['recommendedActions'],
    };
  }
}

Agents should not perform side effects directly. Put side effects inside tools so Flint can authorize and trace them.

Create A Tool

Tools are the side-effect boundary. They can read arguments, access the current user and tenant ids, use metadata, and return structured JSON-like data.

CodeBlock dart
class SupportSummaryTool extends AiTool {
  @override
  String get name => 'support.summary';

  @override
  String get description => 'Summarizes a support issue and drafts a reply.';

  @override
  Set<String> get requiredCapabilities => const {'support:write'};

  @override
  Future<Map<String, dynamic>> execute(AiToolContext context) async {
    final issue = context.arguments['issue']?.toString().trim() ?? '';
    final priority = context.arguments['priority']?.toString() ?? 'normal';

    return {
      'category': issue.toLowerCase().contains('login')
          ? 'account_access'
          : 'general_support',
      'summary': '[$priority] $issue',
      'draft':
          'Thank you for reaching out. We have reviewed this request and will follow up with the next steps.',
      'recommendedActions': [
        'review customer account',
        'confirm details',
        'send response',
      ],
    };
  }
}

Register tools during app startup:

CodeBlock dart
app.ai.registerTool(SupportSummaryTool());

Run The Agent From A Route

CodeBlock dart
app.post('/ai/support', (Context ctx) async {
  final body = await ctx.req.json();

  final result = await ctx.ai.run(
    agent: SupportAgent(),
    goal: AiGoal(
      task: 'Prepare support response',
      input: {
        'issue': body['issue'],
        'priority': body['priority'] ?? 'normal',
      },
    ),
    userId: 'support-user-1',
    threadId: body['threadId']?.toString(),
    context: ctx,
    metadata: {
      'role': 'SUPPORT',
      'capabilities': ['support:write'],
    },
  );

  return ctx.res?.json({
    'status': true,
    'data': result.toMap(),
  });
});

metadata is important in production because the tool policy can read the current role and capabilities before allowing a tool to run.

Save Thread Messages

Use thread memory when building chat or support experiences.

CodeBlock dart
await ctx.ai.saveThreadMessage(threadId, {
  'role': 'user',
  'content': body['issue'],
  'source': 'support_dashboard',
});

final thread = await ctx.ai.loadThreadMessages(threadId);

You can also load run events:

CodeBlock dart
final events = await ctx.ai.loadRunEvents(result.run.id);

Workflows

Use workflows for named reusable operations that do not need a full agent planning step.

CodeBlock dart
class SupportFollowupWorkflow extends AiWorkflow {
  @override
  String get name => 'support_followup';

  @override
  String get description => 'Creates a support follow-up payload.';

  @override
  Future<Map<String, dynamic>> run(AiWorkflowContext context) async {
    return {
      'status': 'queued',
      'userId': context.userId,
      'threadId': context.threadId,
      'nextStep': 'Review draft and send customer update',
    };
  }
}

app.ai.registerWorkflow(SupportFollowupWorkflow());

Run a workflow:

CodeBlock dart
final workflow = await ctx.ai.runWorkflow(
  'support_followup',
  userId: 'support-user-1',
  threadId: 'support-thread-1',
  input: {'priority': 'high'},
  context: ctx,
);

Production Tool Security

AI agents should not be allowed to run every tool. Use production policy when tools can send messages, update records, publish content, delete data, charge money, issue refunds, or call external services.

CodeBlock dart
app.ai.useProductionToolPolicyFromEnv();

Supported policy keys:

  • AIALLOWEDTOOLS
  • AIALLOWEDCAPABILITIES
  • AIALLOWEDROLES

Example .env:

CodeBlock env
AI_ALLOWED_ROLES=ADMIN
AI_ALLOWED_CAPABILITIES=support:write,reporting:read,content:write

Production guidance:

  • Do not set enabledByDefault on destructive tools.
  • Give each destructive tool a specific requiredCapabilities value.
  • Pass userId to ctx.ai.run().
  • Pass role and capabilities through metadata.
  • Prefer human review or queue workflows for high-risk actions.

You can also set the policy manually:

CodeBlock dart
app.ai.toolPolicy = const ProductionAiToolPolicy(
  allowedRoles: {'ADMIN'},
  allowedCapabilities: {'support:write'},
  requireUserBinding: true,
);

Memory And Persistence

The standalone flint_ai package defaults to in-memory stores. Flint Dart adds database-backed adapters and uses them for app.ai.

CodeBlock dart
final app = Flint();

app.ai.memoryStore; // FlintAutoAiMemoryStore
app.ai.repository;  // FlintAutoAiRepository

The auto stores use the Flint database when it is connected. If the database is not connected, or the AI tables have not been migrated yet, they fall back to in-memory stores and log a warning.

For explicit production wiring:

CodeBlock dart
final ai = FlintAi.production(
  memoryStore: FlintAutoAiMemoryStore(),
  repository: FlintAutoAiRepository(),
);

Register AI Tables

Register the AI table definitions in lib/config/table_registry.dart:

CodeBlock dart
import 'dart:isolate';

import 'package:flint_dart/ai.dart';
import 'package:flint_dart/schema.dart';

void main(_, SendPort? sendPort) {
  runTableRegistry([
    ...flintAiTables,
    // your app tables...
  ], _, sendPort);
}

Then run:

CodeBlock bash
flint migrate

The AI table registry includes:

  • ai_runs
  • ai_traces
  • ai_threads
  • ai_artifacts
  • airunevents
  • aithreadmessages

Connect the database before handling production traffic so AI state survives restarts and is shared across workers.

Full Example: Support Agent

This combines provider setup, tool registration, route execution, memory, and events.

CodeBlock dart
import 'package:flint_dart/flint_dart.dart';
import 'package:flint_dart/ai.dart';

void main() {
  final app = Flint();

  app.ai.useChatProvidersFromEnv();
  app.ai.useProductionToolPolicyFromEnv();
  app.ai.registerTool(SupportSummaryTool());
  app.ai.registerWorkflow(SupportFollowupWorkflow());

  app.post('/api/ai/support', (Context ctx) async {
    final body = await ctx.req.json();
    final threadId = body['threadId']?.toString() ??
        'support-${DateTime.now().millisecondsSinceEpoch}';

    await ctx.ai.saveThreadMessage(threadId, {
      'role': 'user',
      'content': body['issue'],
    });

    final result = await ctx.ai.run(
      agent: SupportAgent(),
      goal: AiGoal(
        task: 'Prepare support response',
        input: body.cast<String, dynamic>(),
      ),
      userId: 'support-user-1',
      threadId: threadId,
      context: ctx,
      metadata: {
        'role': 'SUPPORT',
        'capabilities': ['support:write'],
      },
    );

    await ctx.ai.saveThreadMessage(threadId, {
      'role': 'assistant',
      'content': result.output['draft'],
    });

    return ctx.res?.json({
      'status': true,
      'data': {
        'run': result.toMap(),
        'thread': await ctx.ai.loadThreadMessages(threadId),
        'events': await ctx.ai.loadRunEvents(result.run.id),
      },
    });
  });

  app.listen();
}

Sample App And Eulogia Pattern

The Flint Dart sample app includes real AI runtime examples:

  • POST /ai-demo/support
  • GET /ai-demo/reporting
  • POST /ai-demo/content-email

The Eulogia app uses the same pattern under:

  • POST /api/ai-agents/support
  • POST /api/ai-agents/reporting
  • POST /api/ai-agents/content-email

Those endpoints are staff-protected, use ctx.ai, pass role/capability metadata, and store run events through the Flint AI memory and repository adapters.

YOUR NEXT INTELLIGENT PRODUCT

Turn your Flint application into an AI-native platform.

Start with one provider and one typed tool. Scale into agents, workflows, memory, and realtime product experiences without changing languages.

Flint Dart logo
Flint EcosystemThe Unified Dart Technology Stack

One language powering Full-Stack Web, Cross-Platform Clients, Native AI, and Connected Robotics.

Fullstack
Client SDK
AI Engine
Hardware
Copyright 2026 Flint Dart. Maintained by Eulogia Technologies.
v 1.3.2
MIT License
Built with Dart