Decompiled source of ClaudeBackend v1.0.2

plugins/ClaudeBackend.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.CompilerServices;
using HarmonyLib;
using Metater.Persistence;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine.Networking;
using UnityEngine.UIElements;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ClaudeBackend")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ClaudeBackend")]
[assembly: AssemblyTitle("ClaudeBackend")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("local.claudebackend", "ClaudeBackend", "1.0.2")]
public class ClaudeBackend : BaseUnityPlugin
{
	internal static ConfigEntry<string> Effort;

	internal static ConfigEntry<int> MaxTokens;

	internal static ManualLogSource Log;

	private void Awake()
	{
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Effort = ((BaseUnityPlugin)this).Config.Bind<string>("Claude", "Effort", "low", "Default effort (low | medium | high | xhigh | max) when the model's Verbosity tuning is blank. Lower = faster replies.");
		MaxTokens = ((BaseUnityPlugin)this).Config.Bind<int>("Claude", "MaxTokens", 8000, "Minimum max_tokens per reply (thinking counts toward it).");
		new Harmony("local.claudebackend").PatchAll(typeof(ClaudeBackend).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"ClaudeBackend loaded. Pick \"Claude API\" in Host AI settings.");
	}
}
internal static class Mode
{
	public const string Label = "Claude API";

	private const string Flag = "HostAI.ClaudeMode";

	private static readonly string[] Prefs = new string[3] { "HostAI.OpenRouterKey", "HostAI.OpenRouterModel", "HostAI.OpenRouterFavorites" };

	public static bool Claude => MetaSave.Local.GetInt("HostAI.ClaudeMode", 0) == 1;

	public static void Enter()
	{
		if (!Claude)
		{
			Swap(".OpenRouter", ".Claude", 1);
		}
	}

	public static void Leave()
	{
		if (Claude)
		{
			Swap(".Claude", ".OpenRouter", 0);
		}
	}

	private static void Swap(string stashCurrentAs, string loadFrom, int flag)
	{
		string[] prefs = Prefs;
		foreach (string text in prefs)
		{
			Move(text, text + stashCurrentAs);
			Move(text + loadFrom, text);
		}
		MetaSave.Local.SetInt("HostAI.ClaudeMode", flag);
		MetaSave.Local.Save();
	}

	private static void Move(string from, string to)
	{
		string text = MetaSave.Local.GetString(from, string.Empty);
		if (string.IsNullOrEmpty(text))
		{
			MetaSave.Local.DeleteKey(to);
		}
		else
		{
			MetaSave.Local.SetString(to, text);
		}
		MetaSave.Local.DeleteKey(from);
	}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
internal static class PanelCtorPatch
{
	private static void Postfix(HostAiSettingsPanel __instance)
	{
		DropdownField value = Traverse.Create((object)__instance).Field<DropdownField>("backend").Value;
		((BasePopupField<string, string>)(object)value).choices = ((BasePopupField<string, string>)(object)value).choices.Concat(new string[1] { "Claude API" }).ToList();
		Func<string, string> list = ((PopupField<string>)(object)value).formatListItemCallback;
		Func<string, string> sel = ((PopupField<string>)(object)value).formatSelectedValueCallback;
		((PopupField<string>)(object)value).formatListItemCallback = (string v) => (!(v == "Claude API")) ? list(v) : "Claude API";
		((PopupField<string>)(object)value).formatSelectedValueCallback = (string v) => (!(v == "Claude API")) ? sel(v) : "Claude API";
		__instance.Refresh();
	}
}
[HarmonyPatch(typeof(HostAiSettingsPanel), "BackendChanged")]
internal static class BackendChangedPatch
{
	private static bool Prefix(HostAiSettingsPanel __instance, ChangeEvent<string> evt)
	{
		if (!(evt.newValue == "Claude API"))
		{
			if (Mode.Claude)
			{
				Mode.Leave();
				PanelUi.Reset(__instance);
			}
			return true;
		}
		if (HostAiSettings.CanEdit && !Mode.Claude)
		{
			Mode.Enter();
			if (!HostAiSettings.TrySetBackend(1))
			{
				Mode.Leave();
				ClaudeBackend.Log.LogError((object)"Could not switch backend: is ByokUnlock installed?");
			}
			else
			{
				Traverse.Create((object)__instance).Field<Action>("playClick").Value?.Invoke();
			}
			PanelUi.Reset(__instance);
		}
		__instance.Refresh();
		return false;
	}
}
[HarmonyPatch(typeof(HostAiSettingsPanel), "Refresh")]
internal static class RefreshPatch
{
	private static string openRouterPlaceholder;

