В эпоху невероятной популярности ChatGPT и OpentAI все кинулись играть с их инструментами и использовать API в своих продуктах. Я был в общих рядах и сделал продукт для тех, кому затруднительно воспользоваться официальным сайтом ChatGPT. Но в этой статье речь пойдет не о восхитительных продуктах MS, а о создании интерфейса с котиками на основе этого продукта.

Итак, у нас стоит задача: создать десктопный чат с GPT3, которым будет удобно пользоваться человеку, далекому от пк.

Для начала научимся писать простые запросы к апи.

var content = new StringContent(
  "{\"model\": \"" + SelectedModel + "\", \"prompt\": \"" + message + "\",\"temperature\": 1,\"max_tokens\": 1000}",
  Encoding.UTF8, "application/json");


_client.DefaultRequestHeaders.Add("Authorization", "Bearer " + AccessToken);
var response = await _client.PostAsync("https://api.openai.com/v1/completions", content);


var responseString = await response.Content.ReadAsStringAsync();

Больше к этому фрагменту мы не будем возвращаться и сосредоточимся на интерфейсе.

Создадим простую разметку из трех частей: настройки, поле чата и поле ввода.

<Grid Margin="5" RowDefinitions="Auto,Auto, *,Auto">
  <Menu…>
  <WrapPanel…>
  <ScrollViewer Name="Scroll" Grid.Row="2">
    <ItemsRepeater…>
  </ScrollViewer>
  <Grid Margin="5" ColumnDefinitions="*, Auto" Grid.Row="3">
     <TextBox Watermark="Input promt" MaxLength="1000" Text="{Binding SendedMessage}" />
     <Button Grid.Column="1" Margin="5,0,0,0" Command="{Binding SendMessage}">></Button>
  </Grid>
</Grid>

Таким образом мы получим достаточно простой продукт, умеющий выдавать чат.

Для удобства обучим наш продукт сохранять токены, чтобы не приходилось каждый раз их вводить заново.

var path = FilePath;

if (!Directory.Exists(Path.GetDirectoryName(path)))
{
  Directory.CreateDirectory(Path.GetDirectoryName(path));
}

File.WriteAllText(path, JsonConvert.SerializeObject(setting));

И научим наше приложение сохранять данные при закрытии.

private void Window_OnClosing(object? sender, CancelEventArgs e)
{
  var vm = (MainWindowViewModel) DataContext;
  vm.SaveSettings();
}

Но, будем честны, сделано это творение “инженерами для инженеров”. Давайте попробуем улучшить его внешний вид. 

У нас есть название “ChatBox” и для него нам нужно запоминающиеся лого. Попробуем обратиться к нейросети.

Отдав сети следующий промт “logo for desktop application which use AvaloniaUI like chat with ChatGPT” получим в ответ следующий набор иконок.

Полученное выглядит жутенько и скорее удручает. Попробуем пойти другим путем и обратиться к ChatGPT с тем же промтом, чтобы от него получить наставления для Midjorney.

В ответ получим что‑то похожее на тз:

Create an icon for a chat application that uses AvaloniaUI and integrates with OpenAI's natural language processing technology. Requirements: The icon should convey the idea of communication, chat or messaging. The icon should be designed in a way that it fits well within the AvaloniaUI interface. The icon should incorporate elements or symbols that represent the integration with OpenAI's natural language processing technology. The icon should be in a scalable vector format, such as SVG or XAML, to ensure that it can be resized without losing its quality. Considerations: The color palette used in the icon should be consistent with the overall color scheme of the application. The design of the icon should be simple and easy to recognize, even at small sizes. Consider incorporating elements that suggest artificial intelligence, such as circuits, binary code, or robotic elements.”

C этим было не стыдно обратиться к сетке и получить что-то более понятное:

Из этого набора была выбрана финальная картинка:

На этом сражения с нейросетями закончились — известные мне инструменты не смогли сгенерировать интерфейсы, вдохновленные моей иконкой. Так что пришлось обратиться к человеку.

Замечательный художник Вероника (https://www.behance.net/verleon48bee1) согласилась помочь и создала набор интерфейсов для чата.

На основе этих интерфейсов я изменил исходную программу. К сожалению, из фигмы нельзя просто портировать части продукта. Но, вдохновляясь сэмплами, можно создать свой интерфейс, сконцентрировавшись на некоторых элементах.

Авалония позволяет биндить классы контролов, благодаря чему получаются вот такие простые решения.

<TextBox MaxWidth="{Binding #PromtBox.Bounds.Width}" 
        Grid.Column="1" Classes.user="{Binding !Bot}"
        Classes.bot="{Binding Bot}" Grid.Row="1"
        IsReadOnly="True"
        Padding="3"
        Margin="5" TextWrapping="Wrap"
        Text="{Binding Message}"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled" />

Мы можем легко определить, какие свойства необходимо накладывать на текст в зависимости от его признака.

<Style Selector="TextBox.bot">
   <Setter Property="Background" Value="#15000000"></Setter>
</Style>
<Style Selector="TextBox.user">
  <Setter Property="Background" Value="White"></Setter>
</Style>

Почему бы не использовать это свойство и дальше:

<Image VerticalAlignment="Top" IsVisible="{Binding Bot}" Source="/Assets/botkit.png"/>
<Image VerticalAlignment="Top" IsVisible="{Binding !Bot}" Source="/Assets/userkit.png"/>

Легко добавляем и убираем в одном и том же месте визуального дерева необходимое изображение.

После появления обновленных стилей еще в десятой версии авалонии во Fluent теме, можно легко применять скругления углов не только к Border, но и к любому другому контролу.

<Style Selector="TextBox">
  <Setter Property="CornerRadius" Value="50"></Setter>
 </Style>

Можно определить поведение объекта в зависимости от его состояния с помощью псевдоклассов:

<Style Selector="TextBox:pointerover /template/ Border#PART_BorderElement">
  <Setter Property="BorderBrush" Value="#FF9E24"></Setter>
</Style>

Таким образом можно достаточно приблизится к фантазиям дизайнера, а приложив достаточное количество усилий, полностью повторить их.

Подведем небольшой итог по созданию этого милого продукта. Во-первых, очень тяжело жить, если у тебя нет чувства прекрасного. Во-вторых, нейросети могут помочь вам создавать прекрасное. В-третьих, даже сегодня, с таким количеством технологий и возможностей, с друзьями жить легче.

Любите своих друзей, их не заменят нейросети.

GitHub

Комментарии (1)


  1. dimaaan
    00.00.0000 00:00
    +6

    JSON экранировать надо.

    Вместо

    var content = new StringContent(
      "{\"model\": \"" + SelectedModel + "\", \"prompt\": \"" + message + "\",\"temperature\": 1,\"max_tokens\": 1000}",
      Encoding.UTF8, "application/json");
    // ...
    var response = await _client.PostAsync("https://api.openai.com/v1/completions", content);

    делайте

    _client.PostAsJsonAsync("https://api.openai.com/v1/completions", new { model = SelectedModel, prompt = message, temperature = 1, max_tokens = 1000 });