21 [LocalRemote]
public bool remote =
false;
23 [Local, SerializeField]
protected LLM _llm;
34 [Remote]
public string host =
"localhost";
36 [Remote]
public int port = 13333;
40 protected LLM _prellm;
41 protected List<(string, string)> requestHeaders;
42 protected List<UnityWebRequest> WIPRequests =
new List<UnityWebRequest>();
58 requestHeaders =
new List<(string, string)> { (
"Content-Type",
"application/json") };
64 string error = $
"No LLM assigned or detected for LLMCharacter {name}!";
66 throw new Exception(error);
71 if (!String.IsNullOrEmpty(
APIKey)) requestHeaders.Add((
"Authorization",
"Bearer " +
APIKey));
79 protected virtual void SetLLM(
LLM llmSet)
110 protected virtual string NotValidLLMError()
112 return $
"Can't set LLM {llm.name} to {name}";
115 protected virtual void OnValidate()
117 if (_llm != _prellm) SetLLM(_llm);
121 protected virtual void Reset()
126 protected virtual void AssignLLM()
128 if (
remote || llm !=
null)
return;
130 List<LLM> validLLMs =
new List<LLM>();
131#if UNITY_6000_0_OR_NEWER
132 foreach (LLM foundllm
in FindObjectsByType(typeof(LLM), FindObjectsSortMode.None))
134 foreach (LLM foundllm
in FindObjectsOfType<LLM>())
139 if (validLLMs.Count == 0)
return;
141 llm = SortLLMsByBestMatching(validLLMs.ToArray())[0];
142 string msg = $
"Assigning LLM {llm.name} to {GetType()} {name}";
143 if (llm.gameObject.scene != gameObject.scene) msg += $
" from scene {llm.gameObject.scene}";
144 LLMUnitySetup.Log(msg);
147 protected virtual LLM[] SortLLMsByBestMatching(LLM[] arrayIn)
149 LLM[] array = (LLM[])arrayIn.Clone();
150 for (
int i = 0; i < array.Length - 1; i++)
152 bool swapped =
false;
153 for (
int j = 0; j < array.Length - i - 1; j++)
155 bool sameScene = array[j].gameObject.scene == array[j + 1].gameObject.scene;
157 (!sameScene && array[j + 1].gameObject.scene == gameObject.scene) ||
158 (sameScene && array[j].transform.GetSiblingIndex() > array[j + 1].transform.GetSiblingIndex())
163 array[j] = array[j + 1];
173 protected virtual List<int> TokenizeContent(TokenizeResult result)
176 return result.tokens;
179 protected virtual string DetokenizeContent(TokenizeRequest result)
182 return result.content;
185 protected virtual List<float> EmbeddingsContent(EmbeddingsResult result)
188 return result.embedding;
191 protected virtual Ret ConvertContent<Res, Ret>(
string response, ContentCallback<Res, Ret> getContent =
null)
194 if (response ==
null)
return default;
195 response = response.Trim();
196 if (response.StartsWith(
"data: "))
198 string responseArray =
"";
199 foreach (
string responsePart
in response.Replace(
"\n\n",
"").Split(
"data: "))
201 if (responsePart ==
"")
continue;
202 if (responseArray !=
"") responseArray +=
",\n";
203 responseArray += responsePart;
205 response = $
"{{\"data\": [{responseArray}]}}";
207 return getContent(JsonUtility.FromJson<Res>(response));
210 protected virtual void CancelRequestsLocal() {}
212 protected virtual void CancelRequestsRemote()
214 foreach (UnityWebRequest request
in WIPRequests)
227 if (
remote) CancelRequestsRemote();
228 else CancelRequestsLocal();
231 protected virtual async Task<Ret> PostRequestLocal<Res, Ret>(
string json,
string endpoint, ContentCallback<Res, Ret> getContent, Callback<Ret> callback =
null)
236 string callResult =
null;
240 callResult = await llm.
Tokenize(json);
249 callResult = await llm.
Slot(json);
256 Ret result = ConvertContent(callResult, getContent);
257 callback?.Invoke(result);
261 protected virtual async Task<Ret> PostRequestRemote<Res, Ret>(
string json,
string endpoint, ContentCallback<Res, Ret> getContent, Callback<Ret> callback =
null)
265 if (endpoint ==
"slots")
267 LLMUnitySetup.LogError(
"Saving and loading is not currently supported in remote setting");
271 Ret result =
default;
272 byte[] jsonToSend =
new System.Text.UTF8Encoding().GetBytes(json);
273 UnityWebRequest request =
null;
279 using (request = UnityWebRequest.Put($
"{host}:{port}/{endpoint}", jsonToSend))
281 WIPRequests.Add(request);
283 request.method =
"POST";
284 if (requestHeaders !=
null)
286 for (
int i = 0; i < requestHeaders.Count; i++)
287 request.SetRequestHeader(requestHeaders[i].Item1, requestHeaders[i].Item2);
291 UnityWebRequestAsyncOperation asyncOperation = request.SendWebRequest();
294 float lastProgress = 0f;
296 while (!asyncOperation.isDone)
298 float currentProgress = request.downloadProgress;
300 if (currentProgress != lastProgress && callback !=
null)
302 callback?.Invoke(ConvertContent(request.downloadHandler.text, getContent));
303 lastProgress = currentProgress;
308 WIPRequests.Remove(request);
309 if (request.result == UnityWebRequest.Result.Success)
311 result = ConvertContent(request.downloadHandler.text, getContent);
318 error = request.error;
319 if (request.responseCode == (
int)System.Net.HttpStatusCode.Unauthorized)
break;
323 if (tryNr > 0) await Task.Delay(200 * (
numRetries - tryNr));
326 if (error !=
null) LLMUnitySetup.LogError(error);
327 callback?.Invoke(result);
331 protected virtual async Task<Ret> PostRequest<Res, Ret>(
string json,
string endpoint, ContentCallback<Res, Ret> getContent, Callback<Ret> callback =
null)
333 if (
remote)
return await PostRequestRemote(json, endpoint, getContent, callback);
334 return await PostRequestLocal(json, endpoint, getContent, callback);
343 public virtual async Task<List<int>>
Tokenize(
string query, Callback<List<int>> callback =
null)
346 TokenizeRequest tokenizeRequest =
new TokenizeRequest();
347 tokenizeRequest.content = query;
348 string json = JsonUtility.ToJson(tokenizeRequest);
349 return await PostRequest<TokenizeResult, List<int>>(json,
"tokenize", TokenizeContent, callback);
358 public virtual async Task<string>
Detokenize(List<int> tokens, Callback<string> callback =
null)
361 TokenizeResult tokenizeRequest =
new TokenizeResult();
362 tokenizeRequest.tokens = tokens;
363 string json = JsonUtility.ToJson(tokenizeRequest);
364 return await PostRequest<TokenizeRequest, string>(json,
"detokenize", DetokenizeContent, callback);
373 public virtual async Task<List<float>>
Embeddings(
string query, Callback<List<float>> callback =
null)
376 TokenizeRequest tokenizeRequest =
new TokenizeRequest();
377 tokenizeRequest.content = query;
378 string json = JsonUtility.ToJson(tokenizeRequest);
379 return await PostRequest<EmbeddingsResult, List<float>>(json,
"embeddings", EmbeddingsContent, callback);