Search the site
BACKEND
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
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.
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.
@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.
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.
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.
FSL-1.1-MIT licensed. Built with Dartvel.
Dartvel is made by
To the bottom