From 8e5c4ae5511173d1c9e7f56474c5239ed84afd1f Mon Sep 17 00:00:00 2001 From: welsberr Date: Tue, 28 Apr 2026 21:57:48 -0400 Subject: [PATCH] Align translation client with GenieHive auth --- docs/GENIEHIVE_TRANSLATION.md | 2 +- examples/talkorigins-modern.site.json | 8 ++++++++ scisiteforge/translations.py | 2 +- tests/test_scisiteforge.py | 29 +++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/docs/GENIEHIVE_TRANSLATION.md b/docs/GENIEHIVE_TRANSLATION.md index f6dc022..63ec5fa 100644 --- a/docs/GENIEHIVE_TRANSLATION.md +++ b/docs/GENIEHIVE_TRANSLATION.md @@ -34,7 +34,7 @@ Recommended meaning of the fields: - `provider`: translation backend. The supported provider is currently `geniehive`. - `model`: a GenieHive role ID or directly addressable model name. -- `api_key`: the GenieHive client key. +- `api_key`: the GenieHive client key, sent as the `X-Api-Key` request header. - `timeout`: request timeout in seconds. - `system_prompt`: the translation policy for the client. diff --git a/examples/talkorigins-modern.site.json b/examples/talkorigins-modern.site.json index 47ee6d8..5db8f47 100644 --- a/examples/talkorigins-modern.site.json +++ b/examples/talkorigins-modern.site.json @@ -34,6 +34,14 @@ { "code": "hi", "name": "हिन्दी" } ] }, + "translation": { + "provider": "geniehive", + "base_url": "http://127.0.0.1:8800", + "model": "scientific_translator", + "api_key": "change-me-client-key", + "timeout": 120, + "system_prompt": "You are a careful scientific translator. Preserve meaning, structure, citations, and technical terms. Return only the translation." + }, "navigation": [ { "label": "Start Here", "href": "#start" }, { "label": "Key Resources", "href": "#resources" }, diff --git a/scisiteforge/translations.py b/scisiteforge/translations.py index 6cabe57..b6dc60a 100644 --- a/scisiteforge/translations.py +++ b/scisiteforge/translations.py @@ -60,7 +60,7 @@ class GenieHiveTranslator: data = json.dumps(payload).encode("utf-8") headers = {"Content-Type": "application/json"} if self.config.api_key: - headers["Authorization"] = f"Bearer {self.config.api_key}" + headers["X-Api-Key"] = self.config.api_key req = request.Request(url, data=data, headers=headers, method="POST") try: with request.urlopen(req, timeout=self.config.timeout) as resp: diff --git a/tests/test_scisiteforge.py b/tests/test_scisiteforge.py index 2dd4548..000ec88 100644 --- a/tests/test_scisiteforge.py +++ b/tests/test_scisiteforge.py @@ -5,6 +5,7 @@ import unittest from pathlib import Path from types import SimpleNamespace import sys +from unittest.mock import patch ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) @@ -313,6 +314,34 @@ class SciSiteForgeTests(unittest.TestCase): self.assertIn("Spanish", user_text) self.assertIn("evolución", user_text) + def test_geniehive_translator_uses_geniehive_api_key_header(self) -> None: + translator = GenieHiveTranslator( + TranslationConfig(base_url="http://geniehive.local:8800", model="translation-role", api_key="abc123") + ) + captured: dict[str, object] = {} + + class FakeResponse: + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + def read(self): + return b'{"choices":[{"message":{"content":"Hola"}}]}' + + def fake_urlopen(req, timeout): + captured["headers"] = dict(req.header_items()) + captured["timeout"] = timeout + return FakeResponse() + + with patch("scisiteforge.translations.request.urlopen", fake_urlopen): + self.assertEqual(translator.translate("Hello", "Spanish"), "Hola") + + headers = captured["headers"] + self.assertEqual(headers["X-api-key"], "abc123") + self.assertNotIn("Authorization", headers) + def test_translate_site_builds_translator_from_config(self) -> None: from tempfile import TemporaryDirectory