Skip to content

Commit 0743825

Browse files
authored
Merge branch 'main' into zt/custom-models
2 parents 3ddb8f1 + 093a369 commit 0743825

File tree

11 files changed

+471
-12
lines changed

11 files changed

+471
-12
lines changed

AIDevGallery/AIDevGallery.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
</ItemGroup>
5151

5252
<ItemGroup>
53-
<PackageReference Include="ColorCode.WinUI" />
53+
<PackageReference Include="ColorCode.WinUI" />
5454
<PackageReference Include="CommunityToolkit.Mvvm" />
5555
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" />
5656
<PackageReference Include="CommunityToolkit.WinUI.Controls.CameraPreview" />
@@ -244,6 +244,8 @@
244244
<None Remove="Assets\ModelIcons\GitHub.dark.svg" />
245245
<None Remove="Assets\ModelIcons\GitHub.light.svg" />
246246
<None Remove="Assets\ModelIcons\HuggingFace.svg" />
247+
<None Remove="Assets\ModelIcons\lemonade.png" />
248+
<None Remove="Assets\ModelIcons\lemonade.svg" />
247249
<None Remove="Assets\ModelIcons\Microsoft.svg" />
248250
<None Remove="Assets\ModelIcons\Mistral.svg" />
249251
<None Remove="Assets\ModelIcons\Ollama.dark.png" />
1.35 KB
Loading
Lines changed: 53 additions & 0 deletions
Loading

AIDevGallery/Controls/ModelPicker/ModelOrApiPicker.xaml.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -216,16 +216,10 @@ private async Task LoadModels(List<ModelType> types)
216216

217217
foreach (var def in pickers)
218218
{
219-
if (def.Id == "ollama")
219+
if (await def.IsAvailable())
220220
{
221-
// don't add ollama if not available
222-
if (!await OllamaModelProvider.Instance.IsAvailable())
223-
{
224-
continue;
225-
}
221+
modelTypeSelector.Items.Add(new SegmentedItem() { Icon = new ImageIcon() { Source = new BitmapImage(new Uri(def.Icon)) }, Content = def.Name, Tag = def });
226222
}
227-
228-
modelTypeSelector.Items.Add(new SegmentedItem() { Icon = new ImageIcon() { Source = new BitmapImage(new Uri(def.Icon)) }, Content = def.Name, Tag = def });
229223
}
230224

