kotoshu-server 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +26 -4
- data/lib/kotoshu/server/app.rb +106 -3
- data/lib/kotoshu/server/version.rb +1 -1
- data/openapi.yaml +7 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a1120bdda9dcd7ecc468c95d5386127efefa6509e7aa1a03f2fa12a5e28c33f
|
|
4
|
+
data.tar.gz: b8cbe5d0db18715009576fd104be80bb06e35ba1b730cbd2564178755a05e6a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c001de9ec24f78e48eac1c400b0ecf9038b47fdeed7115ad3ed0635e100adc7b3ae9e241a7474c8956636f1690f3dea39b425c125d122577aa67dfd097af2a4
|
|
7
|
+
data.tar.gz: 8696ca46e3728fbd2b2f32b3e9468be4e544290d83badb6837b2133bb76700fd7241c687446b7926e9d0a485eb95fbae22d44183503da64cb84cada4227080d7
|
data/README.md
CHANGED
|
@@ -12,9 +12,12 @@ See `TODO.impl/64-http-api-and-sdks.md` for the full plan.
|
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
empty
|
|
17
|
-
|
|
15
|
+
```bash
|
|
16
|
+
gem install kotoshu-server # >= 0.1.1 (the 0.1.0 gem was empty — a gemspec file-list bug)
|
|
17
|
+
kotoshu-server
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or run from source:
|
|
18
21
|
|
|
19
22
|
```bash
|
|
20
23
|
cd kotoshu-server && bundle install && bundle exec exe/kotoshu-server
|
|
@@ -71,6 +74,24 @@ the default fluency tier (the full tier is far larger), plus
|
|
|
71
74
|
one-time model load on the first model-enabled request. Listing the
|
|
72
75
|
language in `KOTOSHU_SERVER_MODEL_LANGS` warms it at boot instead.
|
|
73
76
|
|
|
77
|
+
## Language detection
|
|
78
|
+
|
|
79
|
+
`POST /v1/detect` reports which engine answered in the `engine`
|
|
80
|
+
field:
|
|
81
|
+
|
|
82
|
+
- `"lid-176"` — the 176-language lid.176 model served through the
|
|
83
|
+
kotoshu native extension (kotoshu >= 0.10.0). The model is set up
|
|
84
|
+
lazily on the first detect — one download, never at boot, and
|
|
85
|
+
never at all under `KOTOSHU_OFFLINE=1` without a cache (Docker
|
|
86
|
+
defaults to offline).
|
|
87
|
+
- `"heuristic"` — the 7-language character-set heuristic (en, de,
|
|
88
|
+
es, fr, pt, ru, ja), the fallback whenever lid-176 cannot serve:
|
|
89
|
+
kotoshu < 0.10.0, a pure-Ruby install or `KOTOSHU_BACKEND=ruby`,
|
|
90
|
+
or the model missing after a failed setup.
|
|
91
|
+
|
|
92
|
+
Set `KOTOSHU_DETECT=heuristic` to pin the heuristic regardless of
|
|
93
|
+
the installed gem — the 0.1.1 behavior, byte for byte.
|
|
94
|
+
|
|
74
95
|
## Endpoints
|
|
75
96
|
|
|
76
97
|
| Method | Path | Body | Returns |
|
|
@@ -81,7 +102,7 @@ language in `KOTOSHU_SERVER_MODEL_LANGS` warms it at boot instead.
|
|
|
81
102
|
| `GET` | `/v1/languages` | — | `{ cached, supported, model }` |
|
|
82
103
|
| `POST` | `/v1/check` | `{ text, language?, format?, model? }` | `{ file, word_count, errors: [...] }` |
|
|
83
104
|
| `POST` | `/v1/suggest` | `{ word, language?, max? }` | `{ word, suggestions: [...] }` |
|
|
84
|
-
| `POST` | `/v1/detect` | `{ text }` | `{ language, confidence }` |
|
|
105
|
+
| `POST` | `/v1/detect` | `{ text }` | `{ language, confidence, engine }` |
|
|
85
106
|
|
|
86
107
|
Each `error` and `suggestion` mirrors the lutaml-model serialization
|
|
87
108
|
(`word`, `distance`, `confidence`, `source`).
|
|
@@ -129,6 +150,7 @@ Healthcheck probes `/v1/health` every 30s.
|
|
|
129
150
|
| `KOTOSHU_SERVER_LAZY` | `0` | Skip pre-warm; load on first request |
|
|
130
151
|
| `KOTOSHU_SERVER_DEFAULT_LANG` | `en` | When client omits `language` |
|
|
131
152
|
| `KOTOSHU_SERVER_LOG_LEVEL` | `info` | `debug`/`info`/`warn`/`error` |
|
|
153
|
+
| `KOTOSHU_DETECT` | `auto` | `heuristic` pins /v1/detect to the heuristic engine |
|
|
132
154
|
| `KOTOSHU_OFFLINE` | `1` (in Docker) | Never trigger downloads |
|
|
133
155
|
|
|
134
156
|
## OpenAPI
|
data/lib/kotoshu/server/app.rb
CHANGED
|
@@ -12,7 +12,9 @@ module Kotoshu
|
|
|
12
12
|
class ModelConfigError < StandardError; end
|
|
13
13
|
|
|
14
14
|
class App < Sinatra::Base
|
|
15
|
-
|
|
15
|
+
# The release version, from lib/kotoshu/server/version.rb —
|
|
16
|
+
# the only source of truth (the release workflow bumps it).
|
|
17
|
+
VERSION = Kotoshu::Server::VERSION
|
|
16
18
|
|
|
17
19
|
# Semantic models (tiers, registry, confidence cascade) ship in
|
|
18
20
|
# kotoshu 0.7.0. The gemspec keeps its `kotoshu ~> 0.6`
|
|
@@ -20,6 +22,14 @@ module Kotoshu
|
|
|
20
22
|
# server validates the *installed* gem at boot instead.
|
|
21
23
|
MODEL_MIN_KOTOSHU = Gem::Version.new("0.7.0")
|
|
22
24
|
|
|
25
|
+
# Native language identification (plan 106) ships in kotoshu
|
|
26
|
+
# 0.10.0: Kotoshu.detect_language returning a
|
|
27
|
+
# Language::Detection backed by the lid-176 model through the
|
|
28
|
+
# native extension. Same policy as MODEL_MIN_KOTOSHU — the
|
|
29
|
+
# gemspec floor stays `kotoshu ~> 0.6` (older gems keep the
|
|
30
|
+
# heuristic), so the installed gem is checked per request.
|
|
31
|
+
DETECT_MIN_KOTOSHU = Gem::Version.new("0.10.0")
|
|
32
|
+
|
|
23
33
|
# Languages to set up at boot, from KOTOSHU_SERVER_LANGUAGES
|
|
24
34
|
# (space separated). Single source of truth for the env var: the
|
|
25
35
|
# boot pre-warm and /v1/health both read it here.
|
|
@@ -148,6 +158,99 @@ module Kotoshu
|
|
|
148
158
|
end
|
|
149
159
|
end
|
|
150
160
|
|
|
161
|
+
# ---- Language detection (/v1/detect) ----
|
|
162
|
+
|
|
163
|
+
@lid_setup_mutex = Mutex.new
|
|
164
|
+
@lid_setup_attempted = false
|
|
165
|
+
|
|
166
|
+
# Whether the installed kotoshu gem carries the lid-176
|
|
167
|
+
# detection surface (0.10.0+): Kotoshu.detect_language returning
|
|
168
|
+
# a Language::Detection, plus setup_lid / LidDetector.available?.
|
|
169
|
+
#
|
|
170
|
+
# @return [Boolean]
|
|
171
|
+
def self.lid_supported?
|
|
172
|
+
Gem::Version.new(Kotoshu::VERSION) >= DETECT_MIN_KOTOSHU
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Whether KOTOSHU_DETECT=heuristic pins /v1/detect to the
|
|
176
|
+
# 7-language heuristic, bypassing the gem engine selection (the
|
|
177
|
+
# 0.1.1 behavior, e.g. to reproduce earlier results).
|
|
178
|
+
#
|
|
179
|
+
# @return [Boolean]
|
|
180
|
+
def self.heuristic_detect?
|
|
181
|
+
ENV.fetch("KOTOSHU_DETECT", nil) == "heuristic"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Detect for /v1/detect: the language code, its score, and the
|
|
185
|
+
# engine that served — "lid-176" or "heuristic".
|
|
186
|
+
#
|
|
187
|
+
# Engine selection, in order:
|
|
188
|
+
# 1. KOTOSHU_DETECT=heuristic — the heuristic, pinned.
|
|
189
|
+
# 2. kotoshu < 0.10.0 — the heuristic, as in 0.1.1; the server
|
|
190
|
+
# keeps working on old gems.
|
|
191
|
+
# 3. Otherwise the gem's own selection: lid-176 through the
|
|
192
|
+
# native extension when it is built, the backend is not
|
|
193
|
+
# explicitly ruby, and the artifact pair is cached; the
|
|
194
|
+
# heuristic otherwise (KOTOSHU_BACKEND=ruby, pure-Ruby
|
|
195
|
+
# install, missing model).
|
|
196
|
+
#
|
|
197
|
+
# The lid model is set up lazily on the first detect reaching
|
|
198
|
+
# case 3 — one download, off the boot path, honoring
|
|
199
|
+
# KOTOSHU_OFFLINE through the gem; a failed setup (offline with
|
|
200
|
+
# no cache, no registry entry, checksum mismatch) logs once and
|
|
201
|
+
# detection answers from whatever the gem can serve.
|
|
202
|
+
#
|
|
203
|
+
# @param text [String] the text to analyze
|
|
204
|
+
# @return [Array(String, Float, String)] code (nil when the
|
|
205
|
+
# heuristic is uncertain), score in [0, 1], engine name
|
|
206
|
+
def self.detect_language_with_engine(text)
|
|
207
|
+
return heuristic_detection(text) if heuristic_detect? || !lid_supported?
|
|
208
|
+
|
|
209
|
+
ensure_lid_setup!
|
|
210
|
+
detection = Kotoshu.detect_language(text)
|
|
211
|
+
[detection.code, detection.score, lid_engine]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# The heuristic detection (Language::Detector, the same engine
|
|
215
|
+
# /v1/detect served in 0.1.1, unchanged across gem versions).
|
|
216
|
+
#
|
|
217
|
+
# @param text [String]
|
|
218
|
+
# @return [Array(String, Float, String)]
|
|
219
|
+
def self.heuristic_detection(text)
|
|
220
|
+
code, confidence = Kotoshu::Language::Detector.detect_with_confidence(text)
|
|
221
|
+
[code, confidence, "heuristic"]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Which engine the gem serves detect from: lid-176 when the
|
|
225
|
+
# native model is loadable, the heuristic otherwise. Asked
|
|
226
|
+
# after a detect, so it always names the engine behind the
|
|
227
|
+
# returned code.
|
|
228
|
+
#
|
|
229
|
+
# @return [String]
|
|
230
|
+
def self.lid_engine
|
|
231
|
+
Kotoshu::Language::LidDetector.available? ? "lid-176" : "heuristic"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Run Kotoshu.setup_lid once per process, on the first detect.
|
|
235
|
+
# Idempotent in the gem; the once-guard keeps concurrent
|
|
236
|
+
# requests from racing the download. Any failure is logged and
|
|
237
|
+
# swallowed — detect then falls back inside the gem to whatever
|
|
238
|
+
# is cached (typically the heuristic).
|
|
239
|
+
#
|
|
240
|
+
# @return [void]
|
|
241
|
+
def self.ensure_lid_setup!
|
|
242
|
+
@lid_setup_mutex.synchronize do
|
|
243
|
+
return if @lid_setup_attempted
|
|
244
|
+
|
|
245
|
+
@lid_setup_attempted = true
|
|
246
|
+
Kotoshu.setup_lid
|
|
247
|
+
end
|
|
248
|
+
rescue StandardError => e
|
|
249
|
+
Logger.new($stderr).warn(
|
|
250
|
+
"lid model setup failed, falling back to the heuristic: #{e.class}: #{e.message}"
|
|
251
|
+
)
|
|
252
|
+
end
|
|
253
|
+
|
|
151
254
|
# ---- Semantic analyzers (memoized per language + model file) ----
|
|
152
255
|
|
|
153
256
|
@semantic_analyzers = {}
|
|
@@ -265,9 +368,9 @@ module Kotoshu
|
|
|
265
368
|
text = body["text"]
|
|
266
369
|
halt_with_error(400, "missing 'text'") unless text.is_a?(String)
|
|
267
370
|
|
|
268
|
-
|
|
371
|
+
language, confidence, engine = self.class.detect_language_with_engine(text)
|
|
269
372
|
content_type :json
|
|
270
|
-
{ language:
|
|
373
|
+
{ language: language, confidence: confidence, engine: engine }.to_json
|
|
271
374
|
end
|
|
272
375
|
|
|
273
376
|
# ---- Error handling ----
|
data/openapi.yaml
CHANGED
|
@@ -236,6 +236,13 @@ components:
|
|
|
236
236
|
properties:
|
|
237
237
|
language: { type: string, nullable: true }
|
|
238
238
|
confidence: { type: number, format: float }
|
|
239
|
+
engine:
|
|
240
|
+
type: string
|
|
241
|
+
enum: [lid-176, heuristic]
|
|
242
|
+
description: >
|
|
243
|
+
Which engine served the detection: the 176-language lid-176
|
|
244
|
+
model (kotoshu >= 0.10.0 with the native extension and the
|
|
245
|
+
model set up) or the 7-language heuristic.
|
|
239
246
|
Error:
|
|
240
247
|
type: object
|
|
241
248
|
properties:
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kotoshu-server
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: kotoshu
|