Dartvel, home
Docs
Features
Studio
Cloud
Compared

Search the site

GitHub
pub.dev

GETTING STARTED

Getting started
Existing Flutter apps
Existing Native apps
Run on your phone

APP

UI and styling
Routing
State
Accessibility
Keyboard shortcuts
Localization
Devices and desktop
Native device access
Media, 3D and XR

DATA

Data models
Forms
Search
Sync and offline
Import and export
Change capture
Database
Cache
File storage
Images
Privacy and erasure

BACKEND

Backend functions
Auth and sessions
Authorization
Queues and jobs
Workers and memory
Notifications and mail
Outbound HTTP
AI
Webhooks
GraphQL and OpenAPI
API keys and OAuth
Multi-tenancy
Billing and commerce
Modules

OPERATIONS

Edge security
Secrets and environments
Monitoring
Releases

SHIPPING

Build targets
Telegram Mini Apps
Static web hosting
Servers and deploying

REFERENCE

Testing
CLI reference
Coding agents

BACKEND

AI

Call Anthropic, OpenAI, Gemini, OpenRouter or a local Ollama model through one API, DV.AI.

Turn a backend function into a tool a model can call, with the JSON Schema written for you.

ON THIS PAGE

Pick a provider

Chat, extract and embed

Let a model call your code

Run a prompt with a budget and a fallback

Status

Pick a provider

void configureAI() {
  DV.AI.configure(AnthropicDVAIAdapter(
    apiKey: DV.Secrets.get('ANTHROPIC_API_KEY'),
  ));
}

Copy code to clipboard

AnthropicDVAIAdapter

Calls: Anthropic. No embeddings or transcription

OpenAIDVAIAdapter

Calls: OpenAI, including Whisper for transcription

OpenRouterDVAIAdapter

Calls: OpenRouter, with the OpenAI request shape

GeminiDVAIAdapter

Calls: Google Gemini

OllamaDVAIAdapter

Calls: A local Ollama server, localhost:11434 by default

LocalDVAIAdapter

Calls: Nothing. A predictable answer for tests and offline work

Nothing picks a provider for you. Call configure once at start-up, or every call throws.

Chat, extract and embed

final String summary = await DV.AI.chat('Summarise this ticket: $ticket');

final DVJsonObject triage = await DV.AI.structuredOutput(
  'Triage this ticket: $ticket',
  const <String, DVJsonValue>{
    'type': DVJsonString('object'),
    'properties': DVJsonMap(<String, DVJsonValue>{
      'urgent': DVJsonMap(<String, DVJsonValue>{'type': DVJsonString('boolean')}),
    }),
  },
);

final List<double> vector = await DV.AI.embed(ticket);

Copy code to clipboard

structuredOutput takes a JSON Schema and returns a map in that shape.

embed returns a vector you can store and search.

transcribe and runAgent are there too. A provider that cannot do one throws UnsupportedError.

Let a model call your code

@DVAITool(description: 'How many orders are waiting in a status')
Future<int> openOrders(String status) async => status == 'paid' ? 3 : 0;

Copy code to clipboard

dartvel routes writes a JSON Schema and a handler for each public @DVAITool function under lib/backend.

The generated backend registers every tool before it serves.

An argument of the wrong type is refused by name, so a tool never runs on a guessed value.

Serve your tools over MCP

DVMcpServer answers tools/list and tools/call for the registered tools, and DVMcpClient adopts the tools of another MCP server. dartvel mcp is a different server: it lets a coding agent read your project's routes, models and jobs.

Run a prompt with a budget and a fallback

final DVPrompts prompts = DVPrompts()
  ..register(
    const DVPrompt(id: 'ticket.summary', version: 4),
    const DVPromptTemplate(
      system: 'Summarise the ticket for a support agent.',
      input: <String, Type>{'body': String},
    ),
  );

final DVAIFeatures features = DVAIFeatures(
  prompts: prompts,
  adapter: const LocalDVAIAdapter(),
  model: 'local',
)..register(const DVAIFeature(
    prompt: 'ticket.summary',
    fallback: <DVAIFallback>[DVAIFallback.degrade],
  ));

final DVAIFeatureResult result = await features.run(
  'ticket.summary',
  input: <String, Object?>{'body': body},
  idempotencyKey: 'summary:$ticketId',
);
final String? summary = switch (result) {
  DVAIAnswered(output: DVJsonString(:final String value)) => value,
  _ => null, // degraded, refused by a budget, or the provider was down
};

Copy code to clipboard

Prompts are versioned. A stored override can be rolled back, and every change is kept in an audit list.

A budget is a usage meter checked before the call. A budgeted run needs an idempotency key, so a retry is counted once.

The result says what happened: answered, degraded, refused by the budget, or unavailable.

DVAIEval replays golden transcripts against a feature and fails when too few match.

Partial

Spec section: AI Operations

Planned work and implementation limits

Prompts and features are registered in code. No generator reads @DVPrompt or @DVAIFeature, and nothing is wired into DV.AI.

The prompt store is in memory, and there is no dartvel ai eval command.

Tokens are estimated at 4 characters each, since adapters report no usage.

Status

Partial

Spec section: AI

Planned work and implementation limits

A tool's schema has a type per parameter and no descriptions or enums.

A tool that returns a type DVJsonCodec cannot encode throws when the result is sent.

PREVIOUS Outbound HTTP Call APIs you have declared, with retries
NEXT Webhooks Signed events your customers subscribe to
GitHub
pub.dev
npm
Acknowledgements
Privacy
Terms

FSL-1.1-MIT licensed. Built with Dartvel.

Dartvel is made by

SigmaDev Digital

To the bottom