	private static string openRouterSearch;

	private static void Postfix(HostAiSettingsPanel __instance)
	{
		Traverse val = Traverse.Create((object)__instance);
		if (!val.Field<bool>("disposed").Value)
		{
			if (Mode.Claude && !HostAiSettings.OpenRouterSelected && HostAiSettings.CanEdit)
			{
				Mode.Leave();
				PanelUi.Reset(__instance);
			}
			Label value = val.Field<Label>("keyPlaceholder").Value;
			if (openRouterPlaceholder == null)
			{
				openRouterPlaceholder = ((TextElement)value).text;
			}
			bool flag = Mode.Claude && HostAiSettings.OpenRouterSelected;
			((TextElement)value).text = (flag ? "Paste your Claude API key (sk-ant-...)" : openRouterPlaceholder);
			Label value2 = val.Field<Label>("searchPlaceholder").Value;
			if (openRouterSearch == null)
			{
				openRouterSearch = ((TextElement)value2).text;
			}
			((TextElement)value2).text = (flag ? "Search Claude models..." : openRouterSearch);
			if (flag)
			{
				((BaseField<string>)(object)val.Field<DropdownField>("backend").Value).SetValueWithoutNotify("Claude API");
			}
		}
	}
}
internal static class PanelUi
{
	public static void Reset(HostAiSettingsPanel panel)
	{
		Traverse val = Traverse.Create((object)panel);
		if (val.Field<CancellationTokenSource>("loading").Value != null)
		{
			val.Method("CancelLoad", Array.Empty<object>()).GetValue();
		}
		val.Field<bool>("attemptedLoad").Value = false;
		val.Field<List<OpenRouterModel>>("catalog").Value = new List<OpenRouterModel>();
		((BaseField<string>)(object)val.Field<TextField>("key").Value).SetValueWithoutNotify(HostAiSettings.ApiKey);
		((TextElement)val.Field<Label>("keyStatus").Value).text = (HostAiSettings.HasSavedKey ? "Key saved on this computer." : "Paste your API key.");
		((TextElement)val.Field<Label>("status").Value).text = string.Empty;
		val.Method("RefreshFavorites", Array.Empty<object>()).GetValue();
		val.Method("Filter", Array.Empty<object>()).GetValue();
	}
}
[HarmonyPatch(typeof(OpenRouterModelCatalog), "LoadAsync")]
internal static class CatalogPatch
{
	private static bool Prefix(CancellationToken cancellationToken, ref UniTask<List<OpenRouterModel>> __result)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		if (!Mode.Claude)
		{
			return true;
		}
		__result = Claude.LoadModelsAsync(cancellationToken);
		return false;
	}
}
[HarmonyPatch(typeof(KolkataApi), "SendDirectOpenRouterAsync")]
internal static class SendDirectPatch
{
	private static bool Prefix(JObject body, string key, int timeoutSeconds, CancellationToken cancellationToken, string provider, ref UniTask<JObject> __result)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		if (provider != "OpenRouter" || !Mode.Claude)
		{
			return true;
		}
		__result = Claude.SendAsync(body, key, timeoutSeconds, cancellationToken);
		return false;
	}
}
internal static class Claude
{
	[CompilerGenerated]
	private sealed class <>c__DisplayClass4_0
	{
		public HashSet<string> favorites;

		internal bool <LoadModelsAsync>b__1(OpenRouterModel m)
		{
			return favorites.Contains(m.Id);
		}
	}

	[StructLayout(LayoutKind.Auto)]
	[CompilerGenerated]
	private struct <LoadModelsAsync>d__4 : IAsyncStateMachine
	{
		public int <>1__state;

