neoagent 2.4.1-beta.39 → 2.4.1-beta.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -111,6 +111,17 @@ GOOGLE_AI_KEY=your-google-ai-key-here
111
111
  # • MiniMax-M2.7 via the Anthropic-compatible MiniMax endpoint
112
112
  MINIMAX_API_KEY=your-minimax-api-key-here
113
113
 
114
+ # NVIDIA NIM API key — used for:
115
+ # • Free-tier and paid models on NVIDIA's hosted inference (Nemotron, Kimi, Llama 4, DeepSeek, etc.)
116
+ # Get your key at: https://build.nvidia.com
117
+ NVIDIA_API_KEY=your-nvidia-nim-api-key-here
118
+
119
+ # OpenRouter API key — used for:
120
+ # • 300+ models from all major providers through a single API
121
+ # • Includes free-tier models (no credit needed for :free variants)
122
+ # Get your key at: https://openrouter.ai/keys
123
+ OPENROUTER_API_KEY=your-openrouter-api-key-here
124
+
114
125
  # GitHub Copilot OAuth token — used for:
115
126
  # • Copilot-backed chat/coding models (gpt-5.3, gpt-4.1)
116
127
  # Set via: neoagent login github-copilot
@@ -40,6 +40,8 @@ At least one key is required unless you only use local Ollama.
40
40
  | `XAI_BASE_URL` | Optional xAI-compatible base URL override |
41
41
  | `GOOGLE_AI_KEY` | Gemini (Google) |
42
42
  | `MINIMAX_API_KEY` | MiniMax (including `MiniMax-M2.7`) |
43
+ | `NVIDIA_API_KEY` | NVIDIA NIM (free-tier + paid: Nemotron, Kimi, Llama 4, DeepSeek, etc.) |
44
+ | `OPENROUTER_API_KEY` | OpenRouter — access 300+ models from all providers through one API; free-tier models included |
43
45
  | `BRAVE_SEARCH_API_KEY` | Brave Search for the `web_search` tool |
44
46
  | `OPENAI_BASE_URL` | Optional OpenAI-compatible base URL override |
45
47
  | `ANTHROPIC_BASE_URL` | Optional Anthropic-compatible base URL override |
@@ -13,6 +13,7 @@ class _ModelPickerOption {
13
13
  this.color,
14
14
  this.icon,
15
15
  this.isAuto = false,
16
+ this.priceTier,
16
17
  });
17
18
 
18
19
  final String value;
@@ -22,6 +23,8 @@ class _ModelPickerOption {
22
23
  final Color? color;
23
24
  final IconData? icon;
24
25
  final bool isAuto;
26
+ /// 'free' | 'cheap' | 'medium' | 'expensive' | null
27
+ final String? priceTier;
25
28
  }
26
29
 
27
30
  // ─── Provider helpers ─────────────────────────────────────────────────────────
@@ -43,6 +46,9 @@ Color _providerPickerColor(String provider) {
43
46
  if (p.contains('copilot') || p.contains('github')) {
44
47
  return const Color(0xFF238636);
45
48
  }
49
+ if (p.contains('openrouter')) return const Color(0xFF6366F1);
50
+ if (p.contains('nvidia')) return const Color(0xFF76B900);
51
+ if (p.contains('minimax')) return const Color(0xFF0EA5E9);
46
52
  if (p.contains('deepgram')) return const Color(0xFF13D4A0);
47
53
  if (p.contains('ollama')) return const Color(0xFF6C8EBF);
48
54
  return const Color(0xFF7C8CFF);
@@ -64,6 +70,9 @@ IconData _providerPickerIcon(String provider) {
64
70
  }
65
71
  if (p.contains('grok') || p.contains('xai')) return Icons.psychology_rounded;
66
72
  if (p.contains('copilot') || p.contains('github')) return Icons.code_rounded;
73
+ if (p.contains('openrouter')) return Icons.hub_rounded;
74
+ if (p.contains('nvidia')) return Icons.speed_rounded;
75
+ if (p.contains('minimax')) return Icons.water_rounded;
67
76
  if (p.contains('deepgram')) return Icons.hearing_rounded;
68
77
  if (p.contains('ollama')) return Icons.device_hub_rounded;
69
78
  return Icons.memory_rounded;
@@ -78,10 +87,15 @@ String _providerPickerLabel(String id) {
78
87
  'meta': 'Meta',
79
88
  'mistral': 'Mistral',
80
89
  'grok': 'xAI',
90
+ 'grok-oauth': 'xAI (OAuth)',
81
91
  'xai': 'xAI',
82
92
  'ollama': 'Ollama',
83
93
  'github-copilot': 'GitHub Copilot',
84
- 'openai-codex': 'OpenAI',
94
+ 'openai-codex': 'OpenAI Codex',
95
+ 'claude-code': 'Claude Code',
96
+ 'openrouter': 'OpenRouter',
97
+ 'nvidia': 'NVIDIA NIM',
98
+ 'minimax': 'MiniMax',
85
99
  'deepgram': 'Deepgram',
86
100
  };
87
101
  return labels[id.toLowerCase()] ?? id;
@@ -113,6 +127,7 @@ List<_ModelPickerOption> _modelPickerOptions(
113
127
  subtitle: parts.isNotEmpty ? parts.join(' · ') : null,
114
128
  color: _providerPickerColor(m.provider),
115
129
  icon: _providerPickerIcon(m.provider),
130
+ priceTier: m.priceTier,
116
131
  );
117
132
  }),
118
133
  ];
@@ -500,6 +515,41 @@ class _ModelPickerDialogState extends State<_ModelPickerDialog> {
500
515
  // Sub-widgets
501
516
  // ─────────────────────────────────────────────────────────────────────────────
502
517
 
518
+ class _PriceTierChip extends StatelessWidget {
519
+ const _PriceTierChip({required this.tier});
520
+
521
+ final String tier;
522
+
523
+ @override
524
+ Widget build(BuildContext context) {
525
+ final (String text, Color color) = switch (tier) {
526
+ 'free' => ('FREE', const Color(0xFF22C55E)),
527
+ 'cheap' => ('\$', const Color(0xFF4ADE80)),
528
+ 'medium' => ('\$\$', const Color(0xFFF59E0B)),
529
+ 'expensive' => ('\$\$\$', const Color(0xFFEF4444)),
530
+ _ => ('', Colors.transparent),
531
+ };
532
+ if (text.isEmpty) return const SizedBox.shrink();
533
+ return Container(
534
+ padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
535
+ decoration: BoxDecoration(
536
+ color: color.withValues(alpha: 0.15),
537
+ borderRadius: BorderRadius.circular(5),
538
+ border: Border.all(color: color.withValues(alpha: 0.35), width: 0.8),
539
+ ),
540
+ child: Text(
541
+ text,
542
+ style: TextStyle(
543
+ fontSize: 10,
544
+ fontWeight: FontWeight.w700,
545
+ color: color,
546
+ letterSpacing: 0.2,
547
+ ),
548
+ ),
549
+ );
550
+ }
551
+ }
552
+
503
553
  class _PickerGroupHeader extends StatelessWidget {
504
554
  const _PickerGroupHeader({required this.label, required this.color});
505
555
 
@@ -612,6 +662,10 @@ class _PickerRow extends StatelessWidget {
612
662
  ),
613
663
  ),
614
664
  const SizedBox(width: 8),
665
+ if (option.priceTier != null) ...<Widget>[
666
+ _PriceTierChip(tier: option.priceTier!),
667
+ const SizedBox(width: 6),
668
+ ],
615
669
  SizedBox(
616
670
  width: 20,
617
671
  child: selected
@@ -2015,6 +2015,7 @@ class ModelMeta {
2015
2015
  this.available = true,
2016
2016
  this.providerStatus = '',
2017
2017
  this.providerStatusLabel = '',
2018
+ this.priceTier,
2018
2019
  });
2019
2020
 
2020
2021
  factory ModelMeta.fromJson(Map<dynamic, dynamic> json) {
@@ -2026,6 +2027,7 @@ class ModelMeta {
2026
2027
  available: json['available'] != false,
2027
2028
  providerStatus: json['providerStatus']?.toString() ?? '',
2028
2029
  providerStatusLabel: json['providerStatusLabel']?.toString() ?? '',
2030
+ priceTier: json['priceTier']?.toString(),
2029
2031
  );
2030
2032
  }
2031
2033
 
@@ -2036,6 +2038,8 @@ class ModelMeta {
2036
2038
  final bool available;
2037
2039
  final String providerStatus;
2038
2040
  final String providerStatusLabel;
2041
+ /// Pricing tier: 'free' | 'cheap' | 'medium' | 'expensive' | null (unknown)
2042
+ final String? priceTier;
2039
2043
  }
2040
2044
 
2041
2045
  class AiProviderConfig {