CustomModelDefinition.fromYaml constructor

CustomModelDefinition.fromYaml(
  1. String name,
  2. Object? node
)

Parses one definition from yaml, strictly: name is the map key; provider must be a catalog provider name (see providerCatalog), baseUrl/model non-empty strings, contextWindow/maxTokens positive integers, input a non-empty list of text/image.

Implementation

factory CustomModelDefinition.fromYaml(String name, Object? node) {
  final where = 'models.custom.$name';
  if (node is! YamlMap) {
    throw ConfigException(
      '$where must be a map with provider, baseUrl, model',
    );
  }
  const knownFields = [
    'provider',
    'baseUrl',
    'model',
    'contextWindow',
    'maxTokens',
    'input',
  ];
  for (final key in node.keys) {
    if (!knownFields.contains(key)) {
      throw ConfigException(
        'unknown field "$key" in $where — expected '
        '${knownFields.join(', ')}',
      );
    }
  }
  String required(String field) {
    final value = node[field];
    if (value is! String || value.trim().isEmpty) {
      throw ConfigException('$where.$field must be a non-empty string');
    }
    return value.trim();
  }

  final provider = required('provider');
  if (catalogProvider(provider) == null) {
    throw ConfigException(
      'unknown provider "$provider" in $where — supported providers: '
      '${providerCatalog.keys.join(', ')}',
    );
  }
  int? optionalInt(String field) {
    final value = node[field];
    if (value == null) return null;
    if (value is! int || value <= 0) {
      throw ConfigException('$where.$field must be a positive integer');
    }
    return value;
  }

  final input = switch (node['input']) {
    null => null,
    final YamlList list when list.isNotEmpty => [
      for (final entry in list)
        switch (entry) {
          'text' || 'image' => entry as String,
          _ => throw ConfigException(
            '$where.input entries must be "text" or "image", got: $entry',
          ),
        },
    ],
    final other => throw ConfigException(
      '$where.input must be a non-empty list of "text"/"image", '
      'got: $other',
    ),
  };
  return CustomModelDefinition(
    provider: provider,
    baseUrl: required('baseUrl'),
    model: required('model'),
    contextWindow: optionalInt('contextWindow'),
    maxTokens: optionalInt('maxTokens'),
    input: input,
  );
}