/
ilyakos
/
ChatWithAI
Обзор
Документация
Войти
/
ilyakos
/
ChatWithAI
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
ChatWithAI_Server/Services/LmStudioService.cs
108 строк
4 KB
ilyakos
новые энд поинты
15 июн 2026, 20:32
15 июн 2026, 20:32
15ab71c
Код
Авторство
О чём код?
using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using ChatWithAI.Database.Models; namespace ChatWithAI_Server.Services; /// <summary> /// Реализация сервиса LM Studio на основе OpenAI-совместимого HTTP API. /// Отправляет запросы на эндпоинт <c>/chat/completions</c> с полной историей диалога. /// </summary> public class LmStudioService(HttpClient httpClient, IConfiguration config) : ILmStudioService { private static readonly JsonSerializerOptions SerializerOptions = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; /// <inheritdoc/> public async Task<string> CompleteChatAsync( string systemPrompt, IEnumerable<Message> history, string userText, string? userImageBase64 = null) { var apiMessages = new List<LmMessage> { new("system", systemPrompt) }; foreach (var msg in history) { apiMessages.Add(new LmMessage( msg.IsUser ? "user" : "assistant", BuildContent(msg.Text, msg.ImageBase64))); } apiMessages.Add(new LmMessage("user", BuildContent(userText, userImageBase64))); var model = config["LmStudio:Model"] ?? "local-model"; var maxTokens = int.Parse(config["LmStudio:MaxTokens"] ?? "2048"); var requestBody = new LmChatRequest(model, apiMessages, maxTokens); var response = await httpClient.PostAsJsonAsync("chat/completions", requestBody, SerializerOptions); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync<LmChatResponse>() ?? throw new InvalidOperationException("LM Studio вернул пустой ответ."); if (result.Choices is not { Count: > 0 }) throw new InvalidOperationException("LM Studio не вернул ни одного варианта ответа."); return result.Choices[0].Message.Content; } /// <summary> /// Формирует содержимое сообщения: строку для текстовых, массив частей для мультимодальных. /// </summary> private static object BuildContent(string? text, string? imageBase64) { if (imageBase64 is null) return text ?? string.Empty; var parts = new List<LmContentPart>(); if (text is not null) parts.Add(new LmContentPart("text", text, null)); parts.Add(new LmContentPart("image_url", null, new LmImageUrl($"data:image/jpeg;base64,{imageBase64}"))); return parts; } // --- Внутренние модели для OpenAI-совместимого API --- private record LmChatRequest( [property: JsonPropertyName("model")] string Model, [property: JsonPropertyName("messages")] List<LmMessage> Messages, [property: JsonPropertyName("max_tokens")] int MaxTokens, [property: JsonPropertyName("stream")] bool Stream = false ); private record LmMessage( [property: JsonPropertyName("role")] string Role, [property: JsonPropertyName("content")] object Content ); private record LmContentPart( [property: JsonPropertyName("type")] string Type, [property: JsonPropertyName("text")] string? Text, [property: JsonPropertyName("image_url")] LmImageUrl? ImageUrl ); private record LmImageUrl( [property: JsonPropertyName("url")] string Url ); private record LmChatResponse( [property: JsonPropertyName("choices")] List<LmChoice> Choices ); private record LmChoice( [property: JsonPropertyName("message")] LmAssistantMessage Message ); private record LmAssistantMessage( [property: JsonPropertyName("role")] string Role, [property: JsonPropertyName("content")] string Content ); }