Skip to content

Commit f883fee

Browse files
committed
translator: openai refactor and docs update
1 parent 00d86f9 commit f883fee

23 files changed

Lines changed: 375 additions & 226 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ imgui-winit-glow-renderer-viewports = { git = "https://github.com/Netdex/imgui-r
1212
# hudhook = { git = "https://github.com/veeenu/hudhook.git" }
1313

1414
[workspace]
15-
members = ["niinii", "ichiran", "openai-chat"]
15+
members = ["niinii", "ichiran", "openai"]
1616
exclude = ["third-party"]
1717
resolver = "2"
1818

README.md

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,33 @@
55

66
[Demonstration](https://www.youtube.com/watch?v=Ap2jXEkhURM)
77

8-
niinii (knee-knee) is a graphical frontend for glossing Japanese text. Useful
9-
for assisted reading of text for language learning purposes. A primary use case
10-
is glossing visual novels, which is shown in the demonstration above. I made
11-
this tool with the express intent to read a single *specific* visual novel,
12-
which is also where the name comes from. If someone else finds it useful that's
13-
cool too.
8+
niinii (knee-knee) is a graphical frontend for glossing and translating Japanese
9+
text. Useful for assisted reading of text for language learning purposes. A
10+
primary use case is glossing visual novels, which is shown in the demonstration
11+
above. I made this tool with the express intent to read a single *specific*
12+
visual novel, which is also where the name comes from. If someone else finds it
13+
useful that's cool too.
1414

1515
For example, in the demonstration above, I use niinii along with a text hooker
1616
to gloss the dialogue in a visual novel. The segmented phrase along with ruby
1717
text (i.e. furigana) is displayed. Hovering over a segment will show dictionary
1818
definitions and inflections from JMDict. You can pop open a separate window by
1919
clicking on a segment. Hovering over kanji will show kanji information from
20-
KANJIDIC2. I would write a more detailed user manual but I think you can
21-
probably figure it out.
20+
KANJIDIC2. Behold the LLM-based live translation. I would write a more detailed
21+
user manual but I think you can probably figure it out.
2222

23-
Japanese language support is implemented using
23+
Segmentation is implemented using
2424
[Ichiran](https://github.com/tshatrov/ichiran) by
2525
[tshatrov](https://github.com/tshatrov). Ichiran is pretty amazing at text
2626
segmentation compared to other tools I've tried.
2727

28+
As of late, niinii incorporates translation features via LLM offerings from
29+
OpenAI. Since I started working on this project, the state of LLM-based
30+
translation has drastically improved both in quality and latency. I won't
31+
comment on the state of the art here since it changes on a monthly cadence.
32+
Again, I would write a more detailed user manual but you'll figure it out.
33+
Bring your own API key.
34+
2835
## Why not use...
2936
This is a tool created to service a personal need, and may not be useful to you.
3037
Below, I laid out my personal justification for investing time into creating
@@ -36,8 +43,10 @@ much better at segmentation, provides more metadata, and makes fewer mistakes.
3643
**Why not use rikai(kun|chan), JGlossator?**: They don't do segmentation.
3744

3845
**Why not use DeepL, Google Translate, etc.?**: I want a gloss, not a
39-
translation tool. If I ever integrate translation features, I'd like to do so in
40-
a way that supplements the gloss rather than dumping text.
46+
translation tool. ~~If I ever integrate translation features, I'd like to do so in
47+
a way that supplements the gloss rather than dumping text.~~ I have finally
48+
incorporated translation features into niinii. Of course, the purpose here is
49+
still to supplement the gloss.
4150

4251
**Why not use the web frontend [ichi.moe](https://ichi.moe)?**:
4352
There are some features I'd like to experiment with to improve the glossing
@@ -46,7 +55,7 @@ experience.
4655
## Build
4756
Prepackaged builds are available in the
4857
[Releases](https://github.com/Netdex/niinii/releases) section of this
49-
repository.
58+
repository. These releases are seldom published and may be missing many unstable features. You may download bleeding-edge builds from [GitHub Actions](https://github.com/Netdex/niinii/actions).
5059

5160
The only target that is properly maintained is `x86_64-pc-windows-msvc`. There's
5261
nothing stopping it from working on other targets (e.g. Linux), but additional

niinii/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ nu-ansi-term = "0.50.1"
7171

7272
# interned
7373
ichiran = { path = "../ichiran" }
74-
openai-chat = { path = "../openai-chat" }
74+
openai = { path = "../openai" }
7575

7676
[target.'cfg(windows)'.dependencies]
7777
imgui-dx11-renderer = { path = "../third-party/imgui-dx11-renderer" }

niinii/src/app.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ impl App {
7676
let parser = Parser::new(&settings).await;
7777
let translator: Arc<dyn Translator> = match settings.translator_type {
7878
TranslatorType::DeepL => Arc::new(DeepLTranslator),
79-
TranslatorType::Chat => Arc::new(ChatTranslator::new(&settings)),
80-
TranslatorType::Realtime => Arc::new(RealtimeTranslator::new(&settings)),
79+
TranslatorType::Chat => Arc::new(ChatTranslator::new(&settings).await),
80+
TranslatorType::Realtime => Arc::new(RealtimeTranslator::new(&settings).await),
8181
};
8282
let tts = TtsEngine::new(&settings);
8383
App {
@@ -213,22 +213,25 @@ impl App {
213213
}
214214
}
215215

216-
if let State::Completed = &self.state {
217-
if let Some(request_gloss_text) = self.request_gloss_text.clone() {
218-
self.request_gloss_text = None;
219-
self.request_parse(ui, &request_gloss_text);
220-
}
221-
};
222-
223216
if self.settings.watch_clipboard {
224217
if let Some(clipboard) = ui.clipboard_text() {
225218
if clipboard != self.last_clipboard {
226219
self.input_text.clone_from(&clipboard);
227220
self.last_clipboard.clone_from(&clipboard);
228-
self.request_gloss_text = Some(clipboard);
221+
// Ignore clipboard contents if they are unreasonably large
222+
if clipboard.len() < 1000 {
223+
self.request_gloss_text = Some(clipboard);
224+
}
229225
}
230226
}
231227
}
228+
229+
if let State::Completed = &self.state {
230+
if let Some(request_gloss_text) = self.request_gloss_text.clone() {
231+
self.request_gloss_text = None;
232+
self.request_parse(ui, &request_gloss_text);
233+
}
234+
};
232235
}
233236

234237
fn show_menu(&mut self, ctx: &mut Context, ui: &Ui) {
@@ -267,8 +270,6 @@ impl App {
267270
ui.separator();
268271
let _disable_state = ui.begin_disabled(matches!(self.state, State::Processing));
269272
{
270-
let mut _disable_tl =
271-
ui.begin_disabled(self.gloss.ast().is_none_or(|ast| ast.empty()));
272273
if ui.menu_item("Translate") {
273274
if let Some(gloss) = self.gloss.ast() {
274275
self.request_translation(ui, gloss.original_text.clone());

niinii/src/parser.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@ use thiserror::Error;
55

66
use crate::settings::Settings;
77

8-
const MAX_TEXT_LENGTH: usize = 512;
9-
108
#[derive(Error, Debug)]
119
pub enum Error {
1210
#[error(transparent)]
1311
Ichiran(#[from] IchiranError),
14-
#[error("Text too long ({length}/{MAX_TEXT_LENGTH} chars)")]
15-
TextTooLong { length: usize },
1612
#[error(transparent)]
1713
RegexError(#[from] fancy_regex::Error),
1814
}
@@ -64,9 +60,6 @@ impl Parser {
6460
}
6561
}
6662
pub async fn parse(&self, text: &str, variants: u32) -> Result<SyntaxTree, Error> {
67-
if text.len() > MAX_TEXT_LENGTH {
68-
return Err(Error::TextTooLong { length: text.len() });
69-
}
7063
let ichiran = &self.shared.ichiran;
7164

7265
let (root, kanji_info, jmdict_data) = tokio::try_join!(

niinii/src/settings.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,32 @@ use num_derive::FromPrimitive;
22
use serde::{Deserialize, Serialize};
33
use strum_macros::{EnumIter, IntoStaticStr};
44

5-
#[derive(Debug, Clone, Copy, Deserialize, Serialize, FromPrimitive, IntoStaticStr, EnumIter)]
5+
#[derive(
6+
Debug,
7+
Clone,
8+
Copy,
9+
PartialEq,
10+
Eq,
11+
Deserialize,
12+
Serialize,
13+
FromPrimitive,
14+
IntoStaticStr,
15+
EnumIter,
16+
)]
617
pub enum RendererType {
718
Glow,
819
#[cfg(windows)]
920
Direct3D11,
1021
}
1122

12-
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, IntoStaticStr, EnumIter)]
23+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, IntoStaticStr, EnumIter)]
1324
pub enum RubyTextType {
1425
None,
1526
Furigana,
1627
Romaji,
1728
}
1829

19-
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, IntoStaticStr, EnumIter)]
30+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, IntoStaticStr, EnumIter)]
2031
pub enum TranslatorType {
2132
DeepL,
2233
Chat,
@@ -27,7 +38,7 @@ pub enum TranslatorType {
2738
#[serde(default)]
2839
pub struct ChatSettings {
2940
pub api_endpoint: String,
30-
pub model: openai_chat::chat::Model,
41+
pub model: openai::ModelId,
3142
pub system_prompt: String,
3243
pub max_context_tokens: u32,
3344
pub temperature: Option<f32>,
@@ -37,6 +48,9 @@ pub struct ChatSettings {
3748
pub connection_timeout: u64,
3849
pub timeout: u64,
3950
pub stream: bool,
51+
pub service_tier: Option<openai::chat::ServiceTier>,
52+
pub reasoning_effort: Option<openai::chat::ReasoningEffort>,
53+
pub verbosity: Option<openai::chat::Verbosity>,
4054
}
4155
impl Default for ChatSettings {
4256
fn default() -> Self {
@@ -53,14 +67,17 @@ impl Default for ChatSettings {
5367
connection_timeout: 3000,
5468
timeout: 10000,
5569
stream: true,
70+
service_tier: Some(openai::chat::ServiceTier::Priority),
71+
reasoning_effort: None,
72+
verbosity: None,
5673
}
5774
}
5875
}
5976

6077
#[derive(Clone, Deserialize, Serialize)]
6178
#[serde(default)]
6279
pub struct RealtimeSettings {
63-
pub model: openai_chat::realtime::Model,
80+
pub model: openai::ModelId,
6481
pub system_prompt: String,
6582
pub temperature: Option<f32>,
6683
}

0 commit comments

Comments
 (0)