|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
using System.Text.Json.Serialization;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Data.Sqlite;
|
|
|
|
namespace AISApp
|
|
{
|
|
// JSON DTOs for API
|
|
public sealed class ChatMessage
|
|
{
|
|
[JsonPropertyName("role")] public string Role { get; set; } = "";
|
|
[JsonPropertyName("content")] public string Content { get; set; } = "";
|
|
}
|
|
|
|
public sealed class ChatRequest
|
|
{
|
|
[JsonPropertyName("model")] public string Model { get; set; } = "";
|
|
[JsonPropertyName("messages")] public List<ChatMessage> Messages { get; set; } = new();
|
|
[JsonPropertyName("temperature")] public double Temperature { get; set; } = 0.0;
|
|
}
|
|
|
|
public sealed class ChatChoice
|
|
{
|
|
[JsonPropertyName("message")] public ChatMessage Message { get; set; } = new();
|
|
}
|
|
|
|
public sealed class ChatResponse
|
|
{
|
|
[JsonPropertyName("choices")] public List<ChatChoice> Choices { get; set; } = new();
|
|
}
|
|
|
|
public sealed class AIS
|
|
{
|
|
// -------- Static / constants --------
|
|
private static readonly HttpClient Http = new HttpClient();
|
|
private static readonly JsonSerializerOptions JsonOpts = new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
WriteIndented = false
|
|
};
|
|
|
|
public static readonly Dictionary<string, string> PredefinedModels = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
["dobby"] = "sentientagi/dobby-mini-unhinged-plus-llama-3.1-8b",
|
|
["dolphin"] = "cognitivecomputations/dolphin-mixtral-8x22b",
|
|
["dolphin_free"] = "cognitivecomputations/dolphin3.0-mistral-24b:free",
|
|
["gemma"] = "google/gemma-3-12b-it",
|
|
["gpt-4o-mini"] = "openai/gpt-4o-mini",
|
|
["gpt-4.1-nano"] = "openai/gpt-4.1-nano",
|
|
["qwen"] = "qwen/qwen3-30b-a3b",
|
|
["unslop"] = "thedrummer/unslopnemo-12b",
|
|
["euryale"] = "sao10k/l3.3-euryale-70b",
|
|
["wizard"] = "microsoft/wizardlm-2-8x22b",
|
|
["deepseek"] = "deepseek/deepseek-chat-v3-0324"
|
|
};
|
|
|
|
// -------- Instance state (mirrors Python) --------
|
|
public string Name { get; private set; }
|
|
public double Temperature { get; private set; }
|
|
public double Backoff { get; private set; }
|
|
public int MaxRetries { get; private set; }
|
|
public bool SafeMode { get; private set; }
|
|
public string SystemMessage { get; private set; }
|
|
public string Model { get; private set; }
|
|
public string BaseUrl { get; private set; }
|
|
public string Rel { get; private set; }
|
|
public string ApiKey { get; private set; }
|
|
public string DbName { get; private set; }
|
|
public bool Loaded { get; private set; }
|
|
|
|
private readonly List<ChatMessage> _messages = new();
|
|
private readonly SqliteConnection _conn;
|
|
|
|
private Dictionary<string, string> Headers => new()
|
|
{
|
|
["Authorization"] = $"Bearer {ApiKey}",
|
|
["Content-Type"] = "application/json"
|
|
};
|
|
|
|
// -------- ctor --------
|
|
public AIS(
|
|
string? systemMessage = null,
|
|
string model = "gemma",
|
|
int maxRetries = 3,
|
|
double backoff = 1,
|
|
string? name = null,
|
|
double temperature = 0.0,
|
|
string dbName = "tuna_ais_client.db",
|
|
bool safeMode = true,
|
|
string baseUrl = "openrouter.ai",
|
|
string rel = "/api",
|
|
string? apiKey = null
|
|
|
|
)
|
|
{
|
|
Console.WriteLine($"[INFO] Initializing AIS name={name} model={model} db={dbName} api_key={apiKey}");
|
|
Loaded = dbName != ":memory:" && File.Exists(dbName);
|
|
DbName = dbName;
|
|
|
|
// Open connection and create schema
|
|
_conn = OpenConnection(dbName);
|
|
InitDb();
|
|
|
|
// If existing DB, hydrate settings first
|
|
if (Loaded)
|
|
{
|
|
temperature = GetSetting("temperature", temperature);
|
|
name = GetSetting("name", name ?? "");
|
|
backoff = GetSetting("backoff", backoff);
|
|
maxRetries = GetSetting("max_retries", maxRetries);
|
|
safeMode = GetSetting("safe_mode", safeMode);
|
|
systemMessage = GetSetting("system_message", systemMessage ?? "");
|
|
model = GetSetting("model", model);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(systemMessage))
|
|
{
|
|
systemMessage = "You respond like a calculator; only the result is the answer. Respond only with the result. No explanations or extra words.";
|
|
}
|
|
|
|
SystemMessage = systemMessage;
|
|
Name = !string.IsNullOrEmpty(name)
|
|
? $"ais_{name}"
|
|
: $"ais_{Guid.NewGuid().ToString("N").Substring(0, 8)}";
|
|
|
|
Backoff = backoff;
|
|
SafeMode = safeMode;
|
|
MaxRetries = maxRetries;
|
|
BaseUrl = baseUrl;
|
|
Rel = rel;
|
|
ApiKey = apiKey ?? Environment.GetEnvironmentVariable("OPENROUTER_API_KEY")
|
|
?? "sk-or-REPLACE_ME"; // keep explicit default like Python had
|
|
Temperature = temperature;
|
|
Model = PredefinedModels.TryGetValue(model, out var mapped) ? mapped : model;
|
|
|
|
if (MessagesExist())
|
|
{
|
|
_messages = LoadMessages();
|
|
}
|
|
else
|
|
{
|
|
_messages.Add(new ChatMessage { Role = "system", Content = SystemMessage });
|
|
SaveMessage("system", SystemMessage);
|
|
}
|
|
|
|
Console.WriteLine($"[INFO] Initialized AIS name={Name} model={Model} db={DbName}");
|
|
}
|
|
|
|
// -------- DB helpers --------
|
|
private static SqliteConnection OpenConnection(string dbName)
|
|
{
|
|
var cs = dbName == ":memory:" ? "Data Source=:memory:" : $"Data Source={dbName}";
|
|
var conn = new SqliteConnection(cs);
|
|
conn.Open();
|
|
return conn;
|
|
}
|
|
|
|
private void InitDb()
|
|
{
|
|
using var cmd1 = _conn.CreateCommand();
|
|
cmd1.CommandText = @"
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
role TEXT NOT NULL,
|
|
content TEXT NOT NULL
|
|
)";
|
|
cmd1.ExecuteNonQuery();
|
|
|
|
using var cmd2 = _conn.CreateCommand();
|
|
cmd2.CommandText = @"
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
json_value TEXT NOT NULL
|
|
)";
|
|
cmd2.ExecuteNonQuery();
|
|
}
|
|
|
|
private bool MessagesExist()
|
|
{
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "SELECT COUNT(*) FROM messages";
|
|
var count = Convert.ToInt32(cmd.ExecuteScalar());
|
|
return count > 0;
|
|
}
|
|
|
|
private List<ChatMessage> LoadMessages()
|
|
{
|
|
var list = new List<ChatMessage>();
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "SELECT role, content FROM messages ORDER BY id ASC";
|
|
using var rdr = cmd.ExecuteReader();
|
|
while (rdr.Read())
|
|
{
|
|
list.Add(new ChatMessage
|
|
{
|
|
Role = rdr.GetString(0),
|
|
Content = rdr.GetString(1)
|
|
});
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private void SaveMessage(string role, string content)
|
|
{
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "INSERT INTO messages (role, content) VALUES ($r, $c)";
|
|
cmd.Parameters.AddWithValue("$r", role);
|
|
cmd.Parameters.AddWithValue("$c", content);
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
// Generic typed setting helpers
|
|
private T GetSetting<T>(string name, T defaultValue)
|
|
{
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "SELECT json_value FROM settings WHERE name = $n LIMIT 1";
|
|
cmd.Parameters.AddWithValue("$n", name);
|
|
var obj = cmd.ExecuteScalar();
|
|
if (obj is string s)
|
|
{
|
|
try
|
|
{
|
|
var node = JsonNode.Parse(s);
|
|
if (node is null) return defaultValue!;
|
|
return node.Deserialize<T>(JsonOpts)!;
|
|
}
|
|
catch
|
|
{
|
|
return defaultValue!;
|
|
}
|
|
}
|
|
return defaultValue!;
|
|
}
|
|
|
|
private void SetSetting(string name, object? value)
|
|
{
|
|
var json = JsonSerializer.Serialize(value, JsonOpts);
|
|
|
|
using (var upd = _conn.CreateCommand())
|
|
{
|
|
upd.CommandText = "UPDATE settings SET json_value = $v WHERE name = $n";
|
|
upd.Parameters.AddWithValue("$v", json);
|
|
upd.Parameters.AddWithValue("$n", name);
|
|
var rows = upd.ExecuteNonQuery();
|
|
if (rows == 0)
|
|
{
|
|
using var ins = _conn.CreateCommand();
|
|
ins.CommandText = "INSERT INTO settings (name, json_value) VALUES ($n, $v)";
|
|
ins.Parameters.AddWithValue("$n", name);
|
|
ins.Parameters.AddWithValue("$v", json);
|
|
ins.ExecuteNonQuery();
|
|
}
|
|
}
|
|
|
|
// Reflectively update instance if property exists (case-insensitive)
|
|
var prop = GetType().GetProperty(name, System.Reflection.BindingFlags.Instance |
|
|
System.Reflection.BindingFlags.Public |
|
|
System.Reflection.BindingFlags.IgnoreCase);
|
|
if (prop != null && prop.CanWrite)
|
|
{
|
|
try
|
|
{
|
|
var targetVal = value;
|
|
if (value is JsonNode node)
|
|
{
|
|
targetVal = node.Deserialize(prop.PropertyType, JsonOpts);
|
|
}
|
|
else if (value is string str && prop.PropertyType != typeof(string))
|
|
{
|
|
try
|
|
{
|
|
var jn = JsonNode.Parse(str);
|
|
if (jn != null) targetVal = jn.Deserialize(prop.PropertyType, JsonOpts);
|
|
}
|
|
catch { /* ignore */ }
|
|
}
|
|
prop.SetValue(this, ConvertIfNeeded(targetVal, prop.PropertyType));
|
|
}
|
|
catch { /* ignore */ }
|
|
}
|
|
}
|
|
|
|
private static object? ConvertIfNeeded(object? value, Type targetType)
|
|
{
|
|
if (value == null) return null;
|
|
if (targetType.IsAssignableFrom(value.GetType())) return value;
|
|
try { return Convert.ChangeType(value, targetType); }
|
|
catch { return value; }
|
|
}
|
|
|
|
private void SaveSettings()
|
|
{
|
|
SetSetting("name", Name);
|
|
SetSetting("temperature", Temperature);
|
|
SetSetting("backoff", Backoff);
|
|
SetSetting("max_retries", MaxRetries);
|
|
SetSetting("safe_mode", SafeMode);
|
|
SetSetting("system_message", SystemMessage);
|
|
SetSetting("model", Model);
|
|
}
|
|
|
|
// -------- Public API (mirrors Python) --------
|
|
public void Append(string messageUser, string messageAssistant, bool save = true)
|
|
{
|
|
_messages.Add(new ChatMessage { Role = "user", Content = messageUser });
|
|
if (save) SaveMessage("user", messageUser);
|
|
_messages.Add(new ChatMessage { Role = "assistant", Content = messageAssistant });
|
|
if (save) SaveMessage("assistant", messageAssistant);
|
|
}
|
|
|
|
public void Save(string dbPath)
|
|
{
|
|
if (DbName != ":memory:")
|
|
{
|
|
Console.WriteLine("[INFO] Already using persistent database.");
|
|
return;
|
|
}
|
|
var dbFile = dbPath.EndsWith(".db", StringComparison.OrdinalIgnoreCase) ? dbPath : dbPath + ".db";
|
|
|
|
using var dest = new SqliteConnection($"Data Source={dbFile}");
|
|
dest.Open();
|
|
_conn.BackupDatabase(dest);
|
|
dest.Close();
|
|
|
|
DbName = dbFile;
|
|
Console.WriteLine($"[INFO] Converted in-memory DB to persistent DB at {DbName}");
|
|
}
|
|
|
|
public void PatchTime()
|
|
{
|
|
const string timePrefix = "Current time: `";
|
|
if (_messages.Count == 0) return;
|
|
|
|
var content = _messages[0].Content ?? "";
|
|
var nowStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
|
if (content.StartsWith(timePrefix, StringComparison.Ordinal))
|
|
{
|
|
var end = content.IndexOf('`', timePrefix.Length);
|
|
if (end >= 0)
|
|
{
|
|
var rest = content.Substring(end + 1).TrimStart('\n');
|
|
_messages[0].Content = $"{timePrefix}{nowStr}`\n\n{rest}";
|
|
}
|
|
else
|
|
{
|
|
_messages[0].Content = $"{timePrefix}{nowStr}`\n\n{content}";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_messages[0].Content = $"{timePrefix}{nowStr}`\n\n{content}";
|
|
}
|
|
|
|
// Persist system message update
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "UPDATE messages SET content = $c WHERE id = (SELECT MIN(id) FROM messages)";
|
|
cmd.Parameters.AddWithValue("$c", _messages[0].Content);
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
public async Task<object> ChatAsync(string message, double? temperature = null, CancellationToken ct = default)
|
|
{
|
|
if (!SafeMode)
|
|
{
|
|
// command handling
|
|
if (message.StartsWith("!reset", StringComparison.OrdinalIgnoreCase)) { Reset(); return "true"; }
|
|
if (message.StartsWith("!instruct", StringComparison.OrdinalIgnoreCase)) { Instruct(message.Length >= 10 ? message[10..] : ""); return "true"; }
|
|
if (message.StartsWith("!undo", StringComparison.OrdinalIgnoreCase)) { return Undo().ToString().ToLowerInvariant(); }
|
|
if (message.StartsWith("!get", StringComparison.OrdinalIgnoreCase)) { var key = message[4..].Trim(); return GetRawSettingOr("not found", key); }
|
|
if (message.StartsWith("!set", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var rest = message.Length >= 5 ? message[5..] : "";
|
|
var idx = rest.IndexOf(' ');
|
|
if (idx > 0)
|
|
{
|
|
var nm = rest[..idx].Trim();
|
|
var val = rest[(idx + 1)..].Trim();
|
|
SetSetting(nm, TryJson(val));
|
|
return "true";
|
|
}
|
|
}
|
|
if (message.StartsWith("!query", StringComparison.OrdinalIgnoreCase)) { return Query(message.Length >= 6 ? message[6..] : ""); }
|
|
if (message.StartsWith("!edit", StringComparison.OrdinalIgnoreCase)) { Edit(message.Length >= 5 ? message[5..] : ""); return SystemMessage; }
|
|
if (message.StartsWith("!append", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var rest = message.Length >= 7 ? message[7..] : "";
|
|
var firstColon = rest.IndexOf(':');
|
|
if (firstColon >= 0 && rest.IndexOf(':', firstColon + 1) < 0)
|
|
{
|
|
var user = rest[..firstColon];
|
|
var assistantMessage = rest[(firstColon + 1)..];
|
|
Append(user, assistantMessage);
|
|
return "true";
|
|
}
|
|
}
|
|
}
|
|
|
|
PatchTime();
|
|
|
|
_messages.Add(new ChatMessage { Role = "user", Content = message });
|
|
SaveMessage("user", message);
|
|
|
|
var payload = new ChatRequest
|
|
{
|
|
Model = Model,
|
|
Messages = _messages,
|
|
Temperature = temperature ?? Temperature
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(payload, JsonOpts);
|
|
var url = $"https://{BaseUrl}{Rel}/v1/chat/completions";
|
|
Console.WriteLine(url);
|
|
|
|
int attempt = 0;
|
|
string assistantResponse = "ERROR";
|
|
double backoff = Backoff;
|
|
|
|
while (attempt < MaxRetries)
|
|
{
|
|
try
|
|
{
|
|
using var req = new HttpRequestMessage(HttpMethod.Post, url)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
|
};
|
|
foreach (var kv in Headers) req.Headers.TryAddWithoutValidation(kv.Key, kv.Value);
|
|
|
|
using var resp = await Http.SendAsync(req, ct);
|
|
var text = await resp.Content.ReadAsStringAsync(ct);
|
|
|
|
//var parsed = JsonSerializer.Deserialize<ChatResponse>(text, JsonOpts);
|
|
//assistantResponse = parsed?.Choices?[0]?.Message?.Content ?? "ERROR";
|
|
var parsed = JsonSerializer.Deserialize<ChatResponse>(text, JsonOpts);
|
|
if (parsed?.Choices != null && parsed.Choices.Count > 0)
|
|
{
|
|
var msg = parsed.Choices[0]?.Message?.Content;
|
|
assistantResponse = string.IsNullOrEmpty(msg) ? "ERROR" : msg!;
|
|
}
|
|
else
|
|
{
|
|
assistantResponse = text;
|
|
// assistantResponse = "ERROR";
|
|
}
|
|
|
|
|
|
SaveSettings();
|
|
|
|
_messages.Add(new ChatMessage { Role = "assistant", Content = assistantResponse });
|
|
SaveMessage("assistant", assistantResponse);
|
|
|
|
return TryJson(assistantResponse);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
attempt++;
|
|
Console.WriteLine($"[WARN] Chat attempt {attempt} failed: {ex.Message}");
|
|
if (attempt < MaxRetries)
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(backoff), ct);
|
|
backoff *= 2;
|
|
}
|
|
else
|
|
{
|
|
_messages.Add(new ChatMessage { Role = "assistant", Content = "ERROR" });
|
|
SaveMessage("assistant", "ERROR");
|
|
Console.WriteLine($"[CRITICAL] Failed after {MaxRetries} attempts.");
|
|
return "ERROR";
|
|
}
|
|
}
|
|
}
|
|
|
|
return "ERROR";
|
|
}
|
|
|
|
public object Chat(string message, double? temperature = null)
|
|
=> ChatAsync(message, temperature).GetAwaiter().GetResult();
|
|
|
|
// JSON helper that mirrors Python _try_json (also strips ``` fences)
|
|
private static object TryJson(string response)
|
|
{
|
|
var fixedStr = (response ?? "").Trim();
|
|
if (fixedStr.StartsWith("```", StringComparison.Ordinal) && fixedStr.EndsWith("```", StringComparison.Ordinal))
|
|
{
|
|
var lines = fixedStr.Split('\n');
|
|
if (lines.Length >= 2)
|
|
{
|
|
fixedStr = string.Join("\n", lines, 1, lines.Length - 2).Trim();
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
var node = JsonNode.Parse(fixedStr);
|
|
return node ?? response;
|
|
}
|
|
catch
|
|
{
|
|
return response;
|
|
}
|
|
}
|
|
|
|
private object GetRawSettingOr(string fallback, string key)
|
|
{
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "SELECT json_value FROM settings WHERE name = $n LIMIT 1";
|
|
cmd.Parameters.AddWithValue("$n", key);
|
|
var obj = cmd.ExecuteScalar();
|
|
return obj is string s ? s : fallback;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
Console.WriteLine("[INFO] Reset conversation history (keeping system message).");
|
|
ChatMessage sys = _messages.Count > 0 ? _messages[0] : new ChatMessage { Role = "system", Content = "" };
|
|
_messages.Clear();
|
|
_messages.Add(sys);
|
|
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "DELETE FROM messages WHERE id != (SELECT MIN(id) FROM messages)";
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
public void Instruct(string instructions)
|
|
{
|
|
SystemMessage = instructions;
|
|
Console.WriteLine($"[INFO] System instructions updated.");
|
|
SaveSettings();
|
|
|
|
if (_messages.Count > 0)
|
|
{
|
|
_messages[0].Content = instructions;
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "UPDATE messages SET content = $c WHERE id = (SELECT MIN(id) FROM messages)";
|
|
cmd.Parameters.AddWithValue("$c", instructions);
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
else
|
|
{
|
|
_messages.Add(new ChatMessage { Role = "system", Content = instructions });
|
|
SaveMessage("system", instructions);
|
|
}
|
|
Reset();
|
|
}
|
|
|
|
public object Query(string query)
|
|
{
|
|
var response = Chat(query);
|
|
Undo();
|
|
return response;
|
|
}
|
|
|
|
private bool DeleteLastMessage()
|
|
{
|
|
try
|
|
{
|
|
if (_messages.Count == 0) return false;
|
|
_messages.RemoveAt(_messages.Count - 1);
|
|
|
|
using var cmd = _conn.CreateCommand();
|
|
cmd.CommandText = "DELETE FROM messages WHERE id = (SELECT MAX(id) FROM messages)";
|
|
cmd.ExecuteNonQuery();
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"[ERROR] Undo error: {e.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool Edit(string change)
|
|
{
|
|
var prompt = $"Apply this update: `{change}` to `{SystemMessage}` and return the result";
|
|
var result = Query(prompt);
|
|
var newSys = result is JsonNode jn ? jn.ToJsonString() : result?.ToString() ?? SystemMessage;
|
|
Instruct(newSys);
|
|
return true;
|
|
}
|
|
|
|
public bool Undo()
|
|
{
|
|
// delete assistant + user
|
|
return DeleteLastMessage() && DeleteLastMessage();
|
|
}
|
|
|
|
public void Repl()
|
|
{
|
|
Console.WriteLine("[INFO] REPL started. Ctrl+C to quit.");
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
Console.Write("You: ");
|
|
var line = Console.ReadLine();
|
|
if (string.IsNullOrWhiteSpace(line)) continue;
|
|
|
|
var response = Chat(line);
|
|
var typeName = response switch
|
|
{
|
|
JsonNode => "JsonNode",
|
|
null => "null",
|
|
_ => response.GetType().Name
|
|
};
|
|
Console.WriteLine($"{Name}: {typeName} {FormatForPrint(response)}");
|
|
}
|
|
catch (OperationCanceledException) { break; }
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[CRITICAL] Unexpected error: {ex.Message}");
|
|
Console.WriteLine("AI: ERROR");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static string FormatForPrint(object? obj)
|
|
{
|
|
if (obj is JsonNode jn) return jn.ToJsonString(new JsonSerializerOptions { WriteIndented = false });
|
|
return obj?.ToString() ?? "null";
|
|
}
|
|
}
|
|
|
|
internal static class Program
|
|
{
|
|
// Equivalent to: if __name__ == '__main__': ai = AIS(model="wizard", safe_mode=False); ai.repl()
|
|
private static void Main(string[] args)
|
|
{
|
|
var ai = new AIS(model: "wizard", safeMode: false);
|
|
ai.Repl();
|
|
}
|
|
}
|
|
}
|
|
|