		public AsyncUniTaskMethodBuilder<List<OpenRouterModel>> <>t__builder;

		public CancellationToken ct;

		private string <key>5__2;

		private Awaiter<(long, string)> <>u__1;

		private void MoveNext()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			int num = <>1__state;
			List<OpenRouterModel> result;
			try
			{
				Awaiter<(long, string)> val;
				if (num != 0)
				{
					<key>5__2 = HostAiSettings.ApiKey;
					if (string.IsNullOrWhiteSpace(<key>5__2))
					{
						throw new InvalidOperationException("Paste your Claude API key, then press Refresh.");
					}
					val = Send("GET", "models?limit=1000", null, <key>5__2, 20, ct).GetAwaiter();
					if (!val.IsCompleted)
					{
						num = (<>1__state = 0);
						<>u__1 = val;
						<>t__builder.AwaitUnsafeOnCompleted<Awaiter<(long, string)>, <LoadModelsAsync>d__4>(ref val, ref this);
						return;
					}
				}
				else
				{
					val = <>u__1;
					<>u__1 = default(Awaiter<(long, string)>);
					num = (<>1__state = -1);
				}
				var (num2, text) = val.GetResult();
				HostAiSettings.RecordKeyResponse(<key>5__2, num2);
				if (num2 != 200)
				{
					throw new InvalidOperationException($"Could not load Claude models (HTTP {num2}): {ErrorMessage(text)}");
				}
				List<OpenRouterModel> list = ParseModels(text);
				if (HostAiSettings.SelectedModel == null && list.Count > 0)
				{
					OpenRouterModel val2 = ((IEnumerable<OpenRouterModel>)list).FirstOrDefault((Func<OpenRouterModel, bool>)new <>c__DisplayClass4_0
					{
						favorites = new HashSet<string>(HostAiSettings.FavoriteModels.Select((OpenRouterModel m) => m.Id))
					}.<LoadModelsAsync>b__1) ?? ((IEnumerable<OpenRouterModel>)list).FirstOrDefault((Func<OpenRouterModel, bool>)((OpenRouterModel m) => m.Id == "claude-opus-5-5")) ?? list[0];
					HostAiSettings.TrySetModel(val2);
					ClaudeBackend.Log.LogInfo((object)("No model selected; using " + val2.Id));
				}
				result = list;
			}
			catch (Exception exception)
			{
				<>1__state = -2;
				<key>5__2 = null;
				<>t__builder.SetException(exception);
				return;
			}
			<>1__state = -2;
			<key>5__2 = null;
			<>t__builder.SetResult(result);
		}