231225
modelTypeSelector.SelectedItem = modelTypeSelector.Items[0];
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<local:BaseModelPickerView
3+
x:Class="AIDevGallery.Controls.ModelPickerViews.LemonadePickerView"
4+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
5+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:local="using:AIDevGallery.Controls.ModelPickerViews"
8+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
9+
xmlns:models="using:AIDevGallery.Models"
10+
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
11+
xmlns:ui="using:CommunityToolkit.WinUI"
12+
xmlns:utils="using:AIDevGallery.Utils"
13+
mc:Ignorable="d">
14+
15+
<Grid>
16+
<ProgressRing
17+
x:Name="LoadingIndicator"
18+
Width="36"
19+
Height="36"
20+
HorizontalAlignment="Center"
21+
VerticalAlignment="Center"
22+
IsActive="True" />
23+
<Grid x:Name="ModelsView" Visibility="Collapsed">
24+
<Grid.RowDefinitions>
25+
<RowDefinition Height="Auto" />
26+
<RowDefinition Height="*" />
27+
</Grid.RowDefinitions>
28+
<TextBlock
29+
Margin="16"
30+
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
31+
Text="Available models via Lemonade"
32+
TextWrapping="Wrap" />
33+
<ListView
34+
x:Name="ModelSelectionView"
35+
Grid.Row="1"
36+
ItemsSource="{x:Bind models, Mode=OneWay}"
37+
SelectionChanged="ModelSelectionView_SelectionChanged"
38+
SelectionMode="Single">
39+
<ListView.ItemContainerTransitions>
40+
<TransitionCollection />
41+
</ListView.ItemContainerTransitions>
42+
<ListView.ItemTemplate>
43+
<DataTemplate x:DataType="models:ModelDetails">
44+
<toolkit:SettingsCard
45+
MinHeight="48"
46+
Padding="4,10,14,10"
47+
Background="Transparent"
48+
BorderThickness="0"
49+
CornerRadius="0"
50+
Header="{x:Bind Name}">
51+
<toolkit:SettingsCard.Resources>
52+
<Thickness x:Key="SettingsCardHeaderIconMargin">0,0,14,0</Thickness>
53+
<x:Double x:Key="SettingsCardWrapThreshold">286</x:Double>
54+
</toolkit:SettingsCard.Resources>
55+
<toolkit:SettingsCard.HeaderIcon>
56+
<ImageIcon
57+
AutomationProperties.AccessibilityView="Control"
58+
AutomationProperties.Name="Model source icon"
59+
Source="{x:Bind Icon}" />
60+
</toolkit:SettingsCard.HeaderIcon>
61+
62+
<Button
63+
Width="28"
64+
Height="28"
65+
Padding="0"
66+
ui:FrameworkElementExtensions.AncestorType="toolkit:SettingsCard"
67+
AutomationProperties.Name="More options"
68+
Content="{ui:FontIcon Glyph=&#xE712;,
69+
FontSize=16}"
70+
Style="{StaticResource SubtleButtonStyle}"
71+
ToolTipService.ToolTip="More options">
72+
<Button.Flyout>
73+
<MenuFlyout Placement="Bottom">
74+
<MenuFlyoutItem
75+
Click="CopyUrl_Click"
76+
Icon="{ui:FontIcon Glyph=&#xE8A5;}"
77+
Tag="{x:Bind}"
78+
Text="Copy URL" />
79+
</MenuFlyout>
80+
</Button.Flyout>
81+
</Button>
82+
</toolkit:SettingsCard>
83+
</DataTemplate>
84+
</ListView.ItemTemplate>
85+
</ListView>
86+
</Grid>
87+
<VisualStateManager.VisualStateGroups>
88+
<VisualStateGroup x:Name="StateGroup">
89+
<VisualState x:Name="ShowLoading" />
90+
<VisualState x:Name="ShowModels">
91+
<VisualState.Setters>
92+
<Setter Target="LoadingIndicator.Visibility" Value="Collapsed" />
93+
<Setter Target="ModelsView.Visibility" Value="Visible" />
94+
</VisualState.Setters>
95+
</VisualState>
96+
</VisualStateGroup>
97+
</VisualStateManager.VisualStateGroups>
98+
</Grid>
99+
</local:BaseModelPickerView>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using AIDevGallery.ExternalModelUtils;
5+
using AIDevGallery.Models;
6+
using Microsoft.UI.Xaml;
7+
using Microsoft.UI.Xaml.Controls;
8+
using System.Collections.Generic;
9+
using System.Collections.ObjectModel;
10+
using System.Linq;
11+
using System.Threading.Tasks;
12+
using Windows.ApplicationModel.DataTransfer;
13+
14+
namespace AIDevGallery.Controls.ModelPickerViews;
15+
16+
internal sealed partial class LemonadePickerView : BaseModelPickerView
17+
{
18+
private ObservableCollection<ModelDetails> models = new ObservableCollection<ModelDetails>();
19+
20+
public LemonadePickerView()
21+
{
22+
this.InitializeComponent();
23+
}
24+
25+
public override async Task Load(List<ModelType> types)
26+
{
27+
VisualStateManager.GoToState(this, "ShowLoading", true);
28+
var lemonadeModels = await LemonadeModelProvider.Instance.GetModelsAsync(ignoreCached: true) ?? [];
29+
lemonadeModels.ToList().ForEach(models.Add);
30+
VisualStateManager.GoToState(this, "ShowModels", true);
31+
}
32+
33+
private void CopyUrl_Click(object sender, RoutedEventArgs e)
34+
{
35+
if (sender is MenuFlyoutItem btn && btn.Tag is ModelDetails details)
36+
{
37+
var url = ExternalModelHelper.GetModelUrl(details);
38+
if (string.IsNullOrEmpty(url))
39+
{
40+
return;
41+
}
42+
43+
var dataPackage = new DataPackage();
44+
dataPackage.SetText(url);
45+
Clipboard.SetContentWithOptions(dataPackage, null);
46+
}
47+
}
48+
49+
private void ModelSelectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
50+
{
51+
if (sender is ListView modelView && modelView.SelectedItem is ModelDetails details)
52+
{
53+
OnSelectedModelChanged(this, details);
54+
}
55+
}
56+
57+
public override void SelectModel(ModelDetails? modelDetails)
58+
{
59+
if (modelDetails != null && models.Contains(modelDetails))
60+
{
61+
var foundModel = models.FirstOrDefault(m => m.Id == modelDetails.Id);
62+
if (foundModel != null)
63+
{
64+
ModelSelectionView.SelectedIndex = models.IndexOf(foundModel);
65+
}
66+
else
67+
{
68+
ModelSelectionView.SelectedItem = null;
69+
}
70+
}
71+
else
72+
{
73+
ModelSelectionView.SelectedItem = null;
74+
}
75+
}
76+
}

