|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Text.Json; |
| 4 | +using System.Text.Json.Serialization; |
| 5 | +using GeneXus.Http.Client; |
| 6 | +using GeneXus.Procedure; |
| 7 | +using GeneXus.Utils; |
| 8 | + |
| 9 | +namespace GeneXus.AI.Chat |
| 10 | +{ |
| 11 | + public class ChatResult |
| 12 | + { |
| 13 | +#if NETCORE |
| 14 | + static readonly IGXLogger log = GXLoggerFactory.GetLogger<ChatResult>(); |
| 15 | +#endif |
| 16 | + |
| 17 | + private GxHttpClient Client { get; set; } |
| 18 | + private string Agent { get; set; } |
| 19 | + private GXProperties Properties { get; set; } |
| 20 | + private List<ChatMessage> Messages { get; set; } |
| 21 | + private CallResult Result { get; set; } |
| 22 | + private GXProcedure AgentProcedure { get; set; } |
| 23 | + |
| 24 | + public ChatResult() |
| 25 | + { |
| 26 | + } |
| 27 | + |
| 28 | + public ChatResult(GXProcedure agentProcedure, string agent, GXProperties properties, List<ChatMessage> messages, CallResult result, GxHttpClient client) |
| 29 | + { |
| 30 | + AgentProcedure = agentProcedure; |
| 31 | + Agent = agent; |
| 32 | + Properties = properties; |
| 33 | + Messages = messages; |
| 34 | + Result = result; |
| 35 | + Client = client; |
| 36 | + } |
| 37 | + public bool HasMoreData() |
| 38 | + { |
| 39 | + return !Client.Eof; |
| 40 | + } |
| 41 | + public string GetMoreData() |
| 42 | + { |
| 43 | +#if NETCORE |
| 44 | + string data = Client.ReadChunk(); |
| 45 | + if (string.IsNullOrEmpty(data)) |
| 46 | + return string.Empty; |
| 47 | + int index = data.IndexOf(ChatCompletionResult.DATA) + ChatCompletionResult.DATA.Length; |
| 48 | + string chunkJson = data.Substring(index).Trim(); |
| 49 | + try |
| 50 | + { |
| 51 | + ChatCompletionResult chatCompletion = JsonSerializer.Deserialize<ChatCompletionResult>(chunkJson); |
| 52 | + if (chatCompletion?.Choices != null && chatCompletion.Choices.Count > 0) |
| 53 | + { |
| 54 | + Choice choice = chatCompletion.Choices[0]; |
| 55 | + if (choice.FinishReason.ToLower() == ChatCompletionResult.FINISH_REASON_TOOL_CALLS && Agent != null) |
| 56 | + { |
| 57 | + Messages.Add(choice.Message); |
| 58 | + return AgentProcedure.ProcessChatResponse(choice, true, Agent, Properties, Messages, Result); |
| 59 | + } |
| 60 | + else |
| 61 | + { |
| 62 | + return choice.Message.Content ?? string.Empty; |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + catch (Exception ex) |
| 67 | + { |
| 68 | + GXLogging.Error(log, "Error processing chat response:", data, ex); |
| 69 | + } |
| 70 | +#endif |
| 71 | + return string.Empty; |
| 72 | + } |
| 73 | + } |
| 74 | +#if NETCORE |
| 75 | + internal class ChatRequestPayload |
| 76 | + { |
| 77 | + [JsonPropertyName("model")] |
| 78 | + public string Model { get; set; } |
| 79 | + |
| 80 | + [JsonPropertyName("messages")] |
| 81 | + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] |
| 82 | + public List<Chat.ChatMessage> Messages { get; set; } |
| 83 | + |
| 84 | + [JsonPropertyName("stream")] |
| 85 | + public bool? Stream { get; set; } |
| 86 | + |
| 87 | + [JsonPropertyName("variables")] |
| 88 | + public List<GxKeyValuePair> Variables { get; set; } |
| 89 | + } |
| 90 | + |
| 91 | + internal class ChatCompletionResult |
| 92 | + { |
| 93 | + internal const string FINISH_REASON_STOP = "stop"; |
| 94 | + internal const string FINISH_REASON_TOOL_CALLS = "tool_calls"; |
| 95 | + internal const string DATA = "data:"; |
| 96 | + |
| 97 | + [JsonPropertyName("id")] |
| 98 | + public string Id { get; set; } |
| 99 | + |
| 100 | + [JsonPropertyName("object")] |
| 101 | + public string Object { get; set; } |
| 102 | + |
| 103 | + [JsonPropertyName("created")] |
| 104 | + public long Created { get; set; } |
| 105 | + |
| 106 | + [JsonPropertyName("choices")] |
| 107 | + public List<Choice> Choices { get; set; } |
| 108 | + |
| 109 | + [JsonPropertyName("usage")] |
| 110 | + public Usage Usage { get; set; } |
| 111 | + |
| 112 | + [JsonPropertyName("tool_calls")] |
| 113 | + public List<ChatMessage> ToolCalls { get; set; } |
| 114 | + |
| 115 | + [JsonPropertyName("data")] |
| 116 | + public List<DataItem> Data { get; set; } |
| 117 | + } |
| 118 | +#endif |
| 119 | + public class Choice |
| 120 | + { |
| 121 | + [JsonPropertyName("index")] |
| 122 | + public int Index { get; set; } |
| 123 | + |
| 124 | + [JsonPropertyName("message")] |
| 125 | + public ChatMessage Message { get; set; } |
| 126 | + |
| 127 | + [JsonPropertyName("finish_reason")] |
| 128 | + public string FinishReason { get; set; } |
| 129 | + } |
| 130 | + |
| 131 | + |
| 132 | + public class Usage |
| 133 | + { |
| 134 | + [JsonPropertyName("prompt_tokens")] |
| 135 | + public int PromptTokens { get; set; } |
| 136 | + |
| 137 | + [JsonPropertyName("completion_tokens")] |
| 138 | + public int CompletionTokens { get; set; } |
| 139 | + |
| 140 | + [JsonPropertyName("total_tokens")] |
| 141 | + public int TotalTokens { get; set; } |
| 142 | + } |
| 143 | + |
| 144 | + public class DataItem |
| 145 | + { |
| 146 | + [JsonPropertyName("id")] |
| 147 | + public string Id { get; set; } |
| 148 | + |
| 149 | + [JsonPropertyName("object")] |
| 150 | + public string Object { get; set; } |
| 151 | + |
| 152 | + [JsonPropertyName("embedding")] |
| 153 | + public List<double> Embedding { get; set; } |
| 154 | + } |
| 155 | + |
| 156 | +} |
0 commit comments