ack_firebase_ai

Firebase AI (Gemini) schema converter for the ACK validation library.

pub package

Overview

Converts ACK schemas to Firebase AI Schema format via .toFirebaseAiSchema(). Assumes familiarity with ACK and firebase_ai.

Installation

dependencies:
  ack: ^1.0.0
  ack_firebase_ai: ^1.0.0
  firebase_ai: ^3.4.0  # Required peer dependency

Compatibility

Requires firebase_ai: >=3.4.0 <5.0.0 as a peer dependency. Report compatibility issues.

Limitations ⚠️

Read this first - Firebase AI schema conversion has important constraints:

1. Gemini Doesn't Enforce Schemas

Firebase AI schemas are hints, not validation. Always validate output with ACK.

// Gemini may ignore constraints
final schema = Ack.integer().min(0).max(10);
final geminiSchema = schema.toFirebaseAiSchema();

final model = FirebaseAI.instance.generativeModel(
  model: 'gemini-1.5-pro',
  generationConfig: GenerationConfig(
    responseMimeType: 'application/json',
    responseSchema: geminiSchema,
  ),
);

final response = await model.generateContent([...]);
// Response may contain values outside 0-10 range!

// ALWAYS validate with ACK
final result = schema.safeParse(response.data);
if (!result.isOk) {
  // Handle validation failure
}

2. String Length Not Enforced

Firebase AI Schema (as of firebase_ai v3.6.0) does not expose minLength/maxLength properties in the serialized JSON schema. These constraints exist in ACK schemas but cannot be passed to Gemini. Always validate AI output with ACK after generation.

Why: This is a limitation of the Firebase AI Dart SDK's Schema API, not the Gemini API itself. The underlying Gemini API supports string constraints, but they're not yet surfaced in the typed Schema class.

Workaround: Validate all AI responses with your ACK schema to enforce length constraints client-side.

final schema = Ack.string().minLength(5).maxLength(20);
final geminiSchema = schema.toFirebaseAiSchema();
// Length constraints not included in geminiSchema.toJson()
// Validate with ACK: schema.safeParse(aiResponse)

3. Refinements Unsupported

Custom validation logic cannot be expressed in Firebase AI schemas.

// Cannot convert
final schema = Ack.string().refine((s) => s.startsWith('ACK_'));

// Validate AI output with ACK schema
final result = schema.safeParse(aiResponse);

4. Regex Patterns Limited

Patterns not supported - use enums or post-validation.

// Not convertible
final schema = Ack.string().matches(r'^[A-Z]{3}-\d{5}$');

// Alternative: enum or validate after
final schema = Ack.enumString(['ABC-12345', 'DEF-67890']);

5. Default Values Not Passed

Defaults are ACK-only. Apply after parsing.

final schema = Ack.string().withDefault('default');
schema.toFirebaseAiSchema(); // Default not included
// Apply defaults after parsing AI response

6. oneOf Converted to anyOf

Firebase AI API only supports anyOf. Schemas using oneOf (exactly-one-of semantics via discriminated unions) are converted to anyOf (at-least-one-of), which may accept additional values.

// oneOf semantics: exactly one branch should match
final schema = Ack.discriminated(
  discriminatorKey: 'type',
  schemas: {
    'circle': Ack.object({'radius': Ack.double()}),
    'square': Ack.object({'side': Ack.double()}),
  },
);
final geminiSchema = schema.toFirebaseAiSchema();
// Converted to anyOf - exclusivity not enforced
// Always validate with ACK to ensure single branch match

7. Transformed Schemas

Supported - metadata overrides (description, title, nullable) work correctly.

final dateSchema = Ack.date(); // TransformedSchema
final geminiSchema = dateSchema.toFirebaseAiSchema(); // Works

Usage

import 'package:ack/ack.dart';
import 'package:ack_firebase_ai/ack_firebase_ai.dart';
import 'package:firebase_ai/firebase_ai.dart';

// 1. Define schema
final userSchema = Ack.object({
  'name': Ack.string().minLength(2).maxLength(50),
  'email': Ack.string().email(),
  'age': Ack.integer().min(0).max(120).optional(),
});

// 2. Convert to Firebase AI
final geminiSchema = userSchema.toFirebaseAiSchema();

// 3. Use with Gemini
final model = FirebaseAI.instance.generativeModel(
  model: 'gemini-1.5-pro',
  generationConfig: GenerationConfig(
    responseMimeType: 'application/json',
    responseSchema: geminiSchema,
  ),
);

final response = await model.generateContent([
  Content.text('Generate a user'),
]);

// 4. ALWAYS validate with ACK (Gemini may violate schema)
final result = userSchema.safeParse(response.data);
if (result.isOk) {
  final user = result.getOrThrow();
  print('Valid: $user');
} else {
  print('Invalid: ${result.getError()}');
}

Schema Mapping

Supported Types

ACK Type Firebase AI Type Conversion details
Ack.string() string Full support
Ack.integer() integer Full support
Ack.double() number Full support
Ack.boolean() boolean Full support
Ack.object({...}) object Full support
Ack.list(...) array Full support
Ack.enumString([...]) string with enum Full support
Ack.anyOf([...]) anyOf array Full support
Ack.any() object Converts to empty object

Supported Constraints

ACK Constraint Firebase AI Notes
.minLength() / .maxLength() minItems / maxItems (arrays only) String length not surfaced - validate with ACK
.min() / .max() minimum / maximum Numeric bounds
.email() / .uuid() / .url() format Format hints (enforcement varies by model)
.nullable() nullable: true Null support
.optional() Excluded from required Optional fields
.describe() description Descriptions

Testing

Requires Flutter (firebase_ai dependency). Use flutter test, not dart test.

cd packages/ack_firebase_ai
flutter test

Contributing

For contribution guidelines, see CONTRIBUTING.md in the root repository.

License

This package is part of the ACK monorepo.

Libraries

ack_firebase_ai
Firebase AI (Gemini) schema converter for ACK validation library.