		void IAsyncStateMachine.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			this.MoveNext();
		}

		[DebuggerHidden]
		private void SetStateMachine(IAsyncStateMachine stateMachine)
		{
			<>t__builder.SetStateMachine(stateMachine);
		}

		void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
		{
			//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
			this.SetStateMachine(stateMachine);
		}
	}

	[StructLayout(LayoutKind.Auto)]
	[CompilerGenerated]
	private struct <Send>d__7 : IAsyncStateMachine
	{
		public int <>1__state;

		public AsyncUniTaskMethodBuilder<(long, string)> <>t__builder;

		public string path;

		public string method;

		public int timeoutSeconds;

		public JObject body;

		public string key;

		public CancellationToken ct;

		private UnityWebRequest <request>5__2;

		private Awaiter<UnityWebRequest> <>u__1;

		private void MoveNext()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_0049: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			int num = <>1__state;
			(long, string) result;
			try
			{
				if (num != 0)
				{
					<request>5__2 = new UnityWebRequest("https://api.anthropic.com/v1/" + path, method)
					{
						timeout = timeoutSeconds,
						redirectLimit = 0,
						downloadHandler = (DownloadHandler)new DownloadHandlerBuffer()
					};
				}
				try
				{
					if (num != 0)
					{
						if (body != null)
						{
							<request>5__2.uploadHandler = (UploadHandler)new UploadHandlerRaw(Encoding.UTF8.GetBytes(((JToken)body).ToString((Formatting)0, Array.Empty<JsonConverter>())));
							<request>5__2.SetRequestHeader("Content-Type", "application/json");
						}
						<request>5__2.SetRequestHeader("x-api-key", key);
						<request>5__2.SetRequestHeader("anthropic-version", "2026-10-21");
						JObject obj = body;
						if (((obj != null) ? obj["fallbacks"] : null) != null)
						{
							<request>5__2.SetRequestHeader("anthropic-beta", "server-side-fallback-2026-07-01");
						}
					}
					try
					{
						Awaiter<UnityWebRequest> val;
						if (num != 0)
						{
							val = UnityAsyncExtensions.ToUniTask(<request>5__2.SendWebRequest(), (IProgress<float>)null, (PlayerLoopTiming)8, ct, false).GetAwaiter();
							if (!val.IsCompleted)
							{
								num = (<>1__state = 0);
								<>u__1 = val;
								<>t__builder.AwaitUnsafeOnCompleted<Awaiter<UnityWebRequest>, <Send>d__7>(ref val, ref this);
								return;
							}
						}
						else
						{
							val = <>u__1;
							<>u__1 = default(Awaiter<UnityWebRequest>);
							num = (<>1__state = -1);
						}
						val.GetResult();
					}
					catch (UnityWebRequestException)
					{
					}
					if (<request>5__2.responseCode == 0L)
					{
						throw new KolkataAiServiceException("Claude request failed: " + <request>5__2.error, (string)null, (string)null, (double?)null, (long?)null);
					}
					result = (<request>5__2.responseCode, <request>5__2.downloadHandler.text);
				}
				finally
				{
					if (num < 0 && <request>5__2 != null)
					{
						((IDisposable)<request>5__2).Dispose();
					}
				}
			}
			catch (Exception exception)
			{
				<>1__state = -2;
				<request>5__2 = null;
				<>t__builder.SetException(exception);
				return;
			}
			<>1__state = -2;
			<request>5__2 = null;
			<>t__builder.SetResult(result);
		}

		void IAsyncStateMachine.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			this.MoveNext();
		}

		[DebuggerHidden]
		private void SetStateMachine(IAsyncStateMachine stateMachine)
		{
			<>t__builder.SetStateMachine(stateMachine);
		}

		void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
		{
			//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
			this.SetStateMachine(stateMachine);
		}
	}

	[StructLayout(LayoutKind.Auto)]
	[CompilerGenerated]
	private struct <SendAsync>d__6 : IAsyncStateMachine
	{
		public int <>1__state;

		public AsyncUniTaskMethodBuilder<JObject> <>t__builder;

		public JObject openAi;

		public string key;

		public int timeoutSeconds;

		public CancellationToken ct;

		private Awaiter<(long, string)> <>u__1;

		private void MoveNext()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			int num = <>1__state;
			JObject result;
			try
			{
				Awaiter<(long, string)> val;
				if (num != 0)
				{
					if (num == 1)
					{
						val = <>u__1;
						<>u__1 = default(Awaiter<(long, string)>);
						num = (<>1__state = -1);
						goto IL_014e;
					}
					JObject body = ToClaude(openAi, full: true);
					val = Send("POST", "messages", body, key, timeoutSeconds, ct).GetAwaiter();
					if (!val.IsCompleted)
					{
						num = (<>1__state = 0);
						<>u__1 = val;
						<>t__builder.AwaitUnsafeOnCompleted<Awaiter<(long, string)>, <SendAsync>d__6>(ref val, ref this);
						return;
					}
				}
				else
				{
					val = <>u__1;
					<>u__1 = default(Awaiter<(long, string)>);
					num = (<>1__state = -1);
				}
				var (num2, text) = val.GetResult();
				if (num2 == 400)
				{
					ClaudeBackend.Log.LogWarning((object)("Claude rejected the request, retrying minimal: " + text));
					val = Send("POST", "messages", ToClaude(openAi, full: false), key, timeoutSeconds, ct).GetAwaiter();
					if (!val.IsCompleted)
					{
						num = (<>1__state = 1);
						<>u__1 = val;
						<>t__builder.AwaitUnsafeOnCompleted<Awaiter<(long, string)>, <SendAsync>d__6>(ref val, ref this);
						return;
					}
					goto IL_014e;
				}
				goto IL_0163;
				IL_014e:
				(num2, text) = val.GetResult();
				goto IL_0163;
				IL_0163:
				HostAiSettings.RecordKeyResponse(key, num2);
				if (num2 != 200)
				{
					string text2 = $"Claude request failed (HTTP {num2}): {ErrorMessage(text)}";
					long? num3 = num2;
					throw new KolkataAiServiceException(text2, (string)null, (string)null, (double?)null, num3);
				}
				result = ToOpenAi(JObject.Parse(text));
			}
			catch (Exception exception)
			{
				<>1__state = -2;
				<>t__builder.SetException(exception);
				return;
			}
			<>1__state = -2;
			<>t__builder.SetResult(result);
		}

		void IAsyncStateMachine.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			this.MoveNext();
		}

		[DebuggerHidden]
		private void SetStateMachine(IAsyncStateMachine stateMachine)
		{
			<>t__builder.SetStateMachine(stateMachine);
		}

		void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
		{
			//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
			this.SetStateMachine(stateMachine);
		}
	}

	private const string Api = "https://api.anthropic.com/v1/";

	private static readonly Dictionary<string, (decimal In, decimal Out)> Prices = new Dictionary<string, (decimal, decimal)>
	{
		["claude-fable-5-1"] = (10m, 50m),
		["claude-fable-5"] = (10m, 50m),
		["claude-opus-5-5"] = (4m, 20m),
		["claude-opus-5"] = (5m, 25m),
		["claude-opus-4-8"] = (5m, 25m),
		["claude-opus-4-7"] = (5m, 25m),
		["claude-opus-4-6"] = (5m, 25m),
		["claude-sonnet-5-5"] = (2m, 10m),
		["claude-sonnet-5"] = (2m, 10m),
		["claude-sonnet-4-6"] = (3m, 15m),
		["claude-haiku-5-5"] = (0.10m, 0.50m),
		["claude-haiku-4-5"] = (1m, 5m)
	};

	private static readonly HashSet<string> FallbackModels = new HashSet<string> { "claude-fable-5-1", "claude-opus-5-5", "claude-opus-5", "claude-sonnet-5-5" };

	private static readonly string[] Supported = new string[4] { "max_tokens", "structured_outputs", "response_format", "verbosity" };

	[AsyncStateMachine(typeof(<LoadModelsAsync>d__4))]
	public static UniTask<List<OpenRouterModel>> LoadModelsAsync(CancellationToken ct)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		<LoadModelsAsync>d__4 <LoadModelsAsync>d__ = default(<LoadModelsAsync>d__4);
		<LoadModelsAsync>d__.<>t__builder = AsyncUniTaskMethodBuilder<List<OpenRouterModel>>.Create();
		<LoadModelsAsync>d__.ct = ct;
		<LoadModelsAsync>d__.<>1__state = -1;
		<LoadModelsAsync>d__.<>t__builder.Start<<LoadModelsAsync>d__4>(ref <LoadModelsAsync>d__);
		return <LoadModelsAsync>d__.<>t__builder.Task;
	}

	internal static List<OpenRouterModel> ParseModels(string json)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		JToken obj = JObject.Parse(json)["data"];
		return (from m in ((IEnumerable)(((object)((obj is JArray) ? obj : null)) ?? ((object)new JArray()))).OfType<JObject>()
			where OpenRouterModel.IsValidId((string)m["id"])
			select m).Select((Func<JObject, OpenRouterModel>)delegate(JObject m)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			string text = (string)m["id"];
			(decimal, decimal) value;
			return new OpenRouterModel
			{
				Id = text,
				Name = (((string)m["display_name"]) ?? text),
				SupportedParameters = Supported,
				MaxCompletionTokens = ((int?)m["max_tokens"]).GetValueOrDefault(),
				PriceLabel = (Prices.TryGetValue(text, out value) ? string.Format(CultureInfo.InvariantCulture, "${0:0.##} input / ${1:0.##} output per 1M tokens", value.Item1, value.Item2) : "Claude API")
			};
		}).ToList();
	}

	[AsyncStateMachine(typeof(<SendAsync>d__6))]
	public static UniTask<JObject> SendAsync(JObject openAi, string key, int timeoutSeconds, CancellationToken ct)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		<SendAsync>d__6 <SendAsync>d__ = default(<SendAsync>d__6);
		<SendAsync>d__.<>t__builder = AsyncUniTaskMethodBuilder<JObject>.Create();
		<SendAsync>d__.openAi = openAi;
		<SendAsync>d__.key = key;
		<SendAsync>d__.timeoutSeconds = timeoutSeconds;
		<SendAsync>d__.ct = ct;
		<SendAsync>d__.<>1__state = -1;
		<SendAsync>d__.<>t__builder.Start<<SendAsync>d__6>(ref <SendAsync>d__);
		return <SendAsync>d__.<>t__builder.Task;
	}

	[AsyncStateMachine(typeof(<Send>d__7))]
	private static UniTask<(long, string)> Send(string method, string path, JObject body, string key, int timeoutSeconds, CancellationToken ct)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		<Send>d__7 <Send>d__ = default(<Send>d__7);
		<Send>d__.<>t__builder = AsyncUniTaskMethodBuilder<(long, string)>.Create();
		<Send>d__.method = method;
		<Send>d__.path = path;
		<Send>d__.body = body;
		<Send>d__.key = key;
		<Send>d__.timeoutSeconds = timeoutSeconds;
		<Send>d__.ct = ct;
		<Send>d__.<>1__state = -1;
		<Send>d__.<>t__builder.Start<<Send>d__7>(ref <Send>d__);
		return <Send>d__.<>t__builder.Task;
	}

	internal static JObject ToClaude(JObject src, bool full)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Expected O, but got Unknown
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Expected O, but got Unknown
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f5: Expected O, but got Unknown
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0200: Unknown result type (might be due to invalid IL or missing references)
		//IL_0230: Expected O, but got Unknown
		//IL_023b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0240: Unknown result type (might be due to invalid IL or missing references)
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Expected O, but got Unknown
		StringBuilder stringBuilder = new StringBuilder();
		JArray val = new JArray();
		JToken obj = src["messages"];
		foreach (JObject item in ((IEnumerable)(((object)((obj is JArray) ? obj : null)) ?? ((object)new JArray()))).OfType<JObject>())
		{
			string text = (string)item["role"];
			string text2 = Text(item["content"]);
			if (text == "system" || text == "developer")
			{
				Append(stringBuilder, text2);
			}
			else if (!string.IsNullOrEmpty(text2))
			{
				val.Add((JToken)new JObject
				{
					["role"] = JToken.op_Implicit((text == "assistant") ? "assistant" : "user"),
					["content"] = JToken.op_Implicit(text2)
				});
			}
		}
		if (((JContainer)val).Count == 0 || (string)((JToken)val).Last[(object)"role"] == "assistant")
		{
			val.Add((JToken)new JObject
			{
				["role"] = JToken.op_Implicit("user"),
				["content"] = JToken.op_Implicit("Continue.")
			});
		}
		string text3 = (string)src["model"];
		JToken obj2 = src["response_format"];
		JObject val2 = (JObject)(object)((obj2 is JObject) ? obj2 : null);
		object obj3;
		if (val2 == null)
		{
			obj3 = null;
		}
		else
		{
			JToken obj4 = val2["json_schema"];
			obj3 = ((obj4 != null) ? obj4[(object)"schema"] : null);
		}
		JObject val3 = (JObject)((obj3 is JObject) ? obj3 : null);
		JObject val4 = new JObject
		{
			["model"] = JToken.op_Implicit(text3),
			["max_tokens"] = JToken.op_Implicit(Math.Max(((int?)src["max_tokens"]).GetValueOrDefault(), ClaudeBackend.MaxTokens.Value)),
			["messages"] = (JToken)(object)val
		};
		if (full)
		{
			JObject val5 = new JObject { ["effort"] = JToken.op_Implicit(((string)src["verbosity"]) ?? ClaudeBackend.Effort.Value) };
			if (val3 != null)
			{
				val5["format"] = (JToken)new JObject
				{
					["type"] = JToken.op_Implicit("json_schema"),
					["schema"] = (JToken)(object)val3
				};
			}
			val4["output_config"] = (JToken)(object)val5;
			if (FallbackModels.Contains(text3))
			{
				val4["fallbacks"] = JToken.op_Implicit("default");
			}
		}
		if (val3 != null && !full)
		{
			Append(stringBuilder, "Reply with only a JSON object matching this schema, no markdown:\n" + ((JToken)val3).ToString((Formatting)0, Array.Empty<JsonConverter>()));
		}
		else if (val3 == null && (string)((val2 != null) ? val2["type"] : null) == "json_object")
		{
			Append(stringBuilder, "Reply with only a valid JSON object, no markdown.");
		}
		if (stringBuilder.Length > 0)
		{
			val4["system"] = JToken.op_Implicit(stringBuilder.ToString());
		}
		return val4;
	}

	internal static JObject ToOpenAi(JObject res)
	{
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Expected O, but got Unknown
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Expected O, but got Unknown
		//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Expected O, but got Unknown
		//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Expected O, but got Unknown
		//IL_021b: Expected O, but got Unknown
		string text = (string)res["stop_reason"];
		if (text == "refusal")
		{
			JToken obj = res["stop_details"];
			throw new KolkataAiServiceException("Claude declined this request (" + (string)((obj != null) ? obj[(object)"category"] : null) + ").", (string)null, (string)null, (double?)null, (long?)null);
		}
		JToken obj2 = res["content"];
		string text2 = StripFence(string.Concat(from b in (IEnumerable<JToken>)(((object)((obj2 is JArray) ? obj2 : null)) ?? ((object)new JArray()))
			where (string)b[(object)"type"] == "text"
			select (string)b[(object)"text"]));
		if (text2.Length == 0)
		{
			throw new KolkataAiServiceException("Claude returned no text (stop_reason " + text + ").", (string)null, (string)null, (double?)null, (long?)null);
		}
		JObject val = new JObject
		{
			["id"] = res["id"],
			["model"] = res["model"],
			["choices"] = (JToken)new JArray((object)new JObject
			{
				["index"] = JToken.op_Implicit(0),
				["message"] = (JToken)new JObject
				{
					["role"] = JToken.op_Implicit("assistant"),
					["content"] = JToken.op_Implicit(text2)
				},
				["finish_reason"] = JToken.op_Implicit((text == "max_tokens") ? "length" : "stop")
			})
		};
		JObject val2 = new JObject();
		JToken obj3 = res["usage"];
		val2["prompt_tokens"] = ((obj3 != null) ? obj3[(object)"input_tokens"] : null);
		JToken obj4 = res["usage"];
		val2["completion_tokens"] = ((obj4 != null) ? obj4[(object)"output_tokens"] : null);
		val["usage"] = (JToken)val2;
		return val;
	}

	private static string Text(JToken content)
	{
		if (content != null)
		{
			JArray val = (JArray)(object)((content is JArray) ? content : null);
			if (val != null)
			{
				return string.Join("\n", from p in ((IEnumerable)val).OfType<JObject>()
					where p["text"] != null
					select (string)p["text"]);
			}
			return ((string)content) ?? "";
		}
		return "";
	}

	private static void Append(StringBuilder sb, string s)
	{
		if (!string.IsNullOrEmpty(s))
		{
			if (sb.Length > 0)
			{
				sb.Append("\n\n");
			}
			sb.Append(s);
		}
	}

	private static string StripFence(string t)
	{
		t = t.Trim();
		if (!t.StartsWith("```"))
		{
			return t;
		}
		int num = t.IndexOf('\n');
		int num2 = t.LastIndexOf("```", StringComparison.Ordinal);
		if (num <= 0 || num2 <= num)
		{
			return t;
		}
		return t.Substring(num + 1, num2 - num - 1).Trim();
	}

	private static string ErrorMessage(string body)
	{
		try
		{
			JToken obj = JObject.Parse(body)["error"];
			return ((string)((obj != null) ? obj[(object)"message"] : null)) ?? body;
		}
		catch (JsonException)
		{
			return body;
		}
	}
}