AIDevGallery/Controls/ModelPicker/ModelPickerViews/ModelPickerDefinitions.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using AIDevGallery.ExternalModelUtils;
45
using AIDevGallery.Utils;
56
using System;
67
using System.Collections.Generic;
8+
using System.Threading.Tasks;
79

810
namespace AIDevGallery.Controls.ModelPickerViews;
911
internal class ModelPickerDefinition
@@ -34,7 +36,8 @@ internal class ModelPickerDefinition
3436
Name = "Ollama",
3537
Id = "ollama",
3638
Icon = $"ms-appx:///Assets/ModelIcons/Ollama{AppUtils.GetThemeAssetSuffix()}.png",
37-
CreatePicker = () => new OllamaPickerView()
39+
CreatePicker = () => new OllamaPickerView(),
40+
IsAvailable = OllamaModelProvider.Instance.IsAvailable
3841
}
3942
},
4043
{
@@ -45,11 +48,22 @@ internal class ModelPickerDefinition
4548
Icon = $"ms-appx:///Assets/ModelIcons/OpenAI{AppUtils.GetThemeAssetSuffix()}.png",
4649
CreatePicker = () => new OpenAIPickerView()
4750
}
51+
},
52+
{
53+
"lemonade", new ModelPickerDefinition()
54+
{
55+
Name = "Lemonade",
56+
Id = "lemonade",
57+
Icon = $"ms-appx:///Assets/ModelIcons/lemonade.png",
58+
CreatePicker = () => new LemonadePickerView(),
59+
IsAvailable = LemonadeModelProvider.Instance.IsAvailable
60+
}
4861
}
4962
};
5063

5164
public required string Name { get; set; }
5265
public required string Icon { get; set; }
5366
public required string Id { get; set; }
5467
public required Func<BaseModelPickerView> CreatePicker { get; set; }
68+
public Func<Task<bool>> IsAvailable { get; set; } = () => Task.FromResult(true);
5569
}

AIDevGallery/ExternalModelUtils/ExternalModelHelper.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
using Microsoft.UI.Xaml.Media.Imaging;
99
using System;
1010
using System.Collections.Generic;
11+
using System.Diagnostics;
1112
using System.Linq;
13+
using System.Threading;
1214
using System.Threading.Tasks;
1315

1416
namespace AIDevGallery.ExternalModelUtils;
@@ -17,7 +19,8 @@ internal static class ExternalModelHelper
1719
{
1820
private static List<IExternalModelProvider> _modelProviders = [
1921
OllamaModelProvider.Instance,
20-
OpenAIModelProvider.Instance
22+
OpenAIModelProvider.Instance,
23+
LemonadeModelProvider.Instance
2124
];
2225

2326
public static async Task<IEnumerable<ModelDetails>> GetAllModelsAsync()
@@ -127,4 +130,31 @@ public static string GetIcon(string url)
127130
{
128131
return GetProvider(url)?.GetIChatClientString(url);
129132
}
133+
134+
public static async Task<(string Output, string Error, int ExitCode)?> GetFromProcessAsync(string command, string args, CancellationToken cancellationToken = default)
135+
{
136+
try
137+
{
138+
using var p = new Process();
139+
p.StartInfo.FileName = command;
140+
p.StartInfo.Arguments = args;
141+
p.StartInfo.RedirectStandardOutput = true;
142+
p.StartInfo.RedirectStandardError = true;
143+
p.StartInfo.UseShellExecute = false;
144+
p.StartInfo.CreateNoWindow = true;
145+
146+
p.Start();
147+
148+
string output = await p.StandardOutput.ReadToEndAsync(cancellationToken);
149+
string error = await p.StandardError.ReadToEndAsync(cancellationToken);
150+
151+
await p.WaitForExitAsync(cancellationToken);
152+
153+
return (output, error, p.ExitCode);
154+
}
155+
catch (Exception)
156+
{
157+
return null;
158+
}
159+
}
130160
}

0 commit comments

Comments
 (0)