diff --git a/README.md b/README.md
index 659bbd1..9935f5d 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,8 @@ Current supported plugins include:
- Probably the most popular LLM that has the highest quality but is not Free
- [Ollama Models](https://ollama.com/)
- Ollama is a local hosting option for LLMs. You are able to run one or more llms on your local machine of varying size. This option is free but will require you to engineer your prompts dependant on the model and/or language.
+- [LM Studio](https://lmstudio.ai/)
+ - LM Studio is a local hosting option for OpenAI-compatible chat completion APIs. Start the local server in LM Studio and use the model id shown by LM Studio in `LmStudio.yaml`.
# Why use this instead of the [Custom] endpoint?
@@ -33,6 +35,7 @@ To configure your LLM you will need to follow the following steps:
2. Open the config for the LLMTranslator you wish to use
- If OpenaI: `OpenAi.Yml`
- If a local Olama LLM: `Ollama.Yml`
+ - If a local LM Studio LLM: `LmStudio.Yml`
3. Update your config with any API keys, custom urls, glossaries and system prompts.
4. Finally update your AutoTranslator INI file with your translate service
- ```
@@ -42,6 +45,32 @@ To configure your LLM you will need to follow the following steps:
```
- If OpenAi: `OpenAiTranslate`
- If a local Olama LLM: `OllamaTranslate`
+ - If a local LM Studio LLM: `LmStudioTranslate`
+
+## LM Studio
+
+To use LM Studio, start the local server in LM Studio and load the model you want to use for translation. The default LM Studio OpenAI-compatible chat completion URL is:
+
+```yaml
+url: "http://localhost:1234/v1/chat/completions"
+```
+
+Copy `LmStudio.yaml` into your AutoTranslator config folder, then update the `model` value to the model identifier shown by LM Studio:
+
+```yaml
+apiKey: "None"
+apiKeyRequired: false
+url: "http://localhost:1234/v1/chat/completions"
+model: "model-identifier"
+```
+
+Finally, set the translator endpoint in `Config.ini`:
+
+```ini
+[Service]
+Endpoint=LmStudioTranslate
+FallbackEndpoint=
+```
## Global API Key
@@ -55,11 +84,11 @@ We also use global environment variables so you can just set your API Key once a
We have seperate files that can be override any config you have loaded in your config file. This makes it easier to publish game specific prompts, glossaries or just make it easier to use multi line prompts without having to worry about YAML formatting.
These files are:
- - `OpenAi-SystemPrompt.txt` or `Ollama-SystemPrompt.txt`
+ - `OpenAi-SystemPrompt.txt`, `Ollama-SystemPrompt.txt` or `LmStudio-SystemPrompt.txt`
- Use this file to update your system prompt
- - `OpenAi-GlossaryPrompt.txt` or `Ollama-GlossaryPrompt.txt`
+ - `OpenAi-GlossaryPrompt.txt`, `Ollama-GlossaryPrompt.txt` or `LmStudio-GlossaryPrompt.txt`
- Use this file to update your glossary prompt
- - `OpenAi-ApiKey.txt` or `Ollama-ApiKey.txt`
+ - `OpenAi-ApiKey.txt`, `Ollama-ApiKey.txt` or `LmStudio-ApiKey.txt`
- Use this file to update your API Key
# Glossary
@@ -98,4 +127,4 @@ A test project is included with the project. The [PromptTests](./XUnity.AutoTran
# Packages
-The assemblies included are the Dev versions of XUnity.AutoTranslator. Feel free to star/fork this repo however you like.
\ No newline at end of file
+The assemblies included are the Dev versions of XUnity.AutoTranslator. Feel free to star/fork this repo however you like.
diff --git a/XUnity.AutoTranslator.LlmTranslators.Tests/ConfigTests.cs b/XUnity.AutoTranslator.LlmTranslators.Tests/ConfigTests.cs
index 11ea2c4..b824552 100644
--- a/XUnity.AutoTranslator.LlmTranslators.Tests/ConfigTests.cs
+++ b/XUnity.AutoTranslator.LlmTranslators.Tests/ConfigTests.cs
@@ -14,4 +14,36 @@ public void TestDefaultConfig()
Assert.True(config.SystemPrompt!.Split("\n").Length > 1);
}
+
+ [Fact]
+ public void TestLmStudioConfig()
+ {
+ var config = Configuration.GetConfiguration($"{sampleDirectory}/LmStudio.yaml");
+
+ Assert.Equal("http://localhost:1234/v1/chat/completions", config.Url);
+ Assert.Equal("model-identifier", config.Model);
+ Assert.False(config.ApiKeyRequired);
+ Assert.True(config.SystemPrompt!.Split("\n").Length > 1);
+ }
+
+ [Fact]
+ public void TestGlossaryPromptOverride()
+ {
+ var config = new LlmConfig { SystemPrompt = "System prompt", GlossaryPrompt = "Glossary prompt" };
+ var file = Path.GetTempFileName();
+
+ try
+ {
+ File.WriteAllText(file, "Override glossary prompt");
+
+ Configuration.LoadGlossaryPrompt(config, file);
+
+ Assert.Equal("System prompt", config.SystemPrompt);
+ Assert.Equal("Override glossary prompt", config.GlossaryPrompt);
+ }
+ finally
+ {
+ File.Delete(file);
+ }
+ }
}
diff --git a/XUnity.AutoTranslator.LlmTranslators/Config/Configuration.cs b/XUnity.AutoTranslator.LlmTranslators/Config/Configuration.cs
index 63d08ec..9109123 100644
--- a/XUnity.AutoTranslator.LlmTranslators/Config/Configuration.cs
+++ b/XUnity.AutoTranslator.LlmTranslators/Config/Configuration.cs
@@ -88,7 +88,7 @@ public static void LoadGlossaryPrompt(LlmConfig config, string file)
if (!File.Exists(file))
return;
- config.SystemPrompt = File.ReadAllText(file, Encoding.UTF8);
+ config.GlossaryPrompt = File.ReadAllText(file, Encoding.UTF8);
}
public static void LoadApiKey(LlmConfig config, string file)
diff --git a/XUnity.AutoTranslator.LlmTranslators/ILRepack.targets b/XUnity.AutoTranslator.LlmTranslators/ILRepack.targets
new file mode 100644
index 0000000..0334c03
--- /dev/null
+++ b/XUnity.AutoTranslator.LlmTranslators/ILRepack.targets
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XUnity.AutoTranslator.LlmTranslators/LmStudioTranslatorEndpoint.cs b/XUnity.AutoTranslator.LlmTranslators/LmStudioTranslatorEndpoint.cs
new file mode 100644
index 0000000..c65ead7
--- /dev/null
+++ b/XUnity.AutoTranslator.LlmTranslators/LmStudioTranslatorEndpoint.cs
@@ -0,0 +1,59 @@
+using SimpleJSON;
+using System.Net;
+using XUnity.AutoTranslator.LlmTranslators.Behavior;
+using XUnity.AutoTranslator.LlmTranslators.Config;
+using XUnity.AutoTranslator.Plugin.Core.Endpoints;
+using XUnity.AutoTranslator.Plugin.Core.Endpoints.Http;
+using XUnity.AutoTranslator.Plugin.Core.Web;
+
+public class LmStudioTranslatorEndpoint : HttpEndpoint
+{
+ public override string Id => "LmStudioTranslate";
+ public override string FriendlyName => "LM Studio Translate";
+ public override int MaxTranslationsPerRequest => 1;
+
+ // Local models can saturate quickly; keep this closer to Ollama's default.
+ public override int MaxConcurrency => 5;
+
+ private LlmConfig _config = new();
+
+ public override void Initialize(IInitializationContext context)
+ {
+ string folder = Configuration.CalculateConfigFolder();
+ var file = Path.Combine(folder, "LmStudio.yaml");
+ _config = Configuration.GetConfiguration(file);
+ Configuration.LoadGlossary(_config, "LmStudio-Glossary.yaml");
+
+ // Remove artificial delays
+ context.SetTranslationDelay(0.1f);
+ context.DisableSpamChecks();
+
+ if (string.IsNullOrEmpty(_config.ApiKey) && _config.ApiKeyRequired)
+ throw new Exception("The endpoint requires an API key which has not been provided.");
+ }
+
+ public override void OnCreateRequest(IHttpRequestCreationContext context)
+ {
+ var requestData = BaseEndpointBehavior.GetRequestData(_config, context.UntranslatedText);
+
+ var request = new XUnityWebRequest("POST", _config.Url, requestData);
+ request.Headers[HttpRequestHeader.ContentType] = "application/json";
+
+ if (_config.ApiKeyRequired)
+ request.Headers[HttpRequestHeader.Authorization] = $"Bearer {_config.ApiKey}";
+
+ context.Complete(request);
+ }
+
+ public override void OnExtractTranslation(IHttpTranslationExtractionContext context)
+ {
+ var data = context.Response.Data;
+
+ var jsonResponse = JSON.Parse(data);
+ var result = jsonResponse["choices"]?[0]?["message"]?["content"]?.ToString() ?? string.Empty;
+ result = BaseEndpointBehavior.ValidateAndCleanupTranslation(context.UntranslatedText, result, _config);
+
+ if (MaxTranslationsPerRequest == 1)
+ context.Complete(result);
+ }
+}
diff --git a/XUnity.AutoTranslator.LlmTranslators/SampleConfig/LmStudio.yaml b/XUnity.AutoTranslator.LlmTranslators/SampleConfig/LmStudio.yaml
new file mode 100644
index 0000000..3772b37
--- /dev/null
+++ b/XUnity.AutoTranslator.LlmTranslators/SampleConfig/LmStudio.yaml
@@ -0,0 +1,58 @@
+apiKey: "None"
+apiKeyRequired: false
+url: "http://localhost:1234/v1/chat/completions"
+model: "model-identifier"
+modelParams:
+ temperature: 0.2
+ max_tokens: 4096
+ top_p: 0.9
+ top_k: 40
+ frequency_penalty: 0
+ presence_penalty: 0
+systemPrompt: |
+ Your task is to accurately translate Chinese strings provided into fluent English, maintaining the original tone and meaning without adding explanations or embellishments.
+
+ #### Guidelines:
+
+ **Output**
+ - Do not include additional HTML or markdown.
+
+ **Gender-Neutral Language:**
+ - Default to gender-neutral language and pronouns ("they/them"), unless specified by the text.
+
+ **Character References:**
+ - Refer to characters by their names or roles, avoiding gendered descriptors.
+ - Use gender-specific terms only if clearly identified in the text or necessary for clarity.
+
+ **Names:**
+ - Use Pinyin for Chinese names.
+
+ **Nicknames:**
+ - Translate into culturally appropriate English equivalents, maintaining meaning and sentiment.
+
+ **Title Conversion:**
+ - Convert titles such as "Doctor," "Lord," or "Master" to their English equivalents.
+ - Precede names with these titles (e.g., "Brother Li").
+ - Use glossary terms for titles as provided, with flexible capitalization but no changes to wording based on context
+ - Preserve the intended meaning and connotations of each title.
+
+ **Cultural Sensitivity:**
+ - Incorporate relevant Wuxia or traditional terms as needed.
+
+ **Idioms:**
+ - Convey the meaning, cultural context, and implications in English.
+ - Avoid direct transliteration; use literal translations within the text to maintain intended message and tone.
+
+ **Contextual Adaptations:**
+ - Resolve ambiguities using broader context for clarity.
+ - Select translations fitting the context and guidelines.
+
+ **Language Refinement:**
+ - Ensure clarity and accuracy with minimal grammatical changes.
+
+ **Capitalization:**
+ - Follow standard English grammar rules.
+glossaryPrompt: |
+ #Glossary for Consistent Translations
+ Prioritise and use the translation when an exact match with the original text is found.
+ ## Terms
diff --git a/XUnity.AutoTranslator.LlmTranslators/XUnity.AutoTranslator.LlmTranslators.csproj b/XUnity.AutoTranslator.LlmTranslators/XUnity.AutoTranslator.LlmTranslators.csproj
index 7f0a612..6404ccf 100644
--- a/XUnity.AutoTranslator.LlmTranslators/XUnity.AutoTranslator.LlmTranslators.csproj
+++ b/XUnity.AutoTranslator.LlmTranslators/XUnity.AutoTranslator.LlmTranslators.csproj
@@ -9,6 +9,7 @@
G:\SteamLibrary\steamapps\common\下一站江湖Ⅱ\下一站江湖Ⅱ\下一站江湖Ⅱ_Data\Managed\Translators
C:\Program Files (x86)\Steam\steamapps\common\LegendOfMortal\Mortal_Data\Managed\Translators
\..\..\..\
+ false
Debug;Release
f11951a3-bacd-43f2-a9fa-0187cf947674
@@ -30,31 +31,28 @@
-
+
-
+
- $(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework)
+ $(MSBuildThisFileDirectory)..\Release\
+ $(ReleaseFolder)Translators\
+ $(ReleaseFolder)SampleConfig\
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
+
+
+
+