Skip to content

Latest commit

 

History

History
404 lines (304 loc) · 15.7 KB

File metadata and controls

404 lines (304 loc) · 15.7 KB

07 · HTTP Server

HybridTM can run as a local JSON-over-HTTP server, for long-lived integrations (editor plugins, CAT tools) that want to keep an instance, and its loaded embedding model, open across many requests instead of paying the load cost on every invocation. It exposes the same HybridTM/HybridTMFactory operations documented in the earlier guides.

Starting and stopping

hybridtm serve [-port <number>] [-network]
hybridtm stop [-port <number>]

serve spawns a detached background process listening on port 8050 by default; pass -port to use a different one. By default it binds 127.0.0.1 only, so it isn't reachable from other machines; pass -network to change that (there's no per-command authentication, so only do this on a trusted network). stop sends it a shutdown request; pass the same -port you started it with if it wasn't the default.

Embedding the server in your own application

HybridTMServer is also exported from the package, for running the same server directly inside a Node.js process instead of launching it via the CLI:

import { HybridTMServer } from 'hybridtm';

const server = new HybridTMServer(8050);              // binds 127.0.0.1 only
// const server = new HybridTMServer(8050, '0.0.0.0'); // reachable from other machines

await server.start();

// ... your application runs; anything that can reach this process
// (including code running in the same process) can now POST JSON
// commands to the address/port above

await server.stop();

start()/stop() return Promises that resolve once the HTTP listener is actually up/down. The constructor's first argument is the port; the second is the bind address, defaulting to 127.0.0.1. A few properties worth knowing:

  • It runs in-process: it shares your application's event loop and lifetime, and stops existing once that process exits.
  • You choose the port (and host) via the constructor, so you can run several independent servers side by side, for example one per test, or one per plugin host.
  • It's a plain object, not a singleton. The registry it reads from, HybridTMFactory's instances.json, is shared machine-wide by every HybridTMServer/HybridTM/HybridTMFactory instance. Creating an instance under a given name from one server/process makes it visible to open on any other.

The rest of this guide (the request/response envelope and every command below) describes the server's behavior regardless of how it was started.

Request/response envelope

The server exposes a single endpoint: POST a JSON object with a command field (plus whatever fields that command needs) to http://127.0.0.1:8050, and read a JSON object back.

curl -s -X POST http://127.0.0.1:8050 \
  -H 'Content-Type: application/json' \
  -d '{"command":"list"}'

Every response has one of two shapes:

{ "status": "success", "payload": /* command-specific */ }
{ "status": "failed", "reason": "human-readable message" }

Instance lifecycle: open / close

The server keeps a HybridTM instance (and its loaded embedder) in memory across requests, until it's explicitly closed. Every data command below (import, match, backup, batchTranslate, storeXliffUnit, concordanceSearch, semanticSearch, semanticTranslationSearch, close) requires the instance to already be open. restore requires it too, unless create is used (see below).

open

{ "command": "open", "name": "project" }

Loads the instance via HybridTMFactory.getInstance and keeps it open. Calling open again for an already-open instance is a no-op success. Fails if no instance is registered under that name (run create first).

close

{ "command": "close", "name": "project" }

Closes the instance (flushing LanceDB and disposing the embedder) and drops it from the server's open-instance set. Fails if the instance isn't currently open.

Registry commands

create, remove, and list manage the registered instances and don't require the instance to be open first.

create

{ "command": "create", "name": "project", "path": "./project.lancedb", "model": "large" }
Field Required Description
name yes Name to register the instance under
path yes Directory where the instance's LanceDB data will live; created if missing
model no compact, standard, large (default), or a literal Hugging Face model id

Registers the instance and closes it immediately. create does not add the instance to the server's open-instance set; send open afterward before using it.

remove

{ "command": "remove", "name": "project" }

If the instance is currently open, it is closed first (so its LanceDB directory isn't deleted out from under an open handle), then the registry entry and data directory are permanently deleted.

list

{ "command": "list" }

Payload is HybridTMInstanceMetadata[], the same array HybridTMFactory.listInstances() returns (name, filePath, modelName, createdAt).

Long-running commands: import, match, backup, and restore

Importing a large XLIFF/TMX/SDLTM file (generating an embedding per segment), matching a file against an instance (running a semantic search per segment), backing up an instance, or restoring one can all take a while, so none of these commands block the request until the work finishes. Each starts the operation in the background and returns a ticket immediately; poll status to find out when it's done.

import

{
  "command": "import",
  "name": "project",
  "file": "./translations/project.xlf",
  "type": "xliff",
  "minState": "translated",
  "noMetadata": false
}
Field Required Description
name yes Instance to import into (must already be open)
file yes File to import
type no xliff, tmx, or sdltm; inferred from the file extension when omitted
minState no Minimum segment state to import (see 02 · Importing Data)
noMetadata no true to skip extracting notes/metadata/extension attributes (default: false)

Empty XLIFF targets are skipped automatically, unless the segment's @state is final.

Returns immediately:

{ "status": "success", "payload": { "ticket": "b6e7…" } }

Once the job finishes, status returns { "imported": <count> } as the result (see below).

match

{
  "command": "match",
  "name": "project",
  "file": "./new-content.xlf",
  "output": "./new-content.matches.xlf",
  "quality": 60,
  "limit": 5
}
Field Required Description
name yes Instance to search against (must already be open)
file yes XLIFF file to enrich
quality yes Minimum hybrid match score, 0-100, written to the output's @matchQuality attribute
output no Output path; defaults to <file-without-extension>.matches.xlf next to the input
limit no Max candidates per segment; defaults to semanticTranslationSearch's own default (10) when omitted

Every segment is processed except ones with state="final".

For every segment, it runs semanticTranslationSearch and adds a <mtc:match> (Translation Candidates module, urn:oasis:names:tc:xliff:matches:2.0) entry to that unit's <mtc:matches> block:

<unit id="auth.signin">
  <mtc:matches>
    <mtc:match matchQuality="92" origin="project" ref="#seg1" similarity="85" type="tm">
      <source>Sign in</source>
      <target>Iniciar sesión</target>
    </mtc:match>
  </mtc:matches>
  ...
</unit>

matchQuality is the overall (hybrid) score; similarity here is source-to-source text similarity only. origin is the instance name; ref points at the specific segment the candidate applies to. Inline codes (<ph>, etc.) in the matched source/target are converted to XLIFF's own <ph>/<originalData> representation. Once the job finishes, status's result is { "segmentsProcessed": n, "segmentsWithMatches": n, "totalMatches": n, "outputPath": "…" }.

backup

{ "command": "backup", "name": "project", "file": "./project-backup.xml" }
Field Required Description
name yes Instance to back up (must already be open)
file yes Output XML file path

Once the job finishes, status's result is { "backedUp": <count> }.

restore

{ "command": "restore", "file": "./project-backup.xml", "name": "project" }
{
  "command": "restore",
  "file": "./project-backup.xml",
  "create": true,
  "path": "./project.lancedb",
  "name": "project",
  "model": "large"
}
Field Required Description
file yes Backup XML file to restore
name see below Existing open instance to restore into (appending its entries). With create, the name for the new instance (default: the name recorded in the backup file)
create no true to create a fresh instance before restoring, instead of appending to an already-open one
path with create Directory for the new instance's LanceDB data
model no Embedding model for the new instance when create is used (default: the model recorded in the backup file)

Without create, name is required and the instance must already be open. With create, the new instance is registered and left open on the server (no separate open call needed); name/model fall back to whatever the backup file itself recorded if omitted. Once the job finishes, status's result is { "restored": <count>, "name": "<instance name>" }.

status

{ "command": "status", "ticket": "b6e7…" }

While the job is still running:

{ "status": "success", "payload": { "ticket": "b6e7…", "jobStatus": "running" } }

Once it finishes:

{ "status": "success", "payload": { "ticket": "b6e7…", "jobStatus": "completed", "result": { "imported": 214 } } }
{ "status": "success", "payload": { "ticket": "b6e7…", "jobStatus": "failed", "reason": "" } }

A finished job is removed from the server's memory the first time status reads it; poll once you see completed/failed, not repeatedly. Polling again with the same ticket afterward returns { "status": "failed", "reason": "Unknown ticket" }, same as an unrecognized or expired ticket.

Search commands

These require the instance to be open and return their result inline (not ticketed) since a single query is expected to be fast. See 03 · Search and Filtering for MetadataFilter/TranslationSearchFilters fields and how ranking works.

concordanceSearch

{
  "command": "concordanceSearch",
  "name": "project",
  "textFragment": "error",
  "language": "en",
  "limit": 100,
  "filters": { "minState": "translated" }
}

limit/filters are optional; omitting limit uses concordanceSearch's own default (100). The payload is the JSON form of Map<string, XMLElement>[]: one object per matching unit, mapping language to the serialized XML element:

[
  { "en": "<source>Connection error</source>", "es": "<target>Error de conexión</target>" }
]

semanticSearch

{
  "command": "semanticSearch",
  "name": "project",
  "queryText": "settings",
  "language": "en",
  "limit": 10,
  "filters": { "contextIncludes": ["ui.settings"] }
}

limit/filters are optional (default limit: 10). Payload is SearchResult[], already plain JSON.

semanticTranslationSearch

{
  "command": "semanticTranslationSearch",
  "name": "project",
  "searchStr": "Reset password",
  "srcLang": "en",
  "tgtLang": "es",
  "similarity": 55,
  "limit": 10,
  "filters": { "target": { "states": ["reviewed", "final"] } }
}

similarity is required. limit/filters are optional. Payload is Match[], serialized via Match.toJSON(): source/target come through as XML strings alongside origin, type, similarity, semantic, fuzzy, and properties.

storeXliffUnit

For editors/CAT tools that want to persist a single confirmed <unit> as the user works, without writing and importing a whole file.

{
  "command": "storeXliffUnit",
  "name": "project",
  "unit": "<unit id=\"u1\"><segment><source>Sign in</source><target>Iniciar sesión</target></segment></unit>",
  "fileId": "demo.xlf",
  "original": "demo.docx",
  "srcLang": "en",
  "tgtLang": "es"
}

This command returns { "status": "success", "payload": {} } directly on success.

batchTranslate

For editors/CAT tools that want to enrich a batch of <unit> elements with TM match candidates in one go.

{
  "command": "batchTranslate",
  "name": "project",
  "units": [
    "<unit id=\"u1\"><segment><source>Sign in</source><target>Iniciar sesión</target></segment></unit>",
    "<unit id=\"u2\"><segment><source>Reset password</source><target>Restablecer contraseña</target></segment></unit>",
    "<unit id=\"u3\"><segment state=\"final\"><source>Welcome back</source><target>Bienvenido de nuevo</target></segment></unit>"
  ],
  "srcLang": "en",
  "tgtLang": "es",
  "quality": 60,
  "limit": 5,
  "fileId": "content.xlf"
}
Field Required Description
name yes Instance to search against (must already be open)
units yes Array of <unit> XML strings to enrich
srcLang yes Source language of the units
tgtLang yes Target language to search for
quality yes Minimum hybrid match score, 0-100, written to the output's @matchQuality attribute
limit no Max candidates per segment; defaults to semanticTranslationSearch's own default (10) when omitted
fileId no File identifier for segment references; used in ref when a segment has no id

Every segment is processed except ones with state="final". For each segment, it runs semanticTranslationSearch and adds <mtc:match> entries to that unit's <mtc:matches> block, declaring the xmlns:mtc namespace on the unit if it isn't already present. The payload contains the enriched units as XML strings, in the same order as the input:

{
  "status": "success",
  "payload": {
    "units": [
      "<unit id=\"u1\">...</unit>",
      "<unit id=\"u2\">...</unit>",
      "<unit id=\"u3\">...</unit>"
    ],
    "segmentsProcessed": 2,
    "segmentsWithMatches": 2,
    "totalMatches": 6
  }
}

stop

{ "command": "stop" }

Shuts down the HTTP server.

Command reference

Command Requires open Ticketed Payload on success
open n/a no {}
close yes no {}
create no no {}
remove no no {}
list no no HybridTMInstanceMetadata[]
import yes yes { ticket }, later { imported }
match yes yes { ticket }, later match stats
backup yes yes { ticket }, later { backedUp }
restore see above yes { ticket }, later { restored, name }
batchTranslate yes no enriched units, plus segment/match counts
status no no job status/result
concordanceSearch yes no Map<string, XMLElement>[] (as JSON)
semanticSearch yes no SearchResult[]
semanticTranslationSearch yes no Match[]
storeXliffUnit yes no {}
stop no no {}

Next steps