datasapien_sdk 0.35.0 copy "datasapien_sdk: ^0.35.0" to clipboard
datasapien_sdk: ^0.35.0 copied to clipboard

Flutter plugin wrapper for DataSapien iOS and Android SDKs

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:datasapien_sdk/datasapien_sdk.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await dotenv.load(fileName: '.env');
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _status = 'Not initialized';
  String? _resultPreview;
  String? _errorMessage;
  bool _isInitialized = false;
  bool _isLoading = false;
  SelectedService _selectedService = SelectedService.backup;

  // Backup state
  String? _backupJson;

  // Intelligence state
  double? _downloadProgress;
  String? _streamingText;

  // Input controllers (kept minimal, but cover all actions)
  final TextEditingController meDataNameController =
      TextEditingController(text: 'step_count');
  final TextEditingController meDataCategoryController =
      TextEditingController(text: 'health');
  final TextEditingController meDataValuesJsonController =
      TextEditingController(text: '1002');
  final TextEditingController meDataCollectNamesController =
      TextEditingController(text: 'step_count,height,weight');

  final TextEditingController audienceSegmentNameController =
      TextEditingController(text: 'ab_testing-a');

  final TextEditingController managedApiNameController =
      TextEditingController(text: 'youtube');

  final TextEditingController journeyNameController =
      TextEditingController(text: 'meal-planner');
  final TextEditingController journeyDataJsonController =
      TextEditingController(text: '{"key": "value"}');

  final TextEditingController intelligenceModelNameController =
      TextEditingController(text: 'SmolLM-135M.Q4_0');
  /// Key identifying the loaded model instance (used for load, unload, invoke, stop).
  final TextEditingController intelligenceModelKeyController =
      TextEditingController(text: 'default');
  final TextEditingController intelligencePromptController =
      TextEditingController(text: 'Hello, how are you?');
  final TextEditingController intelligenceRuleNameController =
      TextEditingController(text: 'sapien_dna');

  @override
  void dispose() {
    meDataNameController.dispose();
    meDataCategoryController.dispose();
    meDataValuesJsonController.dispose();
    meDataCollectNamesController.dispose();
    audienceSegmentNameController.dispose();
    managedApiNameController.dispose();
    journeyNameController.dispose();
    journeyDataJsonController.dispose();
    intelligenceModelNameController.dispose();
    intelligenceModelKeyController.dispose();
    intelligencePromptController.dispose();
    intelligenceRuleNameController.dispose();
    super.dispose();
  }

  void setRunning(String action) {
    setState(() {
      _status = 'Running $action...';
      _errorMessage = null;
    });
  }

  void setSuccess(String action, Object? result) {
    String? preview;
    if (result != null) {
      try {
        const encoder = JsonEncoder.withIndent('  ');
        preview = encoder.convert(result);
      } catch (_) {
        preview = result.toString();
      }
      if (preview.length > 800) {
        preview = '${preview.substring(0, 800)}...';
      }
    }
    setState(() {
      _status = '$action succeeded';
      _resultPreview = preview;
    });
  }

  void setError(String action, Object e, StackTrace stackTrace) {
    final errorMsg = '$e\n\nStack trace:\n$stackTrace';
    debugPrint('❌ $action Error: $e');
    debugPrint('Stack trace: $stackTrace');
    setState(() {
      _status = '$action failed';
      _errorMessage = errorMsg;
    });
  }

  bool ensureInitialized(String actionName) {
    if (_isInitialized) return true;
    const errorMsg = 'SDK must be initialized first';
    debugPrint('❌ $actionName Error: $errorMsg');
    setState(() {
      _errorMessage = errorMsg;
    });
    return false;
  }

  Future<void> _initializeSdk() async {
    if (_isLoading) return;
    
    setState(() {
      _isLoading = true;
      _errorMessage = null;
      _status = 'Initializing...';
    });

    try {
      String envRequired(String key) {
        final value = dotenv.env[key];
        if (value == null || value.trim().isEmpty) {
          throw Exception(
            'Missing $key in sdk/example/.env.\n'
            'Copy sdk/example/.env.example to sdk/example/.env and fill in values.',
          );
        }
        return value.trim();
      }

      final config = DataSapienConfig.builder()
        .setAuth(
          authUrl: envRequired('DATASAPIEN_AUTH_URL'),
          authClientId: envRequired('DATASAPIEN_CLIENT_ID'),
          authClientSecret: envRequired('DATASAPIEN_CLIENT_SECRET'),
          authScope: envRequired('DATASAPIEN_SCOPE'),
        )
        .setHostUrl(envRequired('DATASAPIEN_HOST_URL'))
        .setMediaUrl(envRequired('DATASAPIEN_MEDIA_URL'))
        .setMainColor('#F37102')
        .setDebug(true)
        .build();

      await DataSapien.initialize(config);
      setState(() {
        _status = 'Initialized';
      });

      setState(() {
        _status = 'Setting up...';
      });
      await DataSapien.setup();
      setState(() {
        _status = 'Ready';
        _isInitialized = true;
        _isLoading = false;
      });
    } catch (e, stackTrace) {
      setState(() {
        _status = 'Error';
        _errorMessage = '${e.toString()}\n\nStack trace:\n$stackTrace';
        _isLoading = false;
      });
    }
  }

  // -------------------------
  // Actions (inlined from services/* to keep pub.dev example self-contained)
  // -------------------------

  Future<void> _createBackup() async {
    if (!ensureInitialized('Create Backup')) return;
    try {
      setRunning('Create Backup');
      final backupService = DataSapien.getBackupService();
      final json = await backupService.createBackup();
      setState(() {
        _backupJson = json;
      });
      setSuccess('Create Backup', json);
    } catch (e, stackTrace) {
      setError('Create Backup', e, stackTrace);
    }
  }

  Future<void> _restoreBackup() async {
    if (!ensureInitialized('Restore Backup')) return;
    if (_backupJson == null) {
      setError(
        'Restore Backup',
        StateError('No backup to restore. Create a backup first.'),
        StackTrace.current,
      );
      return;
    }
    try {
      setRunning('Restore Backup');
      final backupService = DataSapien.getBackupService();
      await backupService.restore(_backupJson!);
      setSuccess('Restore Backup', null);
    } catch (e, stackTrace) {
      setError('Restore Backup', e, stackTrace);
    }
  }

  List<ActionItem> _backupActions() => [
        ActionItem(
          label: 'Create Backup',
          description: 'Calls BackupService.createBackup()',
          onTap: _createBackup,
        ),
        ActionItem(
          label: 'Restore Backup',
          description: 'Calls BackupService.restore() with last backup JSON',
          onTap: _restoreBackup,
        ),
      ];

  Future<void> _runMeDataSyncDefinitions() async {
    if (!ensureInitialized('syncMeDataDefinitions')) return;
    setRunning('syncMeDataDefinitions');
    try {
      final service = DataSapien.getMeDataService();
      await service.syncMeDataDefinitions();
      setSuccess('syncMeDataDefinitions', null);
    } catch (e, stackTrace) {
      setError('syncMeDataDefinitions', e, stackTrace);
    }
  }

  Future<void> _runMeDataGetDefinitions() async {
    if (!ensureInitialized('getMeDataDefinitions')) return;
    setRunning('getMeDataDefinitions');
    try {
      final service = DataSapien.getMeDataService();
      final defs = await service.getMeDataDefinitions();
      setSuccess('getMeDataDefinitions', defs);
    } catch (e, stackTrace) {
      setError('getMeDataDefinitions', e, stackTrace);
    }
  }

  Future<void> _runMeDataGetDefinition() async {
    if (!ensureInitialized('getMeDataDefinition')) return;
    setRunning('getMeDataDefinition');
    try {
      final name = meDataNameController.text.trim();
      final service = DataSapien.getMeDataService();
      final def = await service.getMeDataDefinition(name);
      setSuccess('getMeDataDefinition', def);
    } catch (e, stackTrace) {
      setError('getMeDataDefinition', e, stackTrace);
    }
  }

  Future<void> _runMeDataGetCategories() async {
    if (!ensureInitialized('getMeDataCategories')) return;
    setRunning('getMeDataCategories');
    try {
      final service = DataSapien.getMeDataService();
      final categories = await service.getMeDataCategories();
      setSuccess('getMeDataCategories', categories);
    } catch (e, stackTrace) {
      setError('getMeDataCategories', e, stackTrace);
    }
  }

  Future<void> _runMeDataGetDefinitionsByCategory() async {
    if (!ensureInitialized('getMeDataDefinitionsByCategory')) return;
    setRunning('getMeDataDefinitionsByCategory');
    try {
      final name = meDataCategoryController.text.trim();
      final service = DataSapien.getMeDataService();
      final defs = await service.getMeDataDefinitionsByCategory(name);
      setSuccess('getMeDataDefinitionsByCategory', defs);
    } catch (e, stackTrace) {
      setError('getMeDataDefinitionsByCategory', e, stackTrace);
    }
  }

  Future<void> _runMeDataGetRecords() async {
    if (!ensureInitialized('getMeDataRecords')) return;
    setRunning('getMeDataRecords');
    try {
      final name = meDataNameController.text.trim();
      final service = DataSapien.getMeDataService();
      final records = await service.getMeDataRecords(name);
      setSuccess('getMeDataRecords', records);
    } catch (e, stackTrace) {
      setError('getMeDataRecords', e, stackTrace);
    }
  }

  Future<void> _runMeDataGetLastRecord() async {
    if (!ensureInitialized('getLastMeDataRecord')) return;
    setRunning('getLastMeDataRecord');
    try {
      final name = meDataNameController.text.trim();
      final service = DataSapien.getMeDataService();
      final record = await service.getLastMeDataRecord(name);
      setSuccess('getLastMeDataRecord', record);
    } catch (e, stackTrace) {
      setError('getLastMeDataRecord', e, stackTrace);
    }
  }

  Future<void> _runMeDataSaveRecord() async {
    if (!ensureInitialized('saveMeDataRecord')) return;
    setRunning('saveMeDataRecord');
    try {
      final name = meDataNameController.text.trim();
      final raw = meDataValuesJsonController.text.trim();
      dynamic values;
      if (raw.isEmpty) {
        values = null;
      } else {
        values = jsonDecode(raw);
      }
      final service = DataSapien.getMeDataService();
      await service.saveMeDataRecord(name, values);
      setSuccess('saveMeDataRecord', null);
    } catch (e, stackTrace) {
      setError('saveMeDataRecord', e, stackTrace);
    }
  }

  Future<void> _runMeDataSaveLocationRecord() async {
    if (!ensureInitialized('saveLocationMeDataRecord')) return;
    setRunning('saveLocationMeDataRecord');
    try {
      final name = meDataNameController.text.trim();
      final raw = meDataValuesJsonController.text.trim();
      Location? location;

      if (raw.isNotEmpty) {
        final decoded = jsonDecode(raw);
        location = Location.tryParse(decoded);
        if (location == null) {
          throw const FormatException(
            'Expected JSON object with numeric lat/lng, e.g. {"lat":41.0,"lng":29.0}',
          );
        }
      }

      final service = DataSapien.getMeDataService();
      await service.saveLocationMeDataRecord(name, location);
      setSuccess('saveLocationMeDataRecord', location?.toJson());
    } catch (e, stackTrace) {
      setError('saveLocationMeDataRecord', e, stackTrace);
    }
  }

  Future<void> _runMeDataCollect() async {
    if (!ensureInitialized('collectMeData')) return;
    setRunning('collectMeData');
    try {
      final raw = meDataCollectNamesController.text;
      final names = raw
          .split(',')
          .map((s) => s.trim())
          .where((s) => s.isNotEmpty)
          .toList();
      final service = DataSapien.getMeDataService();
      final collected = await service.collectMeData(names);
      setSuccess('collectMeData', collected);
    } catch (e, stackTrace) {
      setError('collectMeData', e, stackTrace);
    }
  }

  List<ActionItem> _meDataActions() => [
        ActionItem(
          label: 'syncMeDataDefinitions',
          description: 'Synchronize MeData definitions from Orchestrator',
          onTap: _runMeDataSyncDefinitions,
        ),
        ActionItem(
          label: 'getMeDataDefinitions',
          description: 'Fetch all MeData definitions',
          onTap: _runMeDataGetDefinitions,
        ),
        ActionItem(
          label: 'getMeDataDefinition',
          description: 'Fetch a single MeData definition by name',
          onTap: _runMeDataGetDefinition,
        ),
        ActionItem(
          label: 'getMeDataCategories',
          description: 'Fetch MeData categories',
          onTap: _runMeDataGetCategories,
        ),
        ActionItem(
          label: 'getMeDataDefinitionsByCategory',
          description: 'Fetch definitions by category name',
          onTap: _runMeDataGetDefinitionsByCategory,
        ),
        ActionItem(
          label: 'getMeDataRecords',
          description: 'Fetch all records for a MeData definition',
          onTap: _runMeDataGetRecords,
        ),
        ActionItem(
          label: 'getLastMeDataRecord',
          description: 'Fetch last record for a MeData definition',
          onTap: _runMeDataGetLastRecord,
        ),
        ActionItem(
          label: 'saveMeDataRecord',
          description: 'Save a new MeData record (name + values)',
          onTap: _runMeDataSaveRecord,
        ),
        ActionItem(
          label: 'saveLocationMeDataRecord',
          description: 'Save LOCATION MeData (JSON: {"lat":..,"lng":..})',
          onTap: _runMeDataSaveLocationRecord,
        ),
        ActionItem(
          label: 'collectMeData',
          description: 'Collect MeData for a list of definition names',
          onTap: _runMeDataCollect,
        ),
      ];

  Future<void> _runAudienceSyncSegmentDefinitions() async {
    if (!ensureInitialized('syncSegmentDefinitions')) return;
    setRunning('syncSegmentDefinitions');
    try {
      final service = DataSapien.getAudienceService();
      await service.syncSegmentDefinitions();
      setSuccess('syncSegmentDefinitions', null);
    } catch (e, stackTrace) {
      setError('syncSegmentDefinitions', e, stackTrace);
    }
  }

  Future<void> _runAudienceGetSegmentDefinitions() async {
    if (!ensureInitialized('getSegmentDefinitions')) return;
    setRunning('getSegmentDefinitions');
    try {
      final service = DataSapien.getAudienceService();
      final defs = await service.getSegmentDefinitions();
      setSuccess('getSegmentDefinitions', defs.map((e) => e.toJson()).toList());
    } catch (e, stackTrace) {
      setError('getSegmentDefinitions', e, stackTrace);
    }
  }

  Future<void> _runAudienceGetSegmentDefinition() async {
    if (!ensureInitialized('getSegmentDefinition')) return;
    setRunning('getSegmentDefinition');
    try {
      final name = audienceSegmentNameController.text.trim();
      final service = DataSapien.getAudienceService();
      final def = await service.getSegmentDefinition(name);
      setSuccess('getSegmentDefinition', def?.toJson());
    } catch (e, stackTrace) {
      setError('getSegmentDefinition', e, stackTrace);
    }
  }

  Future<void> _runAudienceSyncSegmentSubscriptions() async {
    if (!ensureInitialized('syncSegmentSubscriptions')) return;
    setRunning('syncSegmentSubscriptions');
    try {
      final service = DataSapien.getAudienceService();
      await service.syncSegmentSubscriptions();
      setSuccess('syncSegmentSubscriptions', null);
    } catch (e, stackTrace) {
      setError('syncSegmentSubscriptions', e, stackTrace);
    }
  }

  Future<void> _runAudienceSyncAudiences() async {
    if (!ensureInitialized('syncAudiences')) return;
    setRunning('syncAudiences');
    try {
      final service = DataSapien.getAudienceService();
      await service.syncAudiences();
      setSuccess('syncAudiences', null);
    } catch (e, stackTrace) {
      setError('syncAudiences', e, stackTrace);
    }
  }

  List<ActionItem> _audienceActions() => [
        ActionItem(
          label: 'syncSegmentDefinitions',
          description: 'Synchronize segment definitions from Orchestrator',
          onTap: _runAudienceSyncSegmentDefinitions,
        ),
        ActionItem(
          label: 'getSegmentDefinitions',
          description: 'Fetch all segment definitions',
          onTap: _runAudienceGetSegmentDefinitions,
        ),
        ActionItem(
          label: 'getSegmentDefinition',
          description: 'Fetch a single segment definition by name',
          onTap: _runAudienceGetSegmentDefinition,
        ),
        ActionItem(
          label: 'syncSegmentSubscriptions',
          description: 'Synchronize segment subscriptions with backend',
          onTap: _runAudienceSyncSegmentSubscriptions,
        ),
        ActionItem(
          label: 'syncAudiences',
          description: 'Synchronize audiences from Orchestrator',
          onTap: _runAudienceSyncAudiences,
        ),
      ];

  Future<void> _runManagedApiSync() async {
    if (!ensureInitialized('syncManagedAPIs')) return;
    setRunning('syncManagedAPIs');
    try {
      final service = DataSapien.getManagedAPIService();
      await service.syncManagedAPIs();
      setSuccess('syncManagedAPIs', null);
    } catch (e, stackTrace) {
      setError('syncManagedAPIs', e, stackTrace);
    }
  }

  Future<void> _runManagedApiGetList() async {
    if (!ensureInitialized('getManagedAPIs')) return;
    setRunning('getManagedAPIs');
    try {
      final service = DataSapien.getManagedAPIService();
      final apis = await service.getManagedAPIs();
      setSuccess('getManagedAPIs', apis.map((e) => e.toJson()).toList());
    } catch (e, stackTrace) {
      setError('getManagedAPIs', e, stackTrace);
    }
  }

  Future<void> _runManagedApiGetOne() async {
    if (!ensureInitialized('getManagedAPI')) return;
    setRunning('getManagedAPI');
    try {
      final name = managedApiNameController.text.trim();
      final service = DataSapien.getManagedAPIService();
      final api = await service.getManagedAPI(name);
      setSuccess('getManagedAPI', api?.toJson());
    } catch (e, stackTrace) {
      setError('getManagedAPI', e, stackTrace);
    }
  }

  List<ActionItem> _managedApiActions() => [
        ActionItem(
          label: 'syncManagedAPIs',
          description: 'Synchronize managed APIs from backend',
          onTap: _runManagedApiSync,
        ),
        ActionItem(
          label: 'getManagedAPIs',
          description: 'Fetch all available managed APIs',
          onTap: _runManagedApiGetList,
        ),
        ActionItem(
          label: 'getManagedAPI',
          description: 'Fetch a single managed API by name',
          onTap: _runManagedApiGetOne,
        ),
      ];

  Future<void> _runJourneySync() async {
    if (!ensureInitialized('syncJourneys')) return;
    setRunning('syncJourneys');
    try {
      final service = DataSapien.getJourneyService();
      await service.syncJourneys();
      setSuccess('syncJourneys', null);
    } catch (e, stackTrace) {
      setError('syncJourneys', e, stackTrace);
    }
  }

  Future<void> _runJourneyGetList() async {
    if (!ensureInitialized('getJourneys')) return;
    setRunning('getJourneys');
    try {
      final service = DataSapien.getJourneyService();
      final journeys = await service.getJourneys();
      setSuccess('getJourneys', journeys.map((e) => e.name).toList());
    } catch (e, stackTrace) {
      setError('getJourneys', e, stackTrace);
    }
  }

  Future<void> _runJourneyRun() async {
    if (!ensureInitialized('runJourney')) return;
    setRunning('runJourney');
    try {
      final name = journeyNameController.text.trim();
      final raw = journeyDataJsonController.text.trim();
      Map<String, dynamic>? data;
      if (raw.isNotEmpty) {
        data = jsonDecode(raw) as Map<String, dynamic>;
      }
      final service = DataSapien.getJourneyService();
      final result = await service.runJourney(name, data: data);
      setSuccess('runJourney', result);
    } catch (e, stackTrace) {
      setError('runJourney', e, stackTrace);
    }
  }

  Future<void> _runJourneyGetStatus() async {
    if (!ensureInitialized('getJourneyStatus')) return;
    setRunning('getJourneyStatus');
    try {
      final name = journeyNameController.text.trim();
      final service = DataSapien.getJourneyService();
      final status = await service.getJourneyStatus(name);
      setSuccess('getJourneyStatus', status.toString());
    } catch (e, stackTrace) {
      setError('getJourneyStatus', e, stackTrace);
    }
  }

  List<ActionItem> _journeyActions() => [
        ActionItem(
          label: 'syncJourneys',
          description: 'Synchronize journeys from Orchestrator',
          onTap: _runJourneySync,
        ),
        ActionItem(
          label: 'getJourneys',
          description: 'Fetch all available journeys',
          onTap: _runJourneyGetList,
        ),
        ActionItem(
          label: 'runJourney',
          description: 'Run a specific journey by name',
          onTap: _runJourneyRun,
        ),
        ActionItem(
          label: 'getJourneyStatus',
          description: 'Get current status of a journey',
          onTap: _runJourneyGetStatus,
        ),
      ];

  Future<void> _runIntelligenceLoadModel() async {
    if (!ensureInitialized('loadModel')) return;
    setRunning('loadModel');
    try {
      final name = intelligenceModelNameController.text.trim();
      final key = intelligenceModelKeyController.text.trim();
      final service = DataSapien.getIntelligenceService();
      await service.loadModel(name, key);
      setSuccess('loadModel', null);
    } catch (e, stackTrace) {
      setError('loadModel', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceUnloadModel() async {
    if (!ensureInitialized('unloadModel')) return;
    setRunning('unloadModel');
    try {
      final key = intelligenceModelKeyController.text.trim();
      final service = DataSapien.getIntelligenceService();
      await service.unloadModel(key: key.isEmpty ? null : key);
      setSuccess('unloadModel', null);
    } catch (e, stackTrace) {
      setError('unloadModel', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceDownloadModel() async {
    if (!ensureInitialized('downloadModelFiles')) return;
    setRunning('downloadModelFiles');

    setState(() {
      _downloadProgress = 0.0;
    });

    try {
      final name = intelligenceModelNameController.text.trim();
      final service = DataSapien.getIntelligenceService();

      await service.downloadModelFiles(
        name,
        onProgress: (progress) {
          setState(() {
            _downloadProgress = progress;
          });
        },
      );

      setSuccess('downloadModelFiles', 'Download complete');
    } catch (e, stackTrace) {
      setError('downloadModelFiles', e, stackTrace);
    } finally {
      setState(() {
        _downloadProgress = null;
      });
    }
  }

  Future<void> _runIntelligenceDeleteModel() async {
    if (!ensureInitialized('deleteModelFiles')) return;
    setRunning('deleteModelFiles');
    try {
      final name = intelligenceModelNameController.text.trim();
      final service = DataSapien.getIntelligenceService();
      await service.deleteModelFiles(name);
      setSuccess('deleteModelFiles', null);
    } catch (e, stackTrace) {
      setError('deleteModelFiles', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceIsModelDownloaded() async {
    if (!ensureInitialized('isModelFilesDownloaded')) return;
    setRunning('isModelFilesDownloaded');
    try {
      final name = intelligenceModelNameController.text.trim();
      final service = DataSapien.getIntelligenceService();
      final isDownloaded = await service.isModelFilesDownloaded(name);
      setSuccess('isModelFilesDownloaded', isDownloaded);
    } catch (e, stackTrace) {
      setError('isModelFilesDownloaded', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceGetDownloadedModels() async {
    if (!ensureInitialized('getDownloadedModelsList')) return;
    setRunning('getDownloadedModelsList');
    try {
      final service = DataSapien.getIntelligenceService();
      final models = await service.getDownloadedModelsList();
      setSuccess('getDownloadedModelsList', models);
    } catch (e, stackTrace) {
      setError('getDownloadedModelsList', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceGetLoadedModels() async {
    if (!ensureInitialized('getLoadedModels')) return;
    setRunning('getLoadedModels');
    try {
      final key = intelligenceModelKeyController.text.trim();
      final service = DataSapien.getIntelligenceService();
      final isLoaded = await service.isModelLoaded(key);
      setSuccess('getLoadedModels', {'key': key, 'isLoaded': isLoaded});
    } catch (e, stackTrace) {
      setError('getLoadedModels', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceStopModelInference() async {
    if (!ensureInitialized('stopModelInference')) return;
    setRunning('stopModelInference');
    try {
      final key = intelligenceModelKeyController.text.trim();
      final service = DataSapien.getIntelligenceService();
      await service.stopModelInference(key);
      setSuccess('stopModelInference', null);
    } catch (e, stackTrace) {
      setError('stopModelInference', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceInvokeModel() async {
    if (!ensureInitialized('invokeModel')) return;
    setRunning('invokeModel');

    setState(() {
      _streamingText = '';
    });

    try {
      final key = intelligenceModelKeyController.text.trim();
      final promptText = intelligencePromptController.text.trim();
      final service = DataSapien.getIntelligenceService();
      final prompts = [
        Prompt(role: PromptRole.user, content: promptText),
      ];

      final result = await service.invokeModel(
        key,
        prompts,
        onStream: (chunk) {
          setState(() {
            _streamingText = (_streamingText ?? '') + chunk;
          });
        },
      );

      setSuccess('invokeModel', result);
    } catch (e, stackTrace) {
      setError('invokeModel', e, stackTrace);
    } finally {
      setState(() {
        _streamingText = null;
      });
    }
  }

  Future<void> _runIntelligenceSyncRules() async {
    if (!ensureInitialized('syncRules')) return;
    setRunning('syncRules');
    try {
      final service = DataSapien.getIntelligenceService();
      await service.syncRules();
      setSuccess('syncRules', null);
    } catch (e, stackTrace) {
      setError('syncRules', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceGetRules() async {
    if (!ensureInitialized('getRules')) return;
    setRunning('getRules');
    try {
      final service = DataSapien.getIntelligenceService();
      final rules = await service.getRules();
      setSuccess('getRules', rules.map((r) => r.toJson()).toList());
    } catch (e, stackTrace) {
      setError('getRules', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceGetRule() async {
    if (!ensureInitialized('getRule')) return;
    setRunning('getRule');
    try {
      final name = intelligenceRuleNameController.text.trim();
      final service = DataSapien.getIntelligenceService();
      final rule = await service.getRule(name);
      setSuccess('getRule', rule?.toJson());
    } catch (e, stackTrace) {
      setError('getRule', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceRunRule() async {
    if (!ensureInitialized('runRule')) return;
    setRunning('runRule');
    try {
      final name = intelligenceRuleNameController.text.trim();
      final service = DataSapien.getIntelligenceService();
      final rule = await service.runRule(name);
      setSuccess('runRule', rule?.toJson());
    } catch (e, stackTrace) {
      setError('runRule', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceRunRules() async {
    if (!ensureInitialized('runRules')) return;
    setRunning('runRules');
    try {
      final service = DataSapien.getIntelligenceService();
      final rules = await service.runRules();
      setSuccess('runRules', rules.map((r) => r.toJson()).toList());
    } catch (e, stackTrace) {
      setError('runRules', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceSyncManagedAIModels() async {
    if (!ensureInitialized('syncManagedAIModels')) return;
    setRunning('syncManagedAIModels');
    try {
      final service = DataSapien.getIntelligenceService();
      await service.syncManagedAIModels();
      setSuccess('syncManagedAIModels', null);
    } catch (e, stackTrace) {
      setError('syncManagedAIModels', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceGetManagedAIModels() async {
    if (!ensureInitialized('getManagedAIModels')) return;
    setRunning('getManagedAIModels');
    try {
      final service = DataSapien.getIntelligenceService();
      final models = await service.getManagedAIModels();
      setSuccess('getManagedAIModels', models.map((m) => m.toJson()).toList());
    } catch (e, stackTrace) {
      setError('getManagedAIModels', e, stackTrace);
    }
  }

  Future<void> _runIntelligenceGetManagedAIModel() async {
    if (!ensureInitialized('getManagedAIModel')) return;
    setRunning('getManagedAIModel');
    try {
      final name = intelligenceModelNameController.text.trim();
      final service = DataSapien.getIntelligenceService();
      final model = await service.getManagedAIModel(name);
      setSuccess('getManagedAIModel', model?.toJson());
    } catch (e, stackTrace) {
      setError('getManagedAIModel', e, stackTrace);
    }
  }

  List<ActionItem> _intelligenceActions() => [
        ActionItem(
          label: 'syncManagedAIModels',
          description: 'Synchronize managed AI models from backend',
          onTap: _runIntelligenceSyncManagedAIModels,
        ),
        ActionItem(
          label: 'getManagedAIModels',
          description: 'Fetch all managed AI models',
          onTap: _runIntelligenceGetManagedAIModels,
        ),
        ActionItem(
          label: 'getManagedAIModel',
          description: 'Fetch a single managed AI model by name',
          onTap: _runIntelligenceGetManagedAIModel,
        ),
        ActionItem(
          label: 'loadModel',
          description: 'Load a model for inference',
          onTap: _runIntelligenceLoadModel,
        ),
        ActionItem(
          label: 'unloadModel',
          description:
              'Unload model for the instance key (empty key unloads all tracked keys)',
          onTap: _runIntelligenceUnloadModel,
        ),
        ActionItem(
          label: 'downloadModelFiles',
          description: 'Download model with progress (shows progress bar)',
          onTap: _runIntelligenceDownloadModel,
        ),
        ActionItem(
          label: 'deleteModelFiles',
          description: 'Delete downloaded model files',
          onTap: _runIntelligenceDeleteModel,
        ),
        ActionItem(
          label: 'isModelFilesDownloaded',
          description: 'Check if model files are downloaded',
          onTap: _runIntelligenceIsModelDownloaded,
        ),
        ActionItem(
          label: 'getDownloadedModelsList',
          description: 'Get list of downloaded models',
          onTap: _runIntelligenceGetDownloadedModels,
        ),
        ActionItem(
          label: 'getLoadedModels',
          description: 'Get keys of models currently loaded in memory',
          onTap: _runIntelligenceGetLoadedModels,
        ),
        ActionItem(
          label: 'stopModelInference',
          description: 'Stop inference for the model key in inputs',
          onTap: _runIntelligenceStopModelInference,
        ),
        ActionItem(
          label: 'invokeModel',
          description: 'Invoke model with streaming (shows text in real-time)',
          onTap: _runIntelligenceInvokeModel,
        ),
        ActionItem(
          label: 'syncRules',
          description: 'Synchronize rules from backend',
          onTap: _runIntelligenceSyncRules,
        ),
        ActionItem(
          label: 'getRules',
          description: 'Fetch all rules',
          onTap: _runIntelligenceGetRules,
        ),
        ActionItem(
          label: 'getRule',
          description: 'Fetch a single rule by name',
          onTap: _runIntelligenceGetRule,
        ),
        ActionItem(
          label: 'runRule',
          description: 'Run a specific rule',
          onTap: _runIntelligenceRunRule,
        ),
        ActionItem(
          label: 'runRules',
          description: 'Run all rules',
          onTap: _runIntelligenceRunRules,
        ),
      ];

  List<ActionItem> _currentActions() {
    switch (_selectedService) {
      case SelectedService.backup:
        return _backupActions();
      case SelectedService.meData:
        return _meDataActions();
      case SelectedService.audience:
        return _audienceActions();
      case SelectedService.managedApi:
        return _managedApiActions();
      case SelectedService.journey:
        return _journeyActions();
      case SelectedService.intelligence:
        return _intelligenceActions();
      case SelectedService.other:
        return const [];
    }
  }

  String _getServiceTitle() {
    switch (_selectedService) {
      case SelectedService.backup:
        return 'BackupService Actions';
      case SelectedService.meData:
        return 'MeDataService Actions';
      case SelectedService.audience:
        return 'AudienceService Actions';
      case SelectedService.managedApi:
        return 'ManagedAPIService Actions';
      case SelectedService.journey:
        return 'JourneyService Actions';
      case SelectedService.intelligence:
        return 'IntelligenceService Actions';
      case SelectedService.other:
        return 'Other Service Actions';
    }
  }

  Widget? _getServiceInputForm() {
    switch (_selectedService) {
      case SelectedService.meData:
        return MeDataInputForm(
          nameController: meDataNameController,
          categoryController: meDataCategoryController,
          valuesJsonController: meDataValuesJsonController,
          collectNamesController: meDataCollectNamesController,
        );
      case SelectedService.audience:
        return AudienceInputForm(
          segmentNameController: audienceSegmentNameController,
        );
      case SelectedService.managedApi:
        return ManagedApiInputForm(
          nameController: managedApiNameController,
        );
      case SelectedService.journey:
        return JourneyInputForm(
          nameController: journeyNameController,
          dataJsonController: journeyDataJsonController,
        );
      case SelectedService.intelligence:
        return IntelligenceInputForm(
          modelNameController: intelligenceModelNameController,
          modelKeyController: intelligenceModelKeyController,
          promptController: intelligencePromptController,
          ruleNameController: intelligenceRuleNameController,
        );
      default:
        return null;
    }
  }

  double? get downloadProgress => _downloadProgress;
  String? get streamingText => _streamingText;
  String? get backupJson => _backupJson;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('DataSapien SDK Example'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                // SDK Status Card
                Card(
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        const Text(
                          'SDK Status',
                          style: TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text(_status),
                        const SizedBox(height: 12),
                        if (!_isInitialized)
                          ElevatedButton(
                            onPressed: _isLoading ? null : _initializeSdk,
                            child: Text(
                              _isLoading ? 'Initializing...' : 'Initialize SDK',
                            ),
                          ),
                      ],
                    ),
                  ),
                ),
                const SizedBox(height: 12),

                // Service Selector
                Row(
                  children: [
                    const Text(
                      'Selected service:',
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(width: 8),
                    DropdownButton<SelectedService>(
                      value: _selectedService,
                      onChanged: (value) {
                        if (value == null) return;
                        setState(() {
                          _selectedService = value;
                        });
                      },
                      items: const [
                        DropdownMenuItem(
                          value: SelectedService.backup,
                          child: Text('BackupService'),
                        ),
                        DropdownMenuItem(
                          value: SelectedService.meData,
                          child: Text('MeDataService'),
                        ),
                        DropdownMenuItem(
                          value: SelectedService.audience,
                          child: Text('AudienceService'),
                        ),
                        DropdownMenuItem(
                          value: SelectedService.managedApi,
                          child: Text('ManagedAPIService'),
                        ),
                        DropdownMenuItem(
                          value: SelectedService.journey,
                          child: Text('JourneyService'),
                        ),
                        DropdownMenuItem(
                          value: SelectedService.intelligence,
                          child: Text('IntelligenceService'),
                        ),
                        DropdownMenuItem(
                          value: SelectedService.other,
                          child: Text('Other (placeholder)'),
                        ),
                      ],
                    ),
                  ],
                ),
                if (!_isInitialized)
                  const Padding(
                    padding: EdgeInsets.only(top: 4.0, bottom: 8.0),
                    child: Text(
                      'Initialize SDK first to enable service actions.',
                      style: TextStyle(fontSize: 12, color: Colors.black54),
                    ),
                  ),
                const SizedBox(height: 8),

                // Service Actions Section
                ActionSection(
                  title: _getServiceTitle(),
                  actions: _currentActions(),
                  isInitialized: _isInitialized,
                  extraControls: _getServiceInputForm(),
                ),

                // Download Progress Indicator
                if (downloadProgress != null) ...[ 
                  const SizedBox(height: 12),
                  Card(
                    color: Colors.blue.shade50,
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            'Download Progress: ${(downloadProgress! * 100).toStringAsFixed(1)}%',
                            style: const TextStyle(
                              fontSize: 14,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 8),
                          LinearProgressIndicator(value: downloadProgress),
                        ],
                      ),
                    ),
                  ),
                ],

                // Streaming Text Display
                if (streamingText != null) ...[
                  const SizedBox(height: 12),
                  Card(
                    color: Colors.green.shade50,
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text(
                            'Streaming Response',
                            style: TextStyle(
                              fontSize: 16,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 8),
                          Text(
                            streamingText!,
                            style: const TextStyle(fontSize: 12),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],

                // Error Display
                if (_errorMessage != null) ...[
                  const SizedBox(height: 12),
                  Card(
                    color: Colors.red.shade50,
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text(
                            'Error',
                            style: TextStyle(
                              fontSize: 16,
                              fontWeight: FontWeight.bold,
                              color: Colors.red,
                            ),
                          ),
                          const SizedBox(height: 8),
                          Text(
                            _errorMessage!,
                            style: const TextStyle(
                              fontSize: 12,
                              color: Colors.red,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],

                // Result Preview
                if (_resultPreview != null) ...[
                  const SizedBox(height: 12),
                  Card(
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text(
                            'Last result',
                            style: TextStyle(
                              fontSize: 16,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 8),
                          Text(
                            _resultPreview!,
                            style: const TextStyle(fontSize: 12),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],

                // Backup JSON Display
                if (backupJson != null) ...[
                  const SizedBox(height: 12),
                  Card(
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text(
                            'Backup JSON (first 200 chars)',
                            style: TextStyle(
                              fontSize: 16,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 8),
                          Text(
                            backupJson!.length > 200
                                ? '${backupJson!.substring(0, 200)}...'
                                : backupJson!,
                            style: const TextStyle(fontSize: 12),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
              ],
            ),
          ),
        ),
      ),
    );
  }
}

// -------------------------
// Minimal inlined types/widgets (so pub.dev single-file Example is complete)
// -------------------------

enum SelectedService {
  backup,
  meData,
  audience,
  managedApi,
  journey,
  intelligence,
  other,
}

class ActionItem {
  final String label;
  final String? description;
  final Future<void> Function() onTap;

  const ActionItem({
    required this.label,
    required this.onTap,
    this.description,
  });
}

class ActionSection extends StatelessWidget {
  final String title;
  final List<ActionItem> actions;
  final Widget? extraControls;
  final bool isInitialized;

  const ActionSection({
    super.key,
    required this.title,
    required this.actions,
    required this.isInitialized,
    this.extraControls,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 12),
            if (extraControls != null) ...[
              extraControls!,
              const SizedBox(height: 12),
            ],
            for (final action in actions) ...[
              ElevatedButton(
                onPressed: !isInitialized ? null : action.onTap,
                child: Text(action.label),
              ),
              if (action.description != null) ...[
                const SizedBox(height: 4),
                Text(
                  action.description!,
                  style: const TextStyle(fontSize: 12, color: Colors.black54),
                ),
              ],
              const SizedBox(height: 8),
            ],
          ],
        ),
      ),
    );
  }
}

class MeDataInputForm extends StatelessWidget {
  final TextEditingController nameController;
  final TextEditingController categoryController;
  final TextEditingController valuesJsonController;
  final TextEditingController collectNamesController;

  const MeDataInputForm({
    super.key,
    required this.nameController,
    required this.categoryController,
    required this.valuesJsonController,
    required this.collectNamesController,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'MeData inputs',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: nameController,
          decoration: const InputDecoration(
            labelText: 'MeData name',
            hintText: 'e.g. daily_steps',
          ),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: categoryController,
          decoration: const InputDecoration(
            labelText: 'Category name',
          ),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: valuesJsonController,
          decoration: const InputDecoration(
            labelText: 'Values (JSON)',
            hintText: r'[12345, \"2025-10-04\"]',
          ),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: collectNamesController,
          decoration: const InputDecoration(
            labelText: 'Collect names (comma separated)',
            hintText: 'daily_steps,height,weight',
          ),
        ),
      ],
    );
  }
}

class AudienceInputForm extends StatelessWidget {
  final TextEditingController segmentNameController;

  const AudienceInputForm({
    super.key,
    required this.segmentNameController,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'Audience inputs',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: segmentNameController,
          decoration: const InputDecoration(
            labelText: 'Segment name',
            hintText: 'e.g. ab_testing-a',
          ),
        ),
      ],
    );
  }
}

class ManagedApiInputForm extends StatelessWidget {
  final TextEditingController nameController;

  const ManagedApiInputForm({
    super.key,
    required this.nameController,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'ManagedAPI inputs',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: nameController,
          decoration: const InputDecoration(
            labelText: 'ManagedAPI name',
            hintText: 'e.g. google_fit',
          ),
        ),
        const SizedBox(height: 8),
      ],
    );
  }
}

class JourneyInputForm extends StatelessWidget {
  final TextEditingController nameController;
  final TextEditingController dataJsonController;

  const JourneyInputForm({
    super.key,
    required this.nameController,
    required this.dataJsonController,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'Journey inputs',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: nameController,
          decoration: const InputDecoration(
            labelText: 'Journey name',
          ),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: dataJsonController,
          decoration: const InputDecoration(
            labelText: 'Context Data (JSON)',
          ),
        ),
        const SizedBox(height: 8),
      ],
    );
  }
}

class IntelligenceInputForm extends StatelessWidget {
  final TextEditingController modelNameController;
  final TextEditingController modelKeyController;
  final TextEditingController promptController;
  final TextEditingController ruleNameController;

  const IntelligenceInputForm({
    super.key,
    required this.modelNameController,
    required this.modelKeyController,
    required this.promptController,
    required this.ruleNameController,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'Intelligence inputs',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: modelNameController,
          decoration: const InputDecoration(
            labelText: 'Model name (catalog)',
            hintText: 'e.g. test_model',
          ),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: modelKeyController,
          decoration: const InputDecoration(
            labelText: 'Model instance key',
            hintText: 'e.g. default',
          ),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: promptController,
          decoration: const InputDecoration(
            labelText: 'Prompt text',
            hintText: 'Hello, how are you?',
          ),
        ),
        const SizedBox(height: 8),
        TextField(
          controller: ruleNameController,
          decoration: const InputDecoration(
            labelText: 'Rule name',
            hintText: 'e.g. test_rule',
          ),
        ),
        const SizedBox(height: 8),
      ],
    );
  }
}
0
likes
0
points
340
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter plugin wrapper for DataSapien iOS and Android SDKs

Homepage

License

unknown (license)

Dependencies

flutter

More

Packages that depend on datasapien_sdk

Packages that implement datasapien